الگوی Composite

همانطوریکه روی صندلی نشسته اید به بدن خود تان توجه کنید و آنرا مورد بررسی قرار بدهید، شاید بدن خود را به صورت ترکیبی از چندین شی مانند دست، پا، چشم و …  ببینید. سپس با دست خود یک کتاب را بردارید و شروع به ورق زدن آن بکنید، مشاهده می کنید که یک کتاب از ده ها صفحه تشکیل شده است و هر صفحه از چندین پارگراف و هر پار گراف از چندین سطر تشکیل شده است.  روی آیکون 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 با ویژگیهای که در بالا اشاره شد هست.

  1. ۷ مرداد ۱۳۸۶ در ۱۷:۴۶ | #1

    eiwal , mahshar , jaleb va ziba , damet garm , barat arezooye movaffaghiiat mikonam , be site ma ham sar bezan nazar yadet nare : http://www.mobdl.com

  2. ۲۳ مرداد ۱۳۸۶ در ۰۲:۴۸ | #2

    سلام مهندس. من یه سوال درباره ی سی شارپ داشتم.آیا در سی شارپ متدی هست که یک عدد به فرمت دابل رو به یک آرایه ی از نوع کاراکتر تبدیل کنه؟

  3. محمد
    ۹ آذر ۱۳۸۹ در ۰۹:۰۰ | #3

    سلام
    ممنون از مطلبتون
    در متن آورده اید:
    در مورد متد مشترک getComponentSize، با هر دو شی FileComponent و DirComponent رفتار یکسانی را دارد. اما Clinet برای فراخوانی متدهای مانند addComponent و getComponent نیاز دارد که دو شی را از همدیگر تشخیص دهد، چونکه این متدها فقط برای DirComponent تعریف شده است. به همین دلیل client نیاز دارد تا نوع شی مورد نظر را برای فراخوانی این متدها چک کند تا مطمئن شود که با یک نمونه از DirComponent سر و کار دارد. برای بهبود این طراحی بگونه ای که نیازی به تشخیص تفاوت بین دو شی وجود نداشته باشد، طراحی را به چه صورت تغییر می دهد.
    سوال من این است:
    بهر حال کلاینت برای فراخوانی متدی مثل addComponent حتی در الگوی دوم، می بایست نوع واقعی کلاس را بدانددر غیر اینصورت متدی را فراخوانی کرده که هیچ اطمینانی از عملکرد آن ندارد. با این اوصاف بهرحال نیاز به تشخیص تفاوت بین دو شی خواهد بود و برای همین من فلسفه وجودی چنین الگوئی را درک نکردم

  1. بدون بازتاب