de_DEen_USes_ESfa_IRfr_FRhi_INid_IDjapl_PLpt_PTru_RUvizh_CNzh_TW

بر اساس مواد درسی ارائه شده، این راهنما مفاهیم اساسی، نحو و کاربردهای عملی زبان محدودیت شیء (OCL) را در زمینه مدلسازی UML پوشش می‌دهد.

Objct Constraint Language (OCL)


1. معرفی OCL

چرا به OCL نیاز داریم؟

اگرچه نمودارهای UML (مانند نمودارهای کلاس) برای بصری کردن ساختار عالی هستند، اما نمی‌توانند تمام جنبه‌های مربوط به یک مشخصات را بیان کنند.

  • زبان طبیعیمبهم است.

  • زبان‌های رسمی سنتیبی‌امبهام هستند اما برای مدل‌سازان متوسط سیستم خواندن و نوشتن آن‌ها بسیار دشوار است.

  • OCLاین شکاف را پل می‌زند: این یک زبان رسمیاست که برای بیان محدودیت‌های دقیق استفاده می‌شود، با این حال همچنان خواندن و نوشتن آسان است.

ویژگی‌های اصلی OCL

  1. زبان بیانی خالص:بیانیه‌ها اثر جانبی ندارند. ارزیابی یک بیانیه OCL مقداری را برمی‌گرداند اما نمی‌تواندوضعیت سیستم را تغییر ندهد. با این حال، می‌تواند وضعیت تغییر را توصیف کند (مثلاً در شرایط پس از)تغییر وضعیت (مثلاً در یک شرط پس از)

  2. زبان برنامه‌نویسی نیست:نمی‌توانید منطق برنامه، جریان کنترل یا فرآیندها/عملیاتی را فراخوانی کنید که وضعیت را تغییر دهند.

  3. زبان نوع‌دار:هر بیانیه دارای یک نوع است. بیانیه‌های درست ساختار باید قوانین تطبیق نوع را رعایت کنند. هر طبقه‌بندی UML یک نوع OCL است.

  4. ارزیابی لحظه‌ای:وضعیت اشیاء نمی‌تواند در طول ارزیابی یک بیانیه OCL تغییر کند.در طولارزیابی یک بیانیه OCL.


2. سینتکس و معنای اولیه

متن و خود

هر عبارت OCL در متن یک طبقه‌بندی UML خاص (کلاس، رابط و غیره) ارزیابی می‌شود.

  • متن: کلاسی را تعریف می‌کند که محدودیت به آن اعمال می‌شود.

  • خود: کلمه‌ای رزرو شده که به نمونه خاص کلاس متن در حال ارزیابی اشاره دارد.

متن شخص
-- 'خود' به یک نمونه خاص از شخص اشاره دارد

توضیحات و عملگرها

  • توضیحات:با علامت-- (مثلاً-- این یک توضیح است).

  • عملگرهای اینفیکسی:شما می‌توانید عملگرهای استاندارد ریاضی/منطقی (+-=<). به طور مفهومی،a + b با این برابر استa.+(b).

دسترسی به ویژگی‌ها

از نماد نقطه‌ای برای دسترسی به ویژگی‌ها یا عملیات استفاده کنید. پرانتزها برای عملیات ضروری هستند، حتی اگر پارامتری نداشته باشند.

متن Person
self.age             -- به ویژگی 'age' دسترسی پیدا می‌کند
self.stockPrice()    -- عملیات 'stockPrice' را فراخوانی می‌کند

3. انواع و مجموعه‌ها

انواع پیش‌فرض اصلی

OCL انواع استاندارد با عملیات خاص را شامل می‌شود:

  • منطقی: درستنادرست (عملیات: ویانهنتیجه می‌دهدیا جزئی)

  • صحیح / حقیقی: اعداد (عملیات: +-*/مطلقبیشینهکمینهتقسیممانده)

  • رشته:متن (عملیات: اندازهترکیبزیررشتهبه عدد صحیح تبدیل کن)

مجموعه‌ها

مجموعه‌ها نوع‌های مجرد هستند که برای نگهداری چند عنصر استفاده می‌شوند. با استفاده از نماد -> نمادگذاری.

  1. مجموعه: مجموعه ریاضی (بدون تکرار، بدون ترتیب). مجموعه {1, 2, 5}

  2. مجموعه مرتب: بدون تکرار، اما به ترتیب موقعیت مرتب شده‌اند. مجموعه مرتب {5, 4, 3}

  3. کیسه:اجازه‌ی تکرار را می‌دهد، بدون ترتیب.کیسه {1, 2, 2, 5}

  4. دنباله:اجازه‌ی تکرار را می‌دهد، مرتب شده.دنباله {1, 2, 5, 10}

    • نکته: این .. نماد ساختاری یک دنباله از اعداد صحیح متوالی ایجاد می‌کند: دنباله {1..5}.

عملیات کلیدی مجموعه‌ها

  • رایج: اندازه()خالیاست()خالی نیست()شامل(v) استشامل(v) نیستتعداد(v)جمع().

  • ترکیب‌کننده‌ها (کلیدی برای OCL):

    • انتخاب(عبارت): عناصری را بازگردان می‌کند که در آن‌ها عبارت درست است.

    • reject(عبارت): عناصری را بازگردان می‌کند که در آن‌ها عبارت نادرست است.

    • collect(عبارت): یک ویژگی خاص را از همه عناصر به مجموعه‌ای جدید استخراج می‌کند.

    • forAll(عبارت): مقدار درست اگر عبارت برای همه عناصر درست باشد.

    • exists(عبارت): مقدار درست اگر عبارت برای حداقل یک عنصر درست باشد.

    • iterate(عنصر; حالت = مقدارابتدایی | عبارت): پایه کلی برای همه مکررهای متفاوت.

تنوع‌های نحوی مکرر:

self.employee->select(age > 50)
self.employee->select(p | p.age > 50)
self.employee->select(p: Person | p.age > 50)

4. پیمایش مدل‌های UML

OCL به طور گسترده به عنوان زبان ناوبری برای عبور از ارتباطات استفاده می‌شود.

ناوبری استاندارد

با استفاده از نام نقش مقابل حرکت کنید. نوع بازگشت بستگی به چندگانگی دارد:

  • چندگانگی 1 یا 0..1: یک شیء واحد باز می‌گرداند.

  • چندگانگی *: بازگرداندن یک مجموعه (یا مجموعه مرتب‌شده اگر علامت‌گذاری شده باشد {مرتب‌شده}).

متن Company
self.manager.age > 40               -- manager 0..1 است، یک شیء از نوع شخص باز می‌گرداند
self.employee->notEmpty()           -- employee 0..* است، یک مجموعه باز می‌گرداند

اگر نام نقش وجود نداشته باشد، از نام کوچک کلاس در انتهای دیگر استفاده کنید (مثلاً self.bank).

کلاس‌های ارتباطی

برای ناوبری به کلاس ارتباطی از نام کوچک کلاس ارتباطی استفاده کنید.

متن Person
self.job->size() >= 1  -- 'job' کلاس ارتباطی بین شخص و شرکت است

ارتباطات بازگشتی

وقتی یک ارتباط به کلاس همان‌جا باز می‌گردد (مثلاً ازدواج بین دو شخص)، باید جهت را با استفاده از براکت‌های مربعی مشخص کنید [نام_نقش].

متنظر شخص
self.marriage[wife]->select(m | m.ended = false)->size() = 1

ارتباطات مشخص‌شده

از ویژگی‌های مشخص‌کننده در براکت‌های مربعی برای انتخاب یک شیء خاص استفاده کنید.

متنظر بانک
self.customer[12345]  -- شیء خاص شخص با accountNo 12345 را برمی‌گرداند
self.customer         -- مجموعه‌ای از همه مشتریان را برمی‌گرداند

تغییر نوع (تبدیل)

از oclAsType() برای دسترسی به ویژگی‌های یک زیرنوع یا ویژگی مخفی کلاس پایه استفاده شود.

self.oclAsType(Sub).p       -- به ویژگی 'p' تعریف‌شده در زیرنوع دسترسی پیدا می‌کند
self.oclAsType(Super).p     -- به ویژگی 'p' تعریف‌شده در کلاس پایه دسترسی پیدا می‌کند

5. نوشتن محدودیت‌ها

1. موارد ثابت (inv)

شرایطی که باید همیشه درست باشند برای همه نمونه‌های یک کلاس.

متنظر شرکت 
inv: self.noEmployees <= 50
inv SME: self.stockPrice() > 0  -- 'SME' نام اختیاری برای محدودیت است

2. شرایط پیش و پس از (prepost)

محدودیت‌های مرتبط با عملیات.

  • pre: باید درست باشد قبل از اجرای عملیات.

  • post: باید درست باشد بعد ازاجرا. استفاده کنیدنتیجهبرای مقدار بازگشتی

  • @pre: در شرایط پس از استفاده برای ارجاع به مقدار یک ویژگیقبل ازعملیات شروع شد.

متن Person::income(): Integer
پیش شرط adult: self.age >= 18
پس شرط resultOK: result < 5000

متن Company::hireEmployee(p: Person)
پس شرط: employee = employee@pre->including(p)

3. عبارات بدن، اولیه و مشتق شده

  • بدن: نتیجه یک عملیات پرس و جو را تعریف می کند (می تواند بازگشتی باشد).

  • اولیه: مقدار اولیه یک ویژگی را تعریف می کند.

  • مشتق کردن: یک ویژگی مشتق شده را تعریف می کند.

متن Person::income(): Integer
بدن: self.job.salary->sum()

متن Person::isMarried: Boolean
اولیه: false

متن Company::noEmployees: Integer
مشتق کردن: self.employee->size()

6. ویژگی های پیشرفته OCL

عبارات Let و Def

  • let: یک متغیر محلی درون یک عبارت واحد را تعریف می کند.

    let totHours: Integer = self.worksOn->collect(hours)->sum() in totHours >= 30
    
  • def: متغیرهای قابل استفاده مجدد یا عملیاتی که به یک طبقه وابسته هستند را تعریف می کند.

    def: hasTitle(t: String): Boolean = self.job->exists(title = t)
    

مقادیر نامشخص (منطق سه ارزشی)

اگر یک عبارت فرعی نامشخص باشد (مثلاً در دسترسی به یک ارتباط null)، کل عبارت نامشخص خواهد بود. با این حال، عملگرهای منطقی استثناهای کوتاه شده دارند:

  • true یا <نامشخص> = true

  • false و <نامشخص> = نادرست

  • نادرست به معنای <تعریف‌نشده> = درست

  • <تعریف‌نشده> به معنای درست = درست

ویژگی‌های کلاس و ویژگی‌های شیء پیش‌فرض

  • allInstances(): مجموعه‌ای از تمام نمونه‌های موجود یک کلاس را بازگرداند.

    Person.allInstances()->size() <= 100
    
  • ویژگی‌های شیء: oclIsTypeOf(نوع)oclIsKindOf(نوع)oclInState(حالت)oclIsNew (در شرایط پس‌از عمل استفاده می‌شود).


7. مطالعه موردی جامع: مدل شرکت

برای تثبیت این مفاهیم، اینجا مجموعه‌ای از محدودیت‌های یکپارچگی که بر مدل پایگاه داده شرکت (کارمندان، دپارتمان‌ها، پروژه‌ها، وابستگان) اعمال شده‌اند، آورده شده است.

محدودیت‌های ویژگی پایه

-- سن باید >= 18 باشد
متن: Employee inv: self.age() >= 18

-- تاریخ استخدام باید پس از تاریخ تولد باشد
متن: Employee inv: self.hireDate > self.birthDate

-- تاریخ شروع مدیر باید پس از تاریخ استخدام باشد
متن: Employee inv: self.manages->notEmpty() implies self.manages.startDate > self.hireDate

محدودیت‌های ناوبری و رابطه

-- سرپرست باید پیرتر و حقوق بیشتری نسبت به کارمند داشته باشد
متن: Employee 
inv: self.supervisor->notEmpty() implies self.age() > self.supervisor.age()
inv: self.supervisor->notEmpty() implies self.salary < self.supervisor.salary

-- سرپرست باید قبل از زیردست‌های خود استخدام شده باشد
متن: Employee 
inv: self.subordinates->notEmpty() implies self.subordinates->forall(e | e.hireDate > self.hireDate)

-- مدیر دپارتمان باید کارمند این دپارتمان باشد
متن: Department 
inv: self.worksFor->includes(self.manages.employee)

-- مکان پروژه باید یکی از مکان‌های دپارتمان باشد
متن: Project 
inv: self.controls.locations->includes(self.location)

-- کارمند فقط می‌تواند در پروژه‌هایی که توسط دپارتمان خود کنترل می‌شوند، کار کند
متن: Employee 
inv: self.worksFor.controls->includesAll(self.worksOn.project)

محدودیت‌های مجموعه و تکرارگر

-- شماره ملی باید شناسه منحصر به فردی در بین تمام کارمندان باشد
متن: Employee 
inv: Employee.allInstances->forAll(e1, e2 | e1 <> e2 implies e1.SSN <> e2.SSN)

-- نام و رابطه وابسته باید منحصر به فرد برای هر کارمند باشد
متن: Employee 
inv: self.dependents->forAll(e1, e2 | 
      e1 <> e2 implies (e1.name <> e2.name or e1.relationship <> e2.relationship))

-- ویژگی مشتق‌شده: تعداد کارمندان در یک دپارتمان
متن: Department 
inv: self.nbrEmployees = self.worksFor->size()

-- کارمند حداکثر در 4 پروژه کار می‌کند
متن: Employee 
inv: self.worksOn->size() <= 4

منطق پیچیده با استفاده از تعیین کنوانتخاب کن

-- مجموع ساعات کاری باید بین 30 تا 50 ساعت در هفته باشد
متن Employee 
محدودیت: تعیین کن totHours: Integer = self.worksOn->جمع‌آوری(hours)->جمع 
     در این صورت totHours >= 30 و totHours <= 50

-- حداکثر 2 کارمند می‌توانند کمتر از 10 ساعت در یک پروژه خاص کار کنند
متن Project 
محدودیت: self.worksOn->انتخاب(hours < 10)->اندازه <= 2

-- تنها مدیران بخش می‌توانند کمتر از 5 ساعت در یک پروژه کار کنند
متن Employee 
محدودیت: self.worksOn->انتخاب(hours < 5)->عدم خالی بودن نتیجه می‌دهد 
     self.worksFor.manages.employee = self

-- کارمندان بدون زیردست باید حداقل 10 ساعت در همه پروژه‌های خود کار کنند
متن Employee 
محدودیت: self.subordinates->خالی بودن نتیجه می‌دهد self.worksOn->برای همه(hours >= 10)

-- مدیر بخش باید حداقل 5 ساعت در تمام پروژه‌های کنترل شده توسط بخش کار کند
متن Department 
محدودیت: self.controls->برای همه(p: Project | 
      self.manages.employee.worksOn->انتخاب(hours >= 5)->دارای(p))

جلوگیری از چرخه‌ها (منطق بازگشتی)

-- یک کارمند نمی‌تواند خود را نظارت کند
متن Employee محدودیت: self.subordinates->حذف(self)

-- سلسله مراتب نظارتی نباید چرخه‌ای داشته باشد
متن Employee 
def: allSubordinates = self.subordinates->اتحاد(
          self.subordinates->جمع‌آوری(e: Employee | e.allSubordinates))
محدودیت: self.allSubordinates->حذف(self)