یکی شی مرکب دارید، تمایل دارید اشیاء تشکیل دهنده این شی را چگونه ذخیره کنید، در یک آرایه، در یک ArrayList یا در یک ساختار دیگر. شما می توانید با توجه به علاقه و سبک کاری خود هر کدام از این ساختمان داده ها را برای اینکار استفاده کنید. اما اجازه بدهید روی مسئله را کمی تغییر دهیم، یک کلاس دیگر نیاز دارد به اشیاء موجود در این اشیاء دسترسی داشته باشد، برای اینکار حتما نیاز خواهد داشت که بداند شما اشیاء را در این شی به چه صورت ذخیره کرده اید. پس برای غلبه به این مشکل شما نیاز دارید، متدهای را توسط شی مرکب ارائه دهید که کلاس های دیگر بدون اطلاع از نحوه ذخیره شدن اشیاء، به آنها دسترسی داشته باشند.
یک مسئله دیگر را در نظر بگیرید، ما چندین شی مرکب یا لیستی از داده ها داریم، بعضی از کلاس ها نیاز دارند تا به اشیاء و داده های موجود در این اشیاء یا لیست ها دسترسی داشته باشند، آیا می توانیم طراحی خود را به صورتی ارائه دهیم که یک کلاس، به روشی مشابه به لیست اشیاء یا داده ها دسترسی داشته باشد.
الگوی که می توانیم برای حل این مسئله بکار ببریم، الگوی Iterator نام دارد. این الگو یک اینترفیس تعریف می کند که امکان دسترسی نرتیبی به عناصر یک مجموعه را می دهد. هر کلاس که بخواهد به عناصر یک مجموعه دسترسی پیدا کند از طریق این اینترفیس عمل خواهد کرد که مستقل از پیاده سازی شی مرکب و کلاسی که این اینترفیس را پیاده سازی کرده است خواهد بود.
تصور کنید که یک کتاب از چند فصل تشکیل شده است و هر فصل به عنوان یک شی در سیستم در نظر گرفته شده است، می خواهیم اسامی فصول این یک کتاب را نمایش دهیم، برای اینکار ما نیاز داریم تا به لیست فصول آن کتاب دسترسی داشته باشیم، برای اینکار ما از الگوی بالا استفاده خواهیم کرد. طبق تعریف الگوی Iterator، ما در ابتدا نیاز داریم تا یک اینترفیس برای اینکار تعریف کنیم. ما به متدهای مانند بررسی اینکه عنصری بعدی در لیست وجود دارد یا نه، برگرداندن عنصر فعلی، برگرداندن عنصر بعدی، و برگشت به عنصر اول. سپس ما نیاز داریم تا این اینترفیس را پیاده سازی کنیم.

دیاگرام الگوی Iterator
|
تعریف اینترفیس IIterator |
|
Public Interface IIterator Function FirstItem() As Object Function NextItem() As Object Function IsDone() As Boolean Function CurrentItem() As Object End Interface |
|
پیاده سازی اینترفیس IIterator |
|
Public Class ConcreteIterator Implements IIterator Private List As New ArrayList Private Current As Integer = 0 Public Sub New(ByVal VarList As ArrayList) List = VarList End Sub Public Function CurrentItem() As Object Implements IIterator.CurrentItem Return List(Current) End Function Public Function FirstItem() As Object Implements IIterator.FirstItem Current = 0 If IsDone() Then Return List(Current) Else Return Nothing End If End Function Public Function IsDone() As Boolean Implements IIterator.IsDone If Current >= List.Count Then Return False Else Return True End If End Function Public Function NextItem() As Object Implements IIterator.NextItem Current += 1 If IsDone() Then Return List(Current) Else Return Nothing End If End Function End Class |
|
تعریف اینترفیس Aggregate |
|
Public Interface IAggregate Function CreateIterator() As IIterator End Interface |
|
پیاده سازی اینترفیس Aggregate و کلاس Book |
|
Public Class Book Implements IAggregate Private _Name As String Private _Chapters As New ArrayList Public Property Name() As String Get Return _Name End Get Set(ByVal value As String) _Name = value End Set End Property Public Sub New() End Sub Public Sub New(ByVal VarName As String) _Name = VarName End Sub Public Sub Add(ByVal Chapter As Chapter) _Chapters.Add(Chapter) End Sub Public Function CreateIterator() As IIterator Implements IAggregate.CreateIterator Return New ConcreteIterator(_Chapters) End Function End Class |
|
تعریف کلاس Chapter |
|
Public Class Chapter Private _Name As String Public Property Name() As String Get Return _Name End Get Set(ByVal value As String) _Name = value End Set End Property Public Sub New() End Sub Public Sub New(ByVal VarName As String) _Name = VarName End Sub End Class |
|
نحوه استفاده |
|
Dim Book As New Book("Book1") Book.Add(New Chapter("Chapter 1")) Book.Add(New Chapter("Chapter 2")) Dim Iterator As IIterator = Book.CreateIterator Dim Ins As Chapter = Iterator.FirstItem While Iterator.IsDone System.Console.WriteLine(Ins.Name) Ins = Iterator.NextItem End While |
همانطوریکه روی صندلی نشسته اید به بدن خود تان توجه کنید و آنرا مورد بررسی قرار بدهید، شاید بدن خود را به صورت ترکیبی از چندین شی مانند دست، پا، چشم و ... ببینید. سپس با دست خود یک کتاب را بردارید و شروع به ورق زدن آن بکنید، مشاهده می کنید که یک کتاب از ده ها صفحه تشکیل شده است و هر صفحه از چندین پارگراف و هر پار گراف از چندین سطر تشکیل شده است. روی آیکون My Computer کلیک می کنید، دریواهای که کامپیوترتان دارید نشان داده می شود، روی یکی از دریواها کلیک می کنید، لیست فایل ها و folder های آن دریوا نشان داده می شود، سپس روی یکی از folder ها کلیلک می کند، مشاهده می کند که خود آن folder، از چندین folder و یا فایل تشکیل شده است. در هر کدام از نمونه های بالا، همانطوریکه دیدید یک شی ممکن شامل چندین شی ساده یا مرکب دیگر نیز باشد، آن شی باید بداند که چگونه این اشیاء را نگهداری و مدیریت کند و هر چه تعداد اینگونه اشیاء مرکب در سیستم افزایش یابد، سیستم پیچیده تر خواهد شد. شما برای حل این مشکل چه پیشنهادی دارید و چه راه حلی ارائه می کنید؟
اجازه بدهید کار را با یک مثال ادامه دهیم. برای مثال فرض کنید که می خواهیم یک برنامه ایجاد کنیم که فایل سیستم ویندوز را شبیه سازی کند. در فایل سیستم ویندوز ما دو شی اصلی به نامه های Folder و فایل داریم. Folder ها می توانند از فایل ها یا Folder ها دیگر تشکیل شوند، در حالیکه فایل ها نمی توانند شامل مولفه ای دیگری از فایل سیستم باشند. برای این مثال می توان طراحی های گوناگونی را ارائه کرد. یک طراحی می تواند بدین صورت باشد که، یک اینترفیس مشترک برای هر دو شی موجود در مسئله تعریف کنیم، که شامل متدهای باشد که در هر دو شی وجود دارند. شکل زیر طراحی حاصل را نشان می دهد.
با این طراحی Clinet می تواند یک مجموعه از اشیاء FileSystemComponent را ایجاد کند. و با استفاده از متد addComponent، شی DirComponent، نمونه های مختلفی از FileSystemComponents را به DirComponent اضافه کند.
هنگامیکه Clinet بخواهد سایز هر کدام از این اشیاء را استخراج کند، به سادگی متد getComponentSize را فراخوانی کند، Client نباید از نحوه محاسبه و عملیاتی که برای اندازه گیری سایز یک مولفه صورت می گیرد آگاه باشد. در این مورد، client با هر دو شی FileComponent و DirComponent به یک صورت رفتار می کند.
Clinet در مورد متد مشترک getComponentSize، با هر دو شی FileComponent و DirComponent رفتار یکسانی را دارد. اما Clinet برای فراخوانی متدهای مانند addComponent و getComponent نیاز دارد که دو شی را از همدیگر تشخیص دهد، چونکه این متدها فقط برای DirComponent تعریف شده است. به همین دلیل client نیاز دارد تا نوع شی مورد نظر را برای فراخوانی این متدها چک کند تا مطمئن شود که با یک نمونه از DirComponent سر و کار دارد. برای بهبود این طراحی بگونه ای که نیازی به تشخیص تفاوت بین دو شی وجود نداشته باشد، طراحی را به چه صورت تغییر می دهد.

ما می خواهیم طراحی را بگونه ای تغییر دهیم که clinet نیازی به تشخیص شی مرکب DirComponent از FileComponent نداشته باشد، و با هر دو شی به یک صورت رفتار کند.
طراحی را می توانیم به این صورت تغییر دهیم که، متدهای addComponent و getComponent را به اینترفیس مشترک FileSystemComponent انتقال دهیم، و یک پیاده سازی پیش فرض برای این متدها انجام دهیم و اینترفیس مشترک FileSystemComponent را به یک کلاس abstract تغییر دهیم. پیاده سازی پیش فرض برای این متدها بخاطر FileComponent هست و کار خاصی را انجام نمی دهد. اما کلاس DirComponent این متدها را بازنویسی مجدد می کند تا پیاده سازی خاص خود را انجام دهد.

با این کار مشکل قبلی ما حل می شود، چون کلاس پدر FileSystemComponent، دارای یک پیاده سازی پیش فرض برای متدهای addComponent و getComponent هست، و دیگر clinet نیاز به کنترل نوع شی برای فراخوانی این دو متد ندارد.
تصور کنید که می خواهیم یک متد جدید با نام removeComponent را به کلاس DirComponent اضافه کنیم، در اینصورت این متد را به پدر FileComponent نیز اضافه می کنیم و یک پیاده سازی پیش فرض برای این متد در نظر می گیریم.
با طراحی بالا ما توانستیم مشکل خود را حل کنیم، اما روشی که برای غلبه بر مسئله استفاده کردیم، الگوی Composite نام دارد. ما در مواقعی از این الگو استفاده می کتیم که یک شی پیچیده داریم و می خواهیم آنرا به یک سلسله مراتب از اشیاء کل و جز (part-whole hierarchy) تجزیه کنیم. یا همانند مثال بالا، client قادر باشد تفاوت بین اشیاء مرکب (DirComponent) و اشیاء منفرد را نادیده بگیرید. و client با همه اشیاء موجود در ساختار مرکب بصورت یکسان رفتار کند.
کلاس دیاگرام این الگو به صورت زیر است:

در دیاگرام بالا کلاس Leaf، کلاس های هستند که دارای فرزند نیستند مانند کلاس FileComponent در مثال.
کلاس Composite، کلاسی مانند DirComponent در مثال می باشد. در این کلاس رفتارهای مربوط به مولفه های که دارای فرزند هستند تعریف می گردد.
کلاس FileSystemComponent همان کلاسFileSystemComponent با ویژگیهای که در بالا اشاره شد هست.
نمی دانم فارغ التحصیل شدید یا نه، اما همه ما در دوران دانشگاه همکلاسی ها و دوستان صمیمی زیادی داریم که، همیشه می خواهیم خبری از آنها داشته باشیم، اما بعدی از فارغ التحصیلی هزار و یک مشکل به سراغمان می آید. و اگر بخواهیم با تک و تک آنها ارتباط داشته باشیم برایمان خیلی سخت خواهد بود همینطور برای آنها. اما تقریبا همیشه فردی هست که، همه می خواهند با او ارتباط داشته باشند، پس مشکل حل شد ، پس فقط کافی است که ما نیز با این فرد تماس بگیرم تا خبری از همه بچه ها بدست آوریم.

در دنیای نرم افزار های شی گرا نیز ، اشیاء برای انجام دادن یک وظیفه با همدیگر ارتباط دارند. اما همانند مثال بالا، هر چه تعداد ارتباطات افزایش یابد، برنامه پیچیده تر خواهد شد و منجر خواهد شد که نگهداری برنامه نیز سخت تر شود و قابلیت استفاده مجدد نیز کاهش یابد.
برای مثال فرم زیر را در نظر بگیرید، این فرم برای رزور یک اتاق مهمانی در یک هتل استفاده می شود، طرز عملکرد فرم در زیر توضیح داده شده است.

هنگامیکه پنجره برای اولین بار نمایش داده می شود، فقط فیلد مربوط به تعداد مهمان ها و دکمه برگشت فعال است. کنترل های تاریخ، ساعت شروع و ساعت اتمام زمانی فعال می شوند که کاربر تعداد مهمان ها را وارد کند. اما فقط زمانهای را می تواند وارد کند که در آن موقع اتاقی با آن سایز موجود باشد. سپس گزینه سرویس ها فعال می شود. اگر تعداد مهمان ها پاک شود، باید تمام گزینه ها پاک و غیر فعال شوند.
ساعت شروع باید کوچکتر از ساعت اتمام باشد.
هنگامیکه کاربر تعداد مهمانها و ساعت و تاریخ را وارد می کند و یک نوع سرویس را انتخاب کرد، لیست غذا ها فعال می شود. با توجه به تاریخ، ساعت و نوع سرویس، لیست غذاهای ارائه شده متفاوت خواهد بود.
هنگامیکه حداقل یک نوع غذا انتخاب شد، و داده ها دیگر معتبر بودند، دکمه رزور فعال می شود.

شکل بالا، ارتباطات و روابط موجود بین اشیاء روی فرم را نشان می دهد. همانطوریکه داده می شود، هر شی حداقل دارای 2 وابستگی است. همانطوریکه در بالا نیز گفته شده، وابستگی باعث پیچیده شدن منطق برنامه خواهد شد و این نگهداری سیستم را مشکل تر خواهد کرد و قابلیت استقاده مجدد را نیز کاهش خواهد داد. برای مثال تغییر در یکی از کلاس ها ممکن است، به تغییر در سایر کلاس ها منجر شود. شما برای حل این مشکل و کاهش تعداد وابستگی ها چه پیشنهادی دارید؟
برای حل مشکل می توانیم، مانند مثال اول عمل کنیم، یعنی دوستی را ایجاد کنیم که همه به جای کسانی که می خواهند با آنها ارتباط برقرار کنند، فقط با او ارتباط برقرار کند.یعنی در مثال دوم، ما یک کلاس جدید ایجاد کنیم که، به جای اینکه کلاس ها با یکدیگر بطور مستقیم ارتباط برقرار کنند، با کلاس جدید ارتباط بر قرار کنند و کلاس جدید مسئولیت ارتباط با کلاس دیگر را بعهده بگیرید. روشی که برای حل مسئله مطرح شد، الگوی Mediator نام دارد. در واقع این کلاس مسئولیت، مدیریت وابستگی ها را بعهده می گیرد. و با این کار تمام اشیاء، فقط یک وابستگی دارند آن هم با کلاس Mediator. شکل زیر نحوه ارتباطات مثال 2 را، بعد از اعمال شی Mediator را نشان می دهد.

حالا زمانیکه یکی شی می خواهد با شی دیگر ارتباط برقرار کند، پیام خود را به کلاس Mediator، ارسال می کند. و کلاس Mediator همانند یک مسیریاب آن را به شی مورد نظر ارسال می کند. با استفاده از این الگو می توانیم به مزایا زیر دست یابیم:
- سادگی تغییر در برنامه : با استفاده از این الگو وابستگی میان کلاس های مختلف کاهش می یابد و در اکثر موارد فقط با تغییر در کلاس Mediator و یا ایجاد زیر کلاس های از این کلاس، می توانیم تغییرات مورد نظر را اعمال کنیم بدون اینکه تغییری در کلاس های دیگر بدهیم.
- افزایش قابلیت استفاده مجدد: با کاهش وابستگی میان کلاس، قابلیت استفاده مجدد کلاس ها افزایش می یابد.
ولی باید توجه داشت که کلاس Mediator باعث کاهش پیچیدگی وابستگی بین کلاس ها می شود، اما این پیچیدگی به درون ساختار Mediator منتقل می شود و ممکن است باعث شود که تغییر آن مشکل شود.

نمودار UML الگو ی Mediator
در نمودار بالا شی Mediator، یک اینترفیس برای ارتباط با اشیاء Colleague تعریف می کند که این اینترفیس توسط کلاس ConcreteMediator پیاده سازی می شود. و Colleague کلاس ها، کلاس های هستند که باهم ارتباط داشتند و ما این ارتباط و وابستگی را از آنها جدا کردم و در کلاس Mediator قرار دادیم.
وارد فروشگاه می شوید، غرفه ها را یکی یکی نگاه می کنید، بدنبال یک هدیه مناسب برای یک شخص خاص هستید. با زحمت و وسواس زیاد آنرا انتخاب می کنید و به فروشنده تحویل می دهید و می گوید آنرا برایتان کادو و تزئین کند. فروشنده جعبه های تزیئنی مختلف را برای قرار دادن هدیه برای شما نشان می دهد تا یکی از آنها را انتخاب کنید. شما جعبه مورد نظر را انتخاب می کنید. و پیشنهاد می کنید در صورت امکان بعد از قرار دادن هدیه شما و پیچیدن آن در کاغذ کادو، یک شاخه کل روی آن بچسباند. کار تمام می شود. فروشنده می گوید قیمت هدیه ای که انتخاب کرده اید، X تومان است، قیمت جعبه Y تومان است و فیمت شاخه گل Z تومان است. شما باید سر جمع X+Y+Z تومان را پرداخت کنید.
حالا فرض کنید شما خرید، خود را تمام کردید و هدیه را به شخص مورد نظر خود تقدیم کردید. اما از طرف مسئولین همین فروشگاه، شرکت شما برای طراحی سیستم سفارش فروشگاه در نظر گرفته شده است. این فرشگاه دارای کالاهای متعدد کادویی هست و همچنین دارای انواع زیادی از جعبه های تزئینی و وسایل برای تزئین کالاهای مختلف است. سیستم باید قابلیت این را داشته باشدکه قیمت کل را برای یک کالا با سایر وسایل تزئینی محاسبه کند. طراحی شما برای این مسئله به چه صورت می تواند باشد؟
یک طراحی برای این مسئله می تواند به صورت زیر باشد: یعنی به این صورت عمل شود که، اگر مشتری، یک هدیه را بدون هیچ کالای تزئینی دیگر خرید، یک نمونه از خود آن کلاس ایجاد شود و قیمت آن، توسط متد قیمت برگشت داده شود. اگر یک هدیه نوع 1با جعبه 1 خریداری شود، نمونه ای از هدیه نوع 1 با جعبه نوع 1 ایجاد شود، و قیمت کل خرید برگشت داده شود.

اما در این حالت، اگر فروشگاه دارای دهها هدیه و جعبه مختلف باشد، و شما هر کدام از ترکیب های ممکن را بصورت یک کلاس جداگانه تعریف کنید، برای یک کار ساده شاید صدها کلاس داشته باشید که، بی شک مدیریت و تغییر در هر کدام از کلاس ها، هزینه زیادی را در بر خواهد داشت.
در حال طراحی اینترفیس (UI) برنامه هستید، ترجیح می دهید که اطراف بعضی از دکمه ها (button) یک نوار رنگی نازک باشد. اما دکمه های که شما استفاده کرده اید، این قابلیت را ندارد. می خواهید خودتان این قابلیت را به دکمه ها اضافه کنید. شما این کار را به چه روشی انجام می دهید؟
طراحی های مختلفی را می توان برای هر دو مسئله بالا ارائه داد. اما یک طراحی و راه حل خوب الگوی Decorator است.
در هر دو مثال بالا، ما می خواهیم یک رفتار جدید را به یکی شی اضافه کنیم. ولی می خواهیم بدون استفاده از وراثت این رفتار را به شی اضافه کنیم. این الگو اجازه می دهد، تا یک رفتار را بدون استفاده از وراثت و بصورت دینامیک به یک شی اضافه کنیم. نمودار کلاس این الگو بصورت زیر است:

شیی که می خواهیم رفتار جدیدی را به آن اضافه کنیم، همان ConcreteComponent، در نمودار بالا می باشد (شی دکمه در مثال دوم). و شیی که رفتار جدید را به شی ConcreteComponent، اضافه می کند. یکی از ConcreteDecorator ها، خواهد بود (نوار نازک در مثال دوم ). اما این رفتار جدید، چگونه اضافه می شود؟ دقیقا به همان صورتیکه ما هدیه را در داخل جعبه قرار دادم. اینجا نیز یک نمونه از کلاس مورد نظر را در داخل کلاس دیگر قرار می دهیم و اجازه می دهیم کلاس در برگیرنده بر روی آن کار کند. در پایین با مثال اول به بررسی کامل عملکرد این الگو خواهیم پرداخت.
در مثال اول، شما یک هدیه، یک جعبه و یک گل می خرید. فرضی کنید می خواهیم، مقدار کل را با الگوی Decorator محاسبه کنیم:
در ابتدا یک شیی از کلاس هدیه 1 ایجاد می کنیم. این کلاس یک متد به نام قیمت برای محاسبه قیمت خود دارد.
|
هدیه 1 قیمت () |
سپس مشتری یک جعبه را انتخاب کرده بود، و فروشنده هدیه را در داخل آن قرار داده بود. پس ما نیز همین کار را می کنیم، یعنی فرضا نمونه ای از جعبه 1 را ایجاد می کنیم و شی هدیه ایجاد شده را در داخل آن قرار می دهیم. و هدیه را با جعبه تزئین می کنیم.
|
جعبه 1 قیمت()
|
در آخر مشتری خواسته بود، یک گل بر روی هدیه نصب شود. ما نیز همین کار را می کنیم، یک نمونه از گل ایجاد می کنیم. و هدیه را با آن تزئین می کنیم.
|
گل قیمت()
|
حل مشتری مبلغ پرداختی را از فروشنده می پرسد. پس ما باید در این نقطه قیمت را محاسبه کنیم. در ابتدا ما متد قیمت را از بیرونی ترین، تزئین کننده فراخوانی می کنیم. یعنی در ابتدا متد قیمت را برای شی گل فراخوانی می کنیم. این شی قیمت شی بعدی یعنی جعبه را فراخوانی می کند. و جعبه متد قیمت، هدیه اصلی را فراخوانی می کند. در این نقطه چون هدیه یک شی تزئین کننده نیست (بلکه یک شی ConcreteComponent است). قیمت خود را برگشت می دهد یعنی 30000. سپس جعبه قیمت خود را به قیمت برگشت داده شده توسط هدیه، اضافه می کند. و مجموع را برگشت می دهدیعنی 31000. در آخر نیز گل مقدار خود را به مقدار برگشت داده شده اضافه می کند و مجموع کل را برگشت می دهد یعنی 33000. در پایین نحوه پیاده سازی الگو با همین آورده شده است.
|
کلاس Gift (Component) |
|
Public MustInherit Class Gift Public MustOverride Function Cost() As Double End Class |
|
کلاس Decorator |
|
Public MustInherit Class Decorator Inherits Gift End Class |
|
کلاس Gift1 (ConcreteComponent) |
|
Public Class Gift1 Inherits Gift Public Overrides Function Cost() As Double Return 30000 End Function End Class |
|
کلاس Gift2 (ConcreteComponent) |
|
Public Class Gift2 Inherits Gift Public Overrides Function Cost() As Double Return 10000 End Function End Class |
|
کلاس Box1 (ConcreteDecorator ) |
|
Public Class Box1 Inherits Decorator Private Gift As Gift Public Sub New(ByVal Giftvar As Gift) Gift = Giftvar End Sub Public Overrides Function Cost() As Double Return 1000 + Gift.Cost End Function End Class |
|
کلاس Box2 (ConcreteDecorator ) |
|
Public Class Box2 Inherits Decorator Private Gift As Gift Public Sub New(ByVal Giftvar As Gift) Gift = Giftvar End Sub Public Overrides Function Cost() As Double Return 1500 + Gift.Cost() End Function End Class |
|
کلاس Flower (ConcreteDecorator ) |
|
Public Class Flower Inherits Decorator Private Gift As Gift Public Sub New(ByVal Giftvar As Gift) Gift = Giftvar End Sub Public Overrides Function Cost() As Double Return 2000 + Gift.Cost End Function End Class |
|
ماژول Main |
|
Sub Main() Dim Gift1 As Gift = New Gift1() Gift1 = New Box1(Gift1) Gift1 = New Flower(Gift1) System.Console.WriteLine("Price = " & Gift1.Cost) Dim Gift2 As Gift = New Gift2() Gift2 = New Box1(Gift1) Gift1 = New Flower(Gift1) System.Console.WriteLine("Price = " & Gift1.Cost) Console.ReadLine() End Sub |
تصور کنید، شما مسئول طراحی یک برنامه هستید که قرار است برای هر کارمند یک سازمان، کارت ویزیتی مانند شکل زیر چاپ کند. سازمان مورد نظر، یک سازمان بسیار بزرگ با چند هزار کارمند می باشد.
|
نام و نام خانوادگی سمت نام شرکت آدرس |
اواین طراحی که به ذهن می تواند خطور کند، یک طراحی بسیار ساده است. یک کلاس به نام کارت ویزیت تعریف می کنیم و صفاتی را که در کارت قرار دارند، به کلاس منتقل می کنیم و یک متد چاپ هم برای چاپ کارت تعریف می کنیم.
|
visitCard |
|
Name Title Company Address |
|
print |
در این صورت شما باید به ازاء هر کارمند یک نمونه از کلاس visitCard ایجاد کند، یعنی شما باید چند هزار شی visitCard ایجاد کند. و این می تواند منابع زیادی از سیستم را مصرف کند. ولی شما با توجه به ساختار کارت متوجه می شود که، نام شرکت و آدرس برای تمام افراد یکی است. با این آگاهی، آیا می خواهید تغییری در طراحی خود انجام بدهید یا نه؟
یا شما مسئول طراحی یک برنامه پردازش متن هستید. شکل زیر، می تواند یک نمودار کلاس ساده شده برای این برنامه باشد.
هر نمونه از کلاس Docchar نماینگر یک کاراگتر در سند است. برای تغییر فونت یک کاراگتر می توانیم متد SetFont از شی Docchar فراخوانی کنیم. اگر فونت یک کاراگتر مشخص نشده باشد، از فونت container خود استفاده خواهد کرد.
یک سند نمونه از این برنامه را با طراحی بالا در نظر بگیرید، یک سند که شامل چندین صفحه است، هر صفحه شامل چندین شی پارگراف خواهد بود، و هر پارگراف شامل چندین شی LineOfTextad خواهد بود و آن نیز شامل صد ها شی DocChar خواهد بود، یعنی در یک سند چند صفحه ای ممکن است هزار ها شی DocChar داشته باشیم که، حافظه زیادی را از سیستم مصرف خواهد کرد. شما برای کاهش مصرف حافظه، طراحی خود را به چه صورت تغییر می دهید؟
یک راه حل کاندید و خوب برای حل هر دو مسئله بالا، الگوی Flyweight است. که به بررسی آن خواهیم پرداخت. مشکل ما در هر دو مسئله بالا، تولید نمونه های بیش از حد از یک کلاس بود که منجر به مصرف زیاد منابع می شد. یک شی شامل 2 نوع اطلاعات است:
1- اطلاعات داخلی (Intrinsic Information) : این نوع اطلاعات، اطلاعاتی هستند که برای تمام نمونه های یک کلاس ثابت است. برای مثال، نام شرکت و آدرس در مثال اول برای تمام کارمندان ثابت است.
2- اطلاعات خارجی (Extrinsic Information) : این نوع اطلاعات، اطلاعاتی هستند که از نمونه ای به نمونه ای دیگر فرق می کند، یعنی اشیاء را از همدیگر متمایز می سازد. برای مثال، نام و سمت کارمند در مثال اول.
پیشنهاد الگوی Flyweight این است که، اطلاعات خارجی را، به خارج از کلاس منتقل کنیم و این اطلاعات را به صورت پارامتر های ورودی متدها دریافت کنیم، و تعداد نمونه ها را با به اشتراک گذاری آنها کاهش دهیم. نمودار کلاس این الگو بصورت زیر است.

در نمودار بالا هر کدام از،ConcreteFlyweight ها نمونه های به اشتراک گذاری یک کلاس هستند. چون اشیاء به اشتراک گذاشته شده اند، Client ها نباید بطور مستقیم نمونه ای از آنرا ایجاد کنند. برای حل این مشکل، کلاس FlyweightFactory را ایجاد می کنیم. این کلاس باید بداند که آیا نمونه ای از یک کلاس ایجاد شده است یا نه. برای این کار، لیستی از اشیاء ایجاد شده را نگهداری می کنیم که این اشیاء از کلاس های مختلف توسط یک صفت کلید از هم متمایز می شوند. در مثال 2 ما برای حل مشکل خود برای هر کاراکتر، یک کلاس،تعریف می کنیم. کلید هر کدام از کلاس ها همان، کاراکتری است که نمایش خواهند داد. برای نمونه کلاس زیر را در نظر بگیرید، کلید هر کلاس را در یک Hashtable ذخیره می کنیم، و وقتی که Client بخواهد نمونه ای از کلاس مورد نظرش را بسازد، این کلاس بررسی می کند که آیا این کلید در Hashtable وجود دارد یا نه. اگر وجو نداشته باشد، نمونه ای از کلاس مورد نظر ایجاد می کند، در غیر اینصورت مرجعی (reference) به آنرا برگشت می دهد.
|
class CharacterFactory { private Hashtable characters = new Hashtable(); public Character GetCharacter(char key) { Character character = characters[key] as Character; if (character == null) { switch (key) { case 'A': character = new CharacterA(); break; case 'B': character = new CharacterB(); break; //... case 'Z': character = new CharacterZ(); break; } characters.Add(key, character); } return character; } } |
نمی دانم تا بحال پشت کامیوتر خودتان نشستید تا یک مرحله از یک بازی کامپیوتری را به پایان برساند یا نه؟ اما کسانی که اینکار را انجام دادند تصور کنند یک بازی استراتژیک مانند کمانندو ها را بازی می کنند اما بازی بدون قابلیت ذخیره کردن ارائه شده است. در این حالت حتما اکثر افراد این بازی را خیلی زود کنار خواهند گذاشت چون واقعا کار کاربر خیلی سخت خواهد شد. پس اگر روزی قرار باشد یک بازی کامپیوتری را طراحی و پیاده سازی کنیم برای برآورده کردن نیاز های کاربر حتما باید بازی را با قابلیت ذخیره کردن طراحی و پیاده سازی کنیم.
اجازه بدهید یکبار دیگر مسئله را تعریف کنیم: فرضی کنید شما در حال طراحی یک بازی هستید. این بازی باید با قابلیتی ارائه شود که کاربر در هر قسمتی از بازی خواست بتوانید بازی را در آن نقطه ذخیره کنید و سپس هر زمانی که خواست بتواند بازی را از همان نقطه ای که ذخیره کرده است ادامه دهید. راه حل و طراحی شما برای این مسئله چست و چگونه می تواند باشد؟
یا یک مسئله دیگر: تصور کنید شما در حال طراحی یک فروشگاه الکترونیکی برای یک شرکت هستید. و یکی از ویژگیهای در خواست شده از طرف ذی النفعان سیستم این است که، هنگامیکه یک کاربر وارد فروشگاه می شود و اجناس مورد نیاز خود را به سبد خریدش اضافه می کند، اما قبل از اینکه سفارش خود را تائید کند از فروشگاه خارج می شود. سیستم باید قابلیت این را داشت باشد که هنگام ورود بعدی همان کاربر، آیتم های موجود در سبد خرید قبلی را بازیابی کند و در سبد خرید مشتری قراد دهد.
در هر دو مسئله بالا، کار ما ذخیره حالت موجود اشیاء و سپس بازیابی آن حالت در صورت نیاز هست. یک راه حل موجود و خوب برای مسائل بالا و شبیه آنها، الگوی Memento است. که نحوه عملکرد آنرا، برسی خواهیم کرد.
نمودار UML، این الگو بصورت زیر است:

همانطوریکه در بالا اشاره شد، ما نیاز داریم تا حالت یک شی را ذخیره و بازیابی کنیم، این شی با نام originator در این الگو مشخص می شود. اما نحوه ذخیره کردن وضعیت شی موجود به این صورت است که زمانیکه برنامه Client درخواست ذ خیره کردن را از شی originator می کند. این شی (originator)، تمام صفاتی را که برای بازیابی حالتش نیاز است را، در یک شی دیگر به نام Memento قرار می دهد و آن را به Client ازسال میکند (در نمودار مقدار صفت state از شی originator، در مقدار صفت stateشی Memento قرار می گیرد.). ما نیاز داریم تا اشیاء از نوع Memento را، نگهداری و مدیریت کنیم. برای اینکار از کلاسی به نام caretaker، استفاده می کنیم. زمانیکه یک شی Mementoایجاد می شود، آن شی به مجموعه اشیاء Caretaker اضافه می شود. وقتی که یک عمل undo انجام می شود شی Caretaker با یک شی دیگر (client)، همکاری می کند تا یک شی Memento انتخاب شود. بعد از انتخاب شی Memento ، آن شی متد setMemento شی Originator را فراخوانی می کند تا حالت انتخاب شده را بازیابی کند.
فرضی کنید شما در حال طراحی یک برنامه ویرایشگر متن هستید، و یکی از نیازهای مطرح شده توسط کاربران این مورد است که وقتی آنها قسمتی از یک متن را کپی می کنند، آن قسمت به یک لیست اضافه شود و نمایش داده شود. سپس زمانیکه کاربر به یکی از متن های کپی شده نیاز داشت، یکی از آنها از لیست انتخاب کند تا متن انتخاب شده به متن اصلی اضافه شود(عمل paste). دقیقا چیز شبیه clipboard برنامه Microsoft Office Word.
می خواهیم این خواسته را توسط الگوی Memento، طراحی کنیم. در این مثال شی که ما نیاز داریم، حالتش را ذخیره کنیم و سپس در صورت نیاز بازیابی کنیم، شی Clipboard است.
|
کلاس originator که کد مربوط به Clipboard را پیاده سازی می کند. صفت _Clipboard ، بیانگر حالت این شی است و برای بازیابی آن باید این صفت را ذخیره کنیم. |
|
Public Class originator Private _Clipboard As String Public Property Clipboard() As String Get Return _Clipboard End Get Set(ByVal value As String) _Clipboard = value End Set End Property Public Function createMemento() As memento Return New memento(_Clipboard) End Function Public Sub setMemento(ByVal _memento As memento) _Clipboard = _memento.Clipboard End Sub End Class |
|
کلاس memento که برای ذخیره اطلاعات مورد نیاز برای یازیابی حالت یک نمونه از شی originator به کار می رود. |
|
Public Class memento Private _Clipboard As String Public Sub New(ByVal Data As String) _Clipboard = Data End Sub Public Property Clipboard() As String Get Return _Clipboard End Get Set(ByVal value As String) _Clipboard = value End Set End Property End Class |
|
کلاس caretaker برای نگهداری نمونه های مختلفی از کلاس memento به کار می رود. در ا ینجا برای نگهداری این نمونه ها از ArrayList استفاد شده است. تو سط متد list، مجموعه اشیایی memento برای نمایش به کاربر برگشت داده می شود. تا کاربر یکی از آنها را در صورت نیاز انتخاب کند. |
|
Public Class caretaker Private memento As New ArrayList Public Sub Add(ByVal _memento As memento) memento.Add(_memento) End Sub Public Function List() As ArrayList Return memento End Function End Class |
|
برای تست کلاس های بالا از یک فرم به نام FrmTest استفاده شده است. که یک کنترل ListBox بر روی آن قرار دارد تا مقادیر موجود در هر کدام از نمونه های شی memento را نمایش دهد. تا کاربر بتواند حالت مورد نیاز خود را از میان آنها انتخاب کند. |
|
Public Class FrmTest Dim Ins As New originator Dim List As New caretaker Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Ins.Clipboard = "Ali" List.Add(Ins.createMemento()) Ins.Clipboard = "asd" List.Add(Ins.createMemento()) Ins.Clipboard = "sdf" List.Add(Ins.createMemento()) Me.ListBox1.DataSource = List.List Me.ListBox1.DisplayMember = "Clipboard" End Sub Private Sub ListBox1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.Click Ins.setMemento(List.List(Me.ListBox1.SelectedIndex)) End Sub End Class |
توجه: در پیاده سازی مثال بالا، بعضی از قوانین شی گرایی نادیده گرفته شده است.(تصحیح به عهده دوستان)
شاید شما هم زمانیکه تلویزیون را باز می کنید، یا وارد اینترنت می شود، یکراست به سراغ شبکه ها یا سایت های خبری می رود، تا از اتفاقات و رویدادهای مورد علاقه خود آگاه شود. حتی بعضی وقت ها این خبرها و رویدادها باعث می شوند شما روند کاری خود را تغییر دهید. حتی بعضی از خبرها در موضوعات خاص برای شما آنقدر اهمیت دارد که، عضو یک سایت خبری می شود تا هر زمانیکه خبر جدیدی وجود داشته باشد، به ایمیل شما نیز ارسال شود. بنابراین هر زمانیکه یک خبر جدید به سایت اضافه می شود، آن خبر به ایمیل شما نیز ارسال می شود. تا زمانیکه شما از عضویت در آن سایت انصراف ندادید، همه خبرهای جدید به شما ارسال می شود، و زمانیکه شما از عضویت خود انصراف دادید، خبرهای جدید به شما ارسال نخواهد شد ولی برای اعضاء دیگر سایت ارسال خواهد شد. اگر شما مسئول طراحی این سیستم بودید، چگونه عمل می کردید؟
یک مرکز کاریابی را در نظر بگیرید، افراد مختلفی با تخصص های مختلف، به این مرکز مراجعه می کنند، و با توجه به تخصص هایشان برای درخواست کار ثبت نام می کنند. هر زمانیکه درخواست نیرو توسط یک شرکت در یک تخصص خاص از آن مرکز شد. آن مرکز افرادی را که در آن تخصص ثبت نام کرده اند، از آن موقعیت کاری مطلع می کنند. اگر قرار باشد شما یک سیستم نرم افزاری برای این کار طراحی کنید، طراحی شما به چه صورت خواهد بود؟
هر کدام از ما می توانیم نسبت به درک خود از مسئله و سلیقه کاری، طراح های مختلفی برای این مسئله ارائه دهیم. اما یک طراحی که می تواند یک جواب خوب و عالی باشد، الگوی Observer است که توانسته است بارها و بارها به این مسئله پاسخ بدهد.
در هر دو مسئله بالا، ما یک شی داریم که مجموعه ای از اشیاء به آن وابسته هستند. هر زمانی که وضعیت شی مورد نظر تغییر می کند، اشیاء دیگر از آن تغییر آگاه می شوند. در این الگو، مجموعه اشیاء وابسته را Observer های می نامیم و شی را که دیگر اشیاء به آن وابسته هستند را Subject می نامیم. برای نمونه در مثال اول، افرادی که در سایت عضو می شوند یک شی Observer هستند. و شی خبر، همان Subject هست.
الگوی Observer یک وابستگی، یک به چند بین اشیاء هست، هنگامیکه شی طرف یک، وضعیتش تغییر کند، اشیاء وابسته مطلع می شوند و وضعیت خودشان را با توجه به آن وضعیت بروز رسانی می کنند.
مکانیزم عملکرد این الگو بصورت زیر است:
1- Subject باید یک اینترفیس برای ثبت (registering) و انصراف از عضویت (unregistering) و اطلاع از تغییرات را آماده کند.
2- Subject باید اطلاعات حالتی را که، observer ها برای آن ریجستر شده اند را به observer ها ارسال کند.
3- Observer باید یک اینترفیس برای دریافت پیام از Subject آماده کند.
نمودار کلاس این الگو بصورت زیر است:

با یک مثال دیگر کار را ادامه می دهیم. فرض کنید که در اداره شما ، در داده های پایگاه داده مرکزی تغییرات غیر مجازی، داده شده است. برای حل این مشکل مدیران پیشنهاد کرده اند که هر تغییری که در پایگاه داده می شود، باید توسط یک پیام، به افراد مشخص شده ای اعلام شود. شما برای حل این مشکل، الگوی Observer را در نظر می گیرید. دقیقا بر طبق نمودار عمل می کنیم و با یک حالت فرض این مثال را پیاده سازی می کنیم.
|
اینترفیس مربوط به subject |
|
Public Interface subject Sub Attach(ByVal Ins As Observer) Sub Detach(ByVal Ins As Observer) Sub Notify() End Interface |
|
اینترفیس مربوط به Observer |
|
Public Interface Observer Sub Update(ByVal operation As String, ByVal record As String) End Interface |
|
کلاس database که اینترفیس مربوط به subject را پیاده سازی می کند. |
|
Public Class dataBase Implements subject Private observers As ArrayList Private operation As String Private record As String Public Sub New() observers = New ArrayList End Sub Public Sub Attach(ByVal Ins As Observer) Implements subject.Attach observers.Add(Ins) End Sub Public Sub Detach(ByVal Ins As Observer) Implements subject.Detach observers.Remove(observers) End Sub Private Sub Notify() Implements subject.Notify For I As Integer = 0 To observers.Count – 1 Dim Ins As Observer = observers(i) Ins.Update(operation, record) Next End Sub Public Sub editDatabase(ByVal ope As String, ByVal rec As String) Me.operation = ope Me.record = rec Notify() End Sub End Class |
|
کلاس Archiver که اینترفیس Observer را پیاده سازی می کند. |
|
Public Class Archiver Implements Observer Public Sub Update(ByVal operation As String, ByVal record As String) Implements Observer.Update MessageBox.Show("The archiver says a " + operation + " operation was performed on " + record) End Sub End Class |
|
کلاس boss که اینترفیس Observer را پیاده سازی می کند. |
|
Public Class Boss Implements Observer Public Sub Update(ByVal operation As String, ByVal record As String) Implements Observer.Update MessageBox.Show("The boss says a " + operation + " operation was performed on " + record) End Sub End Class |
|
کلاس client که اینترفیس Observer را پیاده سازی می کند. |
|
Public Class client Implements Observer Public Sub Update(ByVal operation As String, ByVal record As String) Implements Observer.Update MessageBox.Show("The client says a " + operation + " operation was performed on " + record) End Sub End Class |
|
تست |
|
Dim DB As New dataBase Dim Ar As New Archiver Dim Bo As New Boss Dim Cl As New client DB.Attach(Ar) DB.Attach(Bo) DB.Attach(Cl) DB.editDatabase("Delete", "Record 1") |
در مثال بالا، ما سه نوع Observer داریم. که هر وقت تغییری در پایگاه داده اعمال می شود، با یک پیام به آنها اعلام می شود.
یکی دیگر از نکات مثبت دیگر در الگوهای طراحی این است که آنها ماحصل تجربهاند و نه مباحث آکادمیک. به عبارت دیگر برای خلق یک الگو، لازم نیست شما در آزمایشگاههای مهندسی نرمافزار به دنبال کشف نمونهای جدیدی از آن باشید، بلکه باید در حین تجربه و انجام کار، حواستان به گذشتهتان باشد یا به آینده؛ و طرح این سئوال که آیا من درگذشته این مسئله را حل کردهام؟ آیا مسئلهای که با آن روبرو هستم دیگر بار ممکن است تکرار شود؟( الگوهای طراحی، محاسن و معایب (Design Patterns Pros & Cons) )
جملات بالا، جملاتی بسیار زیبایی هستند که توسط دوست و استاد خوبمان، آقای مهرداد بی بالان نوشته شده است. و منم نیز به جملات بالا اعتقاد دارم. ولی من می خواهم موضوع را کمی بسط دهم و نظر خودم را درباره استفاده از الگوها، نه تنها الگوهای طراحی بیان کنم و دوست دارم همه دوستان نظر خودشان را در مورد این موضوع بیان کنند.
ما در دنیایی از الگوهای زندگی می کنیم. از همان روزهای اول زندگی، الگوها را فرا می گیریم و بکار می بریم. ما از همان دوران کودکی، چیز هایی را که در اطرفم تکرار می شنوند می بینیم و آنها را تکرار می کنیم. حتی بطور غریزی در شرایط مشابه، عکس العمل های مشابهی از خود نشان می دهیم. پس مفهوم الگو، از اولین روزهای زندگی با ما بوده است و با ما خواهد بود. اینها را نوشتم تا بگویم وقتی درباره الگوها صحبت می کنیم، درباره مفهوم جدیدی صحبت نمی کنیم. بحث اصلی من این است، که برای استفاده از الگوها در نرم افزار(در تمام چرخه حیات یک نرم افزار)، آیا فقط تجربه های که در این رشته دارم را می توانیم بصورت الگوهای در بیاوریم و ار آن استفاده کنیم. یا می توانیم از تمام تجربیاتی که در زندگی روزمره داریم یا حتی تجربیاتی که افراد دیگر در رشته های مختلف دارند استفاده کنیم و با تغییر در شیوه بیان، آنها را در فرمتی ارائه دهیم که بشود در چرخه تولید نرم افزار یا هر مسئله دیگری از آن استفاده کنیم. برای نمونه، یکی از قوانین شیمی که در کتاب دوره دبیرستان که حتما آنرا خواندید، به اینصورت بود: اگر بر یک تعادل پویا تغییری تحمیل شود، آن تعادل در جهتی جا به جا می شود، که تغییر تحمیل شده را تعدیل کند و یا آنرا به کمترین مقدار ممکن برساند. این فانون را من چند سال قبل، در کتاب شیمی حواندم، ولی آن را به عنوان الگوی در شرایط مشابه، برای خودم قرار دادم. و به اعتقادم این قانون می تواند در هر سازمانی و هر تیمی بکار گرفته شود. یا یک مثال دیگر، همه افرادیکه مثل خودم حتی آشنایی بسیار کمی با RUP، داشته باشند. حتما این اصل اساسی RUP، را شنیده اند، اگر به مشکلات حمله نکنید، آنها به شما حمله خواهند کرد. دوباره جمله بسیار زیبایی است، که حتما باید بکار گرفته شود. اما می گویند شرکت رشنال برای استخراج این اصول و سایر خروجیهای RUP، هزاران پروژه را مورد ارزیابی قرار دادند. ولی به نظر من بعضی از این اصول مثل اصل بالا، از زمانهای دور وجود داشته ولی چون بطور شفاهی بیان شده است و یا به دلایلی دیگر، مورد توجه قرار نگرفته است. در پایین داستانی را از کتاب قصه هایی یرای پدران، فرزندان، نوه ها، می آورم که به قول گردآورنده این مجموعه، آقای پائولو کوئلیو، حتی منشا و خالق این داستان ها نیز معلوم نیست، که دقیقا همان اصل، RUP را بیان می کند اما به شیوه ای جذابتر(توصیه می کنم حتما بخوانید و مقایسه کنید):
استاد بزرگ و نگهبان، مراقبت از یک صومعه ذن را بین خود تقسیم کردند. یک روز، تکهبان در گذشت و باید کس دیگری را جایگزین او می کردند.
استاد بزرگ همه ی شاگردها را جمع کرد تا مشخص کند افتخار کار در کنار او، نصیب کدام یکی از آن ها خواهد شد.
استاد بزرگ گفت: مساله ای را مطرح می کنم. کسی که اول این مساله را حل کند، نگهبان جدید معبد خواهد بود.
بعد نیمکتی در وسط تالار گذاشت. روی نیمکت، گلدان سفالی گران بهایی گذاشت که گل سرخی در آن قرار داشت.
استاد گفت: مساله این است.
شاگردها، حیران، به گلدان نگاه کردند: به طرح های پیچیده و نادر روی سفال، به تازگی و زیبایی گل. منظور چه بود؟ چه کار باید می کردند؟ معما چه بود؟
پس از چند دقیقه، یکی از شاگردها برخاست، به استاد و شاگردهای پیرامونش نگاه کرد، و بعد، مصممانه به طرف گلدان رفت و آن را روی زمین انداخت و شکست.
استاد گفت: تو نگهبان جدید مایی.
وقتی شاگرد به جای خودش برگشت، استاد بزرگ توضیح داد:
من خیلی واضح توضیح دادم: گفتم که مساله ای پیش روی شما می گذارم.یک مساله، هر چه هم که زیبا و شگفت انگیز باشد، باید از پیش رو برداشته شود.مشکل، مشکل است. می تواند یک گلدان سفالی بسیار کمیاب باشد. می تواند عشق زیبایی باشد که دیگر برای ما معنایی ندارد. می تواند راهی باشد که باید آنرا ترک کنیم، اما اصرار داریم به راهمان ادامه بدهیم، چون به ما آرامش می بخشد. تنها یک راه برای از میان برداشتن مشکل وجود دارد: حمله ی مستقیم به آن. در این لحظه نمی توان دلسوزی کرد، نباید بگذلریم که جنبه های زیبا و شگفت انگیز تعارضی که پیش روی ماست، ما را وسوسه کند.
به اعتقاد من می توانیم با دقیق شدن و پالایش مسائل و تجربیات ساده، هم در زندگی روزمره و هم در محیط کار، الگوهای را استخراج کنیم که بتواند فرآیندهای را که داریم به شدت بهبود ببخشد و کارائی افراد، تیم ها، سازمان و جامعه را به طور قابل توجهی افزایش دهد. البته به قول یکی از دوستان خوب و خلاقم، اگر بتوانیم بین چندین الگو، تجربه، موضوع ارتباط برقرار کنیم، حتی ارتباطی که از تخیل سرچشمه بگیرد، می توانیم به نتایج خیلی بهتری دست بیابیم. ایشان همیشه می گویند وقتی من نمی توانم موضوع ها را به هم ار تباط بدهم، به سراغ بچه ها می روم، چون تخیل های عجیب برای آنها مثل بیشتر آدم بزرگها عیب نیست. شما هم می تواند امتحان کنید.
در زندگی روزمره خود به مسائلی برخورد می کنیم و برای غلبه بر این مسائل ممکن است چندین راه حل پیش رو داشته باشیم. ما با توجه به شرایط خود یا به اجبار یکی از این راه حل ها را انتخاب می کنیم. در طراحی و پیاده سازی یک نرم افزار نیز این شرایط وجود دارد یعنی جهت انجام یک عمل خاص می توانیم از چندین الگوریتم خاص استفاده کنیم در این حالت می توانیم برنامه را طوری طراحی کنیم که خود برنامه به صورت پویا الگوریتم با بازدهی بالاتر را اتنخاب کند یا این امکان را به برنامه کلاینت یا کاربر بدهیم تا یکی از الگوریتم ها را جهت انجام آن عمل انتخاب کند.
برای مثال فرض کنید که ما در حال طراحی یک برنامه مسیریابی برای یک شبکه هستیم. همانطوریکه می دانیم برای مسیر یابی الگوریتم های مختلفی وجود دارد که هر کدام دارای مزایا و معایبی هستند. و با توجه به وضعیت موجود شبکه یا عملی که قرار است انجام پذیرد باید الگوریتمی را که دارای بالاترین کارائی است انتخاب کنیم. همچنین این برنامه باید امکانی را به کاربر بدهد که کارائی الگوریتم های مختلف را در یک شبکه فرضی بررسی کنید. حالا طراحی پیشنهادی شما برای این مسله چست؟
دوباره فرض کنید که در مثال بالا در بعضی از الگوریتم ها نیاز داریم که گره های شبکه را بر اساس فاصله ی آنها از گره مبداء مرتب کنیم. دوباره برای مرتب سازی الگوریتم های مختلف وجود دارد و هر کدام در شرایط خاص، کارائی بهتری نسبت به الگوریتم های دیگر دارد. مسئله دقیقا شبیه مسئله بالا است و این مسله می توانند دارای طراحی شبیه مسله بالا باشد. پس اگر ما بتوانیم یک طراحی خوب برای این مسئله ارائه دهیم می توانیم این طراحی را برای مسائل مشابه به کار ببریم.
هر کدام از ما می توانیم نسبت به درک خود از مسئله و سلیقه کاری، طراح های مختلفی برای این مسئله ارائه دهیم. اما یک طراحی که می تواند یک جواب خوب و عالی باشد، الگوی استراتژی است که توانسته است بارها و بارها به این مسئله پاسخ بدهد.
الگوی استراتژی گزینه مناسبی برای مسائلی است که می توانند از چندین الگوریتم مختلف به مقصود خود برسند.
نمودار UML این الگو بصورت زیر است:

اجازه بدهید، شیوه کار این الگو را با مثال مربوط به مرتب سازی بررسی کنیم. فرض کنید که ما تصمیم گرفتیم که از سه الگویتم زیر برای مرتب سازی استفاده کنیم.
|
1 |
Shell Sort |
|
2 |
Quick Sort |
|
3 |
Merge Sort |
ما برای مرتب سازی در این برنامه دارای سه استراتژی هستیم. که هر کدام را به عنوان یک کلاس جداگانه در نظر می گیریم (همان کلاس های ConcreteStrategy). برای اینکه کلاس Client بتواند به سادگی یک از استراتژی ها را انتخاب کنید بهتر است که تمام کلاس های استراتزی دارای اینترفیس مشترک باشند. برای این کار می توانیم یک کلاس abstract تعریف کنیم و ویژگیهای مشترک کلاس های استراتژی را در آن قرار دهیم و کلاس های استراتژی آنها را به ارث ببرند(همان کلاس Strategy) و پیاده سازی کنند.
|
کلاس abstract که کلاس های استراتژی آنرا به ارث می برند. |
|
abstract class SortStrategy { public abstract void Sort(ArrayList list); } |
|
کلاس مربوط به QuickSort |
|
class QuickSort : SortStrategy { public override void Sort(ArrayList list) { // الگوریتم مربوطه } } |
|
کلاس مربوط به ShellSort |
|
class ShellSort : SortStrategy { public override void Sort(ArrayList list) { // الگوریتم مربوطه } } |
|
کلاس مربوط به MergeSort |
|
class MergeSort : SortStrategy { public override void Sort(ArrayList list) { // الگوریتم مربوطه } } |
|
کلاس Context که یکی از استراتزیها را برای مرتب کردن لیست به کار می برد. |
|
class SortedList { private ArrayList list = new ArrayList(); private SortStrategy sortstrategy; public void SetSortStrategy(SortStrategy sortstrategy) { this.sortstrategy = sortstrategy; } public void Add(string name) { list.Add(name); } public void Sort() { sortstrategy.Sort(list); } } |
بعضی ار مردم نمی توانند زندگی را بدون قهوه تصور کنند و بعضی دیگر زندگی را بدون چای نمی توانند تصور کنند. ولی جزء تشکیل دهنده اصلی هر دو کافئین است. شباهت های دیگر نیز بین این دو مورد وجود دارد. روش ساخت هر دوی آنها تقریبا مشابه است. شما اغلب موارد به ترتیب زیر عمل می کنید.
دستوالعمل ساخت قهوه:
1. مقداری آب را می جوشانید.
2. مقدار قهوه در آب جوشانده شده می ریزد تا دم بکشد.
3. فهوه را در فنجان می ریزد.
4. مقداری شکر و یا شیر به آن اضافه می کنید.
دستورالعمل ساخت چای:
1. مقداری آب را می جوشانید.
2. مقدار چای خشک در آب جوشانده شده می ریزد تا دم بکشد.
3. چای را در فنجان می ریزد.
4. مقداری شکر به آن اضافه می کنید.
اگر بخواهیم برنامه ای برای تهیه چای و قهوه بنویسیم کلاس های را به صورت زیر خواهیم داشت.
|
کلاس قهوه |
|
Public Class Coffee 'هر مرحله از دستورالعمل ساخت قهوه به عنوان یک متد در نظر گرفته شده است. Public Sub prepareRecipe() 'مراحل تهیه قهوه boilWater() brewCoffeeGrinds() pourInCup() addSugereAndMilk() End Sub Public Sub boilWater() Console.WriteLine("جوشاندن آب") End Sub Public Sub brewCoffeeGrinds() Console.WriteLine("ریختن پودر قهوه به داخل آب جوشانده شده") End Sub Public Sub pourInCup() Console.WriteLine("ریختن قهوه به فنجان") End Sub Public Sub addSugereAndMilk() Console.WriteLine("اضافه کردن شیر و شکر") End Sub End Class |
|
کلاس چای |
|
Public Class Tea Public Sub prepareRecipe() boilWater() brewTea() pourInCup() addSugere() End Sub Public Sub boilWater() Console.WriteLine("جوشاندن آب") End Sub Public Sub brewTea() Console.WriteLine("ریختن چای به داخل آب جوشانده شده") End Sub Public Sub pourInCup() Console.WriteLine("ریختن چای به فنجان") End Sub Public Sub addSugere() Console.WriteLine("اضافه کردن شکر") End Sub End Class |

طراحی بالا یک طراحی خوب است اما نمی شود طراحی را بهتر از این کرد؟ اگر توجه کنیم متوجه می شویم که برای تهیه هر دو مورد الگوریتم یکسانی را به کار می بریم:
1. جوشاندن آبی
2. اضافه کردن چای یا قهوه به آب جوشیده شده
3. ریختن نوشیدنی به دست امده در فنجان
4. اضافه کردن چاشنی مورد نظر به نوشیدنی
پس ما می توانیم با کمی تغییرات متد prepareRecipe را در کلاس پایه پیاده سازی کنیم.
|
Public Sub prepareRecipe() boilWater() brewTea() pourInCup() addSugere() End Sub |
Public Sub prepareRecipe() boilWater() brewCoffeeGrinds() pourInCup() addSugereAndMilk() End Sub |
|
Public Sub prepareRecipe() boilWater() brew () pourInCup() addCondiments() End Sub |

حالا ما برای این مسله به یک طراحی ائده ال دست یافتیم. کاری که ما برای حل این مسله انجام دادیم به نام الگوی Template method شناخته می شود. دلیل نامگذاری این الگو را می توانیم با مشاهده متد prepareRecipe از کلاس پایه درک کنیم. اولین اینکه prepareRecipe یک متد است و دوما به عنوان یک قالب برای الگوریتم به کار می رود. برای نمونه در این مثال شامل الگوریتم تهیه یک نوشیدنی می باشد.
الگوی Template method، مراحل انجام یک الگوریتم را در یک متد در کلاس پایه تعریف می کند و اجازه می دهد زیر کلاس ها یک یا چند مرحله از الگوریتم را پیاده سازی کنند. در واقع اسکلت یک الگوریتم در یک متد تعریف می شود.
هدف ما در این الگو، ایجاد یک قالب برای یک الگوریتم است. یک قالب یک الگوریتم را به صورت مجموعه ای از مراحل تعریف می کند. هر کدام از این مراحل به عنوان یک متد در نظر گرفته می شود. که بعضی از متدها در همان کلاس پایه پیاده سازی می شود و بعضی از متدها به صورت abstract در کلاس پایه در نظر گرفته می شود و در زیر کلاس ها پیاده سازی می شود.
نمودار UML :

نمودار UML این الگو به صورت بالا است. که در آن کلاس AbstractClass یک کلاس Abstract است. که الگوی قالب در آن تعریف می شود. و متد primitiveOperation یک متد Abstract است (یک مرحله از الگوریتم) که در زیر کلاس پیاده سازی خواهد شد. همانطوریکه در مثال بالا مشاهده کردیم می تواند بیش از یک کلاس ConcerteClass وجود داشته باشد. این حالت زمانی رخ می دهد که بعضی از مراحل الگوریتم بتواند در روش های مختلف پیاده سازی شود.
یک سرویس دهنده اینترنتی را در نظر بگیرید. این شرکت دارای سه سطح پشتیبانی به شرح زیر برای کاربران است:
سطح 1: در این سطح به مشکلات ابتدایی کاربران مانند فراموش کردن کلمه عبور کاربران و ... رسیدگی می شود.
سطح 2: اگر اعضای گروه سطح 1 نتوانند مشکل مورد نظر را حل کنند. مشکل برای حل به گروه سطح 2 ارجاع داده می شود.
سطح 3: اگر اعضای گروه 2 نیز نتوانند مشکل مورد نظر را حل کنند.مشکل برای حل به گروه سطح 3 ارجاع داده می شود. در این سطح برای حل مشکل یک قرار ملاقات در محل مشکل با مشتری گذاشته می شود.
همانطوریکه در مثال بالا مشخص است. ممکن است هر کدام از گروه ها به در خواست ارائه شده پاسخ دهند. ولی دقیقا مشخص نیست کدام گروه به در خواست ارائه شده پاسخ خواهد داد. اما همه گروه ها باید شانس این را داشته باشند که به درخواست ارائه شده پاسخ دهند. این شانس اولویت دار است. برای نمونه در مثال بالا ابتدا به تیم سطح 1 این شانس داده می شود تا مشکل را حل کند. در صور ت عدم حل مشکل به ترتیب، شانس به تیمهای 2 و 3 داده می شود. این امکان نیز وجود دارد که هیچ یک از تیم ها نتوانند این مشکل را حل کنند و درخواست ارائه شده بدون پاسخ بماند.
مثالی که در بالا ارائه شده تقریبا تعریفی از الگوی Chain of responsibility (زنجیره مسئولیت) می باشد.
در این الگو به یک شی اجازه داده می شود در خواستی را ارسال کند بدون اینکه بداند کدام شی یا اشیاء آنرا دریافت خواهند کرد و به آن پاسخ خواهند داد. این کار با ارسال درخواست به زنجیره ای از اشیاء صورت می گیرد. هر شی در این زنجیره می تواند به درخواست ارائه شده پاسخ دهد یا آن را به شی بعدی در زنجیره ارسال کند. در واقع هر شی در زنجیره بعنوان یک پاسخگو برای درخواست ارائه شده عمل می کند . اگر شی مورد نظر بتواند درخواست راجواب دهد، پاسخ آن را می دهددر غیر اینصورت آن را به شی جانشین اش (شی بعدی در زنجیره) ارسال می کند.
برای ادامه موضوع یک مثال دیگر را در نظر می گیریم. فرض کنید شما مسول نوشتن یک برنامه برای شبیه سازی فرآیند تائید یک درخواست سفارش از یک شرکت هستید. در این شرکت، بعد از اینکه درخواستی توسط مشتری ارائه می شود باید در ابتدا توسط مدیر تائید شود و در صورت تائید توسط مدیر، جنس به مشتری فروخته خواهد شد. این شرکت دارای چهار سطح مدیریت می باشد و مشخص شده است که هر مدیر سفارشاتی با مبلغ چقدر را می تواند تائید کند.که حد مبالغی که می توانند تائید کنند در جدول زیر مشخص شده است.
|
سطوح مدیریت |
مبلغ سفارش |
|
مدیر نمایندگی |
25000 |
|
مدیر ناحیه |
100000 |
|
نايب رئيس |
200000 |
|
رئيس |
400000 |
شما برای اینکار چه الگوی را پیشنهاد می کنید؟
اجاز ه بدهید کار را با الگوی زنجیره مسولئیت ادامه دهیم. در ابتدا یک کلاس برای سفارش به نام سفارش خرید در نظر می گیریم.
سپس یک کلاس انتزاعی به نام مدیریت سفارش تعریف می کنیم که دارای یک اینترفیس مشترک برای هر کدام از سطوح مدیریت است. که نمودار کلاس مربوط به مدیریت سفارش و سطوح مدیریت به صورت زیر خواهد بود. که متد تعیین شی جانشین (SetSuccessor)، برای مشخص کردن شی بعدی (شی جانشین) در زنجیره عمل می کند.
برای تائید یک سفارش، کارهای زیر صورت خواهد گرفت:
1. ایجاد یک مجموعه از اشیای که می توانند به درخواست های رسیده شده پاسخ بدهند (سطوح مدیریت). و قرار دادن آنها در یک زنجیره به طور مرتب بر حسب مقادیری که می توانند تائید کنند. هر یک از این اشیاء توسط متد SetSuccessor به شی بعدی متصل می شود.(کلاس Main)
2. یک درخواست تائید به اولین شی موجود در زنجیره ارسال می شود که این کار توسط فراخوانی متد authorize آن شی انجام می شود. اگر مبلغ سفارش از مقدار مبلغی که ان شی نی تواند تائید کند کوچکتر باشد، آنرا تائید می کند در غیر اینصورت آن درخواست را به شی بعدی در زنجیره ارسال می کند. (برای مثال در کد زیر می توانید کلاس BranchManager را بررسی کنید. )
|
کلاس مدیریت سفارش |
|
abstract class RHandler { protected RHandler successor; public void SetSuccessor(RHandler successor) { //تعیین شی جانشین this.successor = successor; } public abstract void authorize (PurchaseRequest purchase); } |
|
کلاس مدیر نمایندگی |
|
class BranchManager: RHandler public override void authorize (PurchaseRequest purchase) if (purchase.Amount <=25000) } |
|
کلاس Main |
|
class MainApp { static void { BranchManager A = new BranchManager(); RegionalDirector B = new RegionalDirector(); VicePresident C = new VicePresident(); President D = new President(); A.SetSuccessor(B); B.SetSuccessor(C); C.SetSuccessor(D); Purchase p = new Purchase( 1, "Order 1",15000); A.authorize(p); Purchase p = new Purchase( 2, "Order 2",110000); A.authorize(p); } } |

حال شما چطور است؟، سوالی است که در یک روز بارها از شما پرسیده می شود یا شما از دیگران می پرسید. حال شما یک ویژگی ثابت شما نیست بلکه یک ویژگی پویا است که ممکن است با توجه به رویدادهای خارجی یا درونی تغییر کند. و شما می توانید در مقابل یک رویداد مشابه با توجه به حالتان رفتار متفاوتی از خود نشان دهید.
علاوه بر خودمان اکثر اشیای که در اطراف ما قرار دارند دارای حالت های متفاوتی هستند و با توجه به رویداد های بیرونی و داخلی از حالت به حالت دیکر می روند و رفتار متفاوتی را در هر حالت از خود بروز می دهند.
برای مثال فرضی کنید با یک برنامه مانند Windows Media Player، در حال دیدن یک فیلم هستید. این برنامه دکمه های مانند Play، Pause و Stop دارد که با کلیک روی آنها می توانید برنامه را به حالت همنام با آن دکمه ببرید. پس برای این شی، سه حالت بالا را می توانید در نظر بگیرید. حال تصویر کنید که یک برنامه شبیه به مثال بالا را خودمان می خواهیم بنویسیم. یکی از شیوه ها برای انجام این کار می تواند به این ترتیب باشد که ما در ابتدا تمام حالاتی را که یک شی Media Player، می تواند قبول کند را جمع آوری می کنیم. در مرحله دوم برای هر حالت یک متغییر تعریف می کنیم و مقادیر مربوط به آنها را مشخص می کنیم و علاوه بر اینها یک متغییر برای نگهداری حالت فعلی شی در نظر می گیریم.
const int play=0;
const int stop=1;
const int pause=2;
int state = stop; //حالت اولیه
سپس تمام اعمالی و رویدادهای که در سیستم می تواند رخ دهد را جمع آور می کنیم. و در انتها یک کلاس ایجاد می کنیم که به عنوان یک ماشین حالت عمل کند، برای هر عمل یک متد ایجاد می کنیم که جملات شرطی را به کار می برند تا رفتار مناسبی را که در هر حالت نیاز است را پیاده سازی کنند.
public void Stop()
{
if (state == play)
{
//عملیات مورد نظر
}
if (state == pause)
{
//عملیات مورد نظر
}
if (state == stop)
{
//عملیات مورد نظر
}
}
روش بالا می تواند برای پیاده سازی یک شی که دارای چندین حالت است به کار برود. ولی تصور کند که یک حالت جدید را بخواهیم به شی اضافه کنیم و یا در نحوه عملکرد شی در یک حالت خاص تغییر دهیم مطمئنا کار مشکلی را خواهیم داشت.
یک روش خوب و کارا برای پیاده سازی اشیای که در طول حیات خود می توانند چندین حالت مختلف داشته باشند، می تواند چگونه باشد؟ شاید شما پاسخ های خوب و بهتری بتوانید ارائه کنید. ولی یکی از روش ها می تواند به صورت زیر باشد:
1. در ابتدا یک اینترفیس به نام state تعریف می کنیم که شامل یک متد برای هر عملی است که شی مورد نظر می تواند انجام دهد.
2. در مرحله دوم برای هر حالت شی یک کلاس تعریف می کنیم که اینترفیس state را پیاده سازی می کند. در واقع این کلاس مسول پاسخگویی به رویدادها است. زمانیکه شی در این حالت قرار دارد.
برای نمونه در همان مثال Media Player، در ابتدا ما یک کلاس اینترفیس به نام state تعریف می کنیم. سپس برای هر حالت یک کلاس تعریف می کنیم که اینترفیس state را پیاده سازی می کند. برای پیاده سازی هر حالت ما نیاز داریم تا رفتار هر کلاس را مشخص کنیم هنکامیکه هر عمل فرا خوانی می شود.

بعد از پیاده سازی تمام حالات شی مورد نظر، به کلاس اصلی یا همان کلاس شی مورد نظر (Media Player) می رسیم. این کلاس همان متد های روش اول را خواهد داشت اما با چندین تفاوت:
1. به جای استفاده از متغییرهای عددی برای هر حالت و حالت فعلی، از اشیاء که برای حالت ها تعریف کردیم استفاده می کنیم.
|
کد جدید |
کد قدیمی |
|
public class MediaPlayer{ State MediaState; PlayState playstate; PauseState pausestate; StopState stopstate; MediaPlayer{ playstate = new PlayState(); new PauseState ();= pausestate stopstate = new StopState(); MediaState = stopstate; } |
Public class MediaPlayer{ const int play=0; const int stop=1; const int pause=2; MediaPlayer{ const int state = stop; //حالت اولیه } |
2. در پیاده سازی عمل ها دیگر از جملات شرطی استفاده نمی کنیم. بلکه وقتی عملی فراخوانی می شود آن عمل به کلاس حالت فعلی برای انجام واگذار می شود.
public void Stop()
{
MediaState.stop();
}
چون MediaState اشاره گر به حالت فعلی است. متد stop حالت فعلی اجاره خواهد شد بدون هیچ جمله ی شرطی.
نمودار الگوی حالت (State Pattern):
ما تا اینجا یک الگوی حالت را بطور کامل پیاده سازی کردیم. ولی دوباره نکات بالا را باهم بررسی می کنیم.
1. رفتار یک شی توسط حالت داخلی آن شی مشخصی می شود که در مقابل رخ دادن یک رویداد می تواند تغییر کند.
2. تعداد حالات یک شی را باید بتوانیم افزایش بدهیم بدون اینکه مجبور باشیم قسمت های زیادی از کد را مرور کنیم یا تغییر دهیم.
الگوی حالت، به یکی شی اجازه می دهد رفتارش را زمانیکه حالت داخلی شی تغییر کند، تغییر دهد. و همچنین با در نظر گرفتن هر حالت به عنوان یک شی جداگانه، حالات شی را نهان سازی می کند. و ما می توانیم هر تعداد حالات مورد نظر را به شی مربوطه اضافه کنیم بدون اینکه در پیاده سازی کلاس context تغییر انجام دهیم.
کلاس Context کلاسی است که می تواند دارای چندین حالت داخلی باشد در مثال ما همان Media Player.
اینترفیس State یک اینترفیس مشترک برای همه حالت ها تعریف می کند.
کلاس های Concrete، عمل های را که شی در آن حالت انجام می دهد، پیاده سازی می کنند.(کلاسهای PlayState, StopState,PauseState)
Adapter Pattern:
الگوی سازگارساز (وفق دهنده، مبدل، Adapter Pattern)، الگوی است که در دنیا واقعی نمونه های زیادی از این الگو وجود دارد و به همین خاطر درک این الگو زیاد مشکل نخواهد بود.
شکل زیر را در نظر بگیرید و فرض کنید که کامپیوتر laptop شما دارای دو شاخه ای به شکل زیر است ولی پریز های برقی که در دیوار نصب شده است به شکلی است که در سمت چپ تصویر نشان داده شده است. شما برای استفاده از لپ تاپ خود در این محل چه کاری را انجام می دهید؟

شما می توانید برای حل این مشکل از یک مبدل استفاده کنید. که بین دو شاخه، لپ تاپ شما و پریز برق قرار می گیرد و شما می توانید با قرار دادن دو شاخه در مبدل و زدن مبدل به پریز از برق استفاده کنید. در واقع مبدل، اینترفیس پریز را به گونه ای تغییر می دهد که با اینترفیسی که لپ تاپ شما نیاز دارد همخوانی داشته باشد.
مثال بالا یک مثال از دنیا واقعی بود. ولی یک وفق دهنده شی گرا چست؟ وفق دهنده های شی گرا دقیقا کار مشابهی را انجام می دهند. آنها یک اینترفیس را به گونه ای تغییر می دهند که سرویس گیرنده ها انتظار آنرا دارند.
وفق دهند های شی گرا:
فرض کنید که شما یک سیستم نرم افزاری دارید که نیاز دارد با کتابخانه ی از کلاس های جدید که مربوط به فروشندگان است کار کند، اما کلاس های جدید به گونه ای طراحی شده اند که اینترفیس متفاوتی را نسبت به اینترفیس قبلی دارند.

برای حل این مشکل شما چیکار می کنید؟ می توانید سیستم نرم افزاری موجود را به گونه ای باز نویسی کنید که با کلاس هاس جدید همخوانی داشته باشد. یا کلاس های جدید را به گونه ای تغییر می دهید که با سیستم موجود همخوانی داشته باشد. اما آیا راه حل کم هزینه تری وجود ندارد که بدون تغییر هیچ کدام از سیستم ها، سیستم کار خود را انجام دهد. شما می توانید یک کلاس بنویسید که اینترفیس فروشندگان را به گونه ای تغییر بدهد که سیستم موجود انتظار دارد.

Adapter(سازگار ساز، وفق دهنده) مانند یک واسطه عمل می کند و درخواست های را که از سیستم موجود می رسد دریافت می کند و آنها را به شکلی تغییر می دهد و برای کلاس مورد نظر در سیستم فروشندگان ارسال می کند که قابل درک و اجرا برای آن کلاس باشد.

برای نمونه در شکل بالا کلاس کلاینت متدی به نام Max را که در اینترفیس Math1 تعریف شده است را نیاز دارد. ولی کلاس های که این اینترفیس را پیاده سازی می کنند. عملی را که کلاس کلاینت در نظر دارد انجام نمی دهند. ولی اینترفیس Math2 دارای عملی به نام Maxvalue است که توسط کلاس

