IranIT.info Articles
عنوان الگوهاي طراحي، قسمت هفتم : ادامه فصل اول، بخش 6-1
نويسندهحسن ابوالحسنى تاريخ ارسال 23/12/1381 نام قسمت فناورى
1.6 چگونه الگوهاي طراحي مسائل طراحي را حل مي کنند
الگوهاي طراحي بسياري از مسائل طراحي که طراحان سيستم هاي شيء گرا بطور روزمره با آنها مواجه هستند را به طرق مختلف حل مي کنند. در اينجا چند نمونه از اين مسائل و چگونگي حل آنها بوسيله الگوهاي طراحي آمده است.

يافتن اشياء مناسب
برنامه هاي شيء گرا از اشياء تشکيل مي شوند. يک شيء داده ها و پروسيجرهايي که بر روي آن داده ها عمل مي کنند را بسته بندي مي نمايد. پروسيجرها معمولا متدها يا اعمال نيز ناميده مي شوند. يک شيء يک عمل را هنگاميکه درخواستي (يا پيغامي) از يک مشتري دريافت کند اجرا مي نمايد.

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

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

متدلوژيهاي شئ گرا مشي هاي متفاوت بسياري مرحمت مي کنند. مي توانيد يک جمله تشريح کننده مسئله را نوشته، اسامي و افعال آنرا جدا کرده، و کلاسها و اعمال مربوطه را تشکيل دهيد. يا مي توانيد بر روي همکاريها و وظايف در سيستم تاکيد داشته باشيد. و يا مي توانيد دنياي واقعي را مدل کرده و اشياء يافت شده در طي تحليل را به اشياء متناظري در سيستم کامپيوتري ترجمه کنيد. همواره در مورد اينکه چه مشيي بهتر است اختلاف سليقه وجود دارد.

بسياري از اشياء در هنگام طراحي سيستم از متناظر آنها در مدل تحليل سيستم حاصل مي شوند. ولي طراحي هاي شئ گرا اغلب با کلاسهايي که هيچگونه متناظري در دنياي واقعي ندارند ختم مي شوند. برخي از اينها کلاسهايي سطح پايين نظير آرايه ها هستند. کلاسهاي ديگر خيلي سطح بالاتر هستند. براي مثال الگوي ترکيب (composite) تجردي براي برخورد يکسان با اشياء معرفي مي کند که هيچگونه شئ فيزيکي متناظري ندارد. مدل کردن دنياي واقعي بصورت محض ما را به ساخت سيستمي هدايت مي کند که منعکس کننده واقعيت هاي امروز است ولي نه لزوما در برگيرنده واقعيت هاي فردا. تجرد هايي که در طي طراحي ايجاد مي شوند کليدي براي ساخت يک طراحي قابل انعطاف است.

الگوهاي طراحي براي تشخيص تجردهاي کمتر بديهي و اشيايي که آنها را مي سازند کمک مي کند. بعنوان نمونه، اشيايي که يک پروسه يا الگوريتم را نمايش مي دهند در طبيعت وجود ندارند، ولي در عين حال آنها قسمتي اساسي از يک طراحي قابل انعطاف را تشکيل مي دهند. الگوي استراتژي(strategy) چگونگي پياده سازي فاميلي از الگوريتم هاي قابل جايگزيني را تشريح مي کند. الگوي حالت (state) هر وضعيت يک موجوديت را بصورت يک شئ نمايش مي دهد. اين اشياء بندرت در طول تحليل يا حتي قدمهاي اوليه طراحي يافت مي شوند، آنها بعدا در خلال ايجاد طراحي قابل انعطاف تر و قابل استفاده مجدد تر کشف مي شوند.

تعيين گرانيته يک شئ
اشياء مي توانند بسيار از نظر اندازه و تعداد متغيير باشند. آنها مي توانند هر چيزي از اشياء سطح پايين نزديک به سخت افزار تا سطح بالا تا حد يک برنامه کاربردي را نمايش دهند. چگونه تصميم مي گيريم چه چيزي يک شئ باشد؟

الگوهاي طراحي اين مسئله را نيز آدرس دهي مي کنند. الگوي نماي خارجي (facade) چگونگي نمايش کل يک سيستم بصورت اشياء را تشريح کرده، و الگوي مگس وزن (flyweight) چگونگي پشتيباني از تعداد زيادي از اشياء داراي گرانيته خيلي پايين را شرح مي دهد. الگوهاي طراحي ديگر روشهاي مشخصي براي تجزيه يک شئ به اشياء کوچکتر را تشريح مي کنند. کارخانه مجرد (abstract factory) و سازنده (builder) اشيايي که تنها وظيفه شان ساخت اشياء ديگر است را ارزاني مي دارند. ملاقات کننده(visitor) و فرمان(command) اشيايي که تنها وظيفه شان پياده سازي درخواستي براي يک شئ يا گروهي از اشياء ديگر است را ارزاني مي دارند.

تعيين اينترفيسهاي يک شئ
براي هر عمل تعريف شده براي يک شئ، نام عمل، اشيايي که بعنوان پارامتر دريافت کرده، و مقدار برگشتي از آن را تعيين مي شوند. اين مشخصات بعنوان امضاء (signature) عمل شناخته مي شود. مجموعه تمام امضاء هاي تغريف شده براي اعمال يک شئ اينترفيس شئ ناميده مي شود. اينترفيس يک شئ مجموعه کل درخواستهايي که به شئ مي تواند فرستاده شود را تعيين مي کند. هر درخواستي که با يک امضاء در اينترفيس شئ تطابق داشته باشد مي تواند به شئ ارسال شود.

يک تايپ نامي است که براي مشخص کردن يک اينترفيس بکار مي رود. گوئيم که يک شئ از نوع "پنجره" است اگر تمام درخواستها براي اعمال تعريف شده توسط اينترفيس "پنجره" را قبول کند. يک شئ ممکن است نوعهاي متفاوتي داشته باشد، و برعکس اشياء بسيار متفاوت مي توانند از يک نوع باشند. قسمتي از اينترفيس يک شئ ممکن است با يک نوع مشخص شده و بخشهاي ديگر آن با نوعهاي ديگر. دو شئ از يک نوع تنها نياز دارند تا در قسمتهايي از اينترفيسهايشان مشترک باشند. اينترفيسها مي توانند اينترفيسهاي ديگري را بصورت زير مجموعه شامل شوند. گوييم يک نوع زير نوع يک نوع ديگر است اگر اينترفيس آن شامل اينترفيس سوپر تايپ باشد. اغلب مي گوييم که يک زير نوع اينترفيس سوپر تايپ را به ارث مي برد. اينترفيس ها در سيستم هاي شئ گرا اهميت فراواني دارند. اشياء تنها از طريق اينترفيس شان شناخته مي شوند. راهي براي شناخت يک شئ يا ارسال درخواستي به آن وجود ندارد مگر اينکه از طريق اينترفيس آن عمل شود. اينترفيس يک شئ چيزي در مورد پياده سازي آن نمي گويد – اشياء مختلف آزادند تا درخواستها را بصورت متفاوتي پياده سازي نمايند. يعني اينکه دو شئ که داراي پياده سازي کاملا متفاوتي هستند مي توانند اينترفيس يکساني داشته باشند.

هنگاميکه درخواستي براي يک شئ فرستاده مي شود، عملي که انجام مي شود بستگي به درخواست و شئ دريافت کننده آن دارد. اشياء متفاوتي که درخواستهاي يکساني تدارک مي بينند ممکن است پياده سازي هاي متفاوتي براي اعمال داشته باشند. انتساب يک درخواست به يک شئ و يکي از اعمال آن در زمان اجرا بنام انقياد پويا (dynamic binding) شناخته مي شود.

انقياد پويا به اين معني است که صدور يک درخواست شما را به يک پياده سازي خاص تا زمان اجرا متعهد نمي سازد. در نتيجه مي توانيد برنامه هايي بنويسيد که که يک شئ با اينترفيس مشخص شده را انتظار داشته باشند، با علم به اينکه هر شئ که اينترفيس در ستي داشته باشد درخواست را قبول مي کند. بيشتر از اين، انقياد پويا اجازه مي دهد تا اشيايي که داراي اينترفيس هاي يکساني هستند را در زمان اجرا با يکديگر جايگزين کنيد. اين نوع قابليت جايگزيني بعنوان چند ريختي (polymorphism) شناخته مي شود که مفهمومي کليدي در سيستم هاي شئ گراست. چند ريختي اين امکان را فراهم مي کند که يک شئ مشتري فرضيات کمي خارج از يک اينترفيس مشخص در مورد ديگر اشياء داشته باشد. و باعث ساده تر شدن تعريف مشتري ها شده، اشياء را از يکديگر جدا ساخته و اجازه مي دهد تا ارتباطاتشان را در زمان اجرا تغيير دهيد.

الگوهاي طراحي به شما در تشخيص عناصر کليدي در يک اينترفيس و انواع دادهايي که در طي يک اينترفيس ارسال مي شود کمک مي کند. يک الگوي طراحي همچنين ممکن است به شما بگويد که چه چيزي را در اينترفيس قرار ندهيد. الگوي خاطره (memento) يک مثال خوب است. اين الگو اشياء را مقيد مي کند که دو اينترفيس را تعريف نمايند: يک اينترفيس محصور شده که اجازه مي دهد مشتريها آن اشياء را نگهداري و کپي کنند و يک اينترفيس ويژه که تنها شئ اصلي مي تواند استفاده کرده تا حالت يک شئ خاطره را دخيره و بازيابي کند.

الگوهاي طراحي همچنين ارتباطات بين اينترفيس ها را معين مي کنند. بطور مشخص آنها اغلب نياز دارند که بعضي از کلاسها داراي اينترفيس مشابه بوده و يا محدوديتهايي را بر روي اينترفيس برخي از آنها قرار مي دهند. براي مثال هم آذينگر (decorator) و هم نماينده (proxy) لازم دارند که اينترفيس اشياء decorator و proxy با اشياء decorated و proxied يکسان باشند. در الگوي ملاقات کننده (visitor) اينترفيس visitor بايستي منعکس کننده تمام کلاسهايي از اشياء که visitor مي تواند ملاقات کند باشد.

تعيين پياده سازي اشياء
تا کنون کمتر در مورد اينکه چگونه يک شئ بطور واقعي پياده سازي مي شود صحبت کرده ايم. پياده سازي يک شئ بوسيله کلاس آن تعريف مي شود. کلاس مشخص کننده حالت دروني شئ و نحوه نمايش آن و همچنين اعماليکه آن شئ مي تواند انجام دهد است.

نمادگذاري ما براساس OMT (که در ضميمه ب خلاصه شده است) يک کلاس را بصورت مربع مستطيل که نام کلاس در آن بصورت برجسته آورده مي شود، معين مي کند. اعمال با نگارشي معمولي در زير نام کلاس آورده مي شوند. هر داده اي که کلاس تعريف مي کند پس از اعمال آن ليست مي شود. خطوطي نام کلاس را از اعمال و اعمال را از داده ها جدا مي سازد.

انواع مقادير برگشتي از اعمال و انواع متغييرهاي نمونه (instance variables) اختياري هستند، چرا که فرض در مورد زبان پياده سازي با انواع ايستا نداريم.

اشياء با نمونه سازي کردن يک کلاس ساخته مي شوند. شئ را نمونه اي از يک کلاس گوئيم . پروسه نمونه سازي حافظه اي به داده هاي داخلي شئ (که از متغيرهاي instance تشکيل مي شود) اختصاص داده و اعمال را به اين داده ها ارتباط مي دهد.

يک خط چين جهت دار مشخص مي کند که يک کلاس کلاس ديگري را نمونه سازي مي کند. کمان به کلاس مربوط به اشياء نمونه سازي شونده اشاره مي کند.

کلاسهاي جديد مي توانند بر اساس کلاسهاي موجود توسط وراثت کلاسي(class inheritance) تعريف شوند. هنگاميکه يک زير کلاس(subclass) از يک کلاس پدر (parent class) ارث مي برد تمام داده ها و اعماليکه کلاس پدر تعريف مي کند را شامل مي شود. اشيائيکه نمونه هايي از يک زير کلاس هستند تمام دادهاي تعريف شده بوسيله زير کلاس و کلاسهاي پدر آن را دارا بوده و قادرند تا تمام اعمال تعريف شده بوسيله اين زير کلاس و کلاسهاي پدر آنرا اجرا کنند. ارتباط زير کلاس بودن را بوسيله يک خط عمودي و يک مثلث مشخص مي کنيم.

يک کلاس مجرد (abstract class) کلاسي است که هدف اصلي از آن تعريف يک اينترفيس مشترک براي زير کلاسهايش است. يک کلاس مجرد قسمتي يا تمامي پياده سازي اعمال را به زير کلاسها محول کرده و بنابراين يک کلاس مجرد نمي تواند نمونه سازي شود. اعماليکه يک کلاس مجرد تعريف کرده ولي پياده سازي نمي کند را اعمال مجرد (abstract operations) گويند کلاسهايي که مجرد نيستند کلاسهاي واقعي (concrete classes) ناميده مي شوند.

زير کلاسها مي توانند رفتار کلاسهاي پدر خود را تصفيه يا از نو تعريف کنند. بطور مشخص، يک کلاس ممکن است يک عمل تعريف شده بوسيله کلاس پدرش را بازنويسي (override) نمايد. بازنويسي کردن به زير کلاسها شانسي براي جوابگويي به درخواستها بجاي کلاسهاي پدرشان را مي دهد. وراثت اجازه مي دهد کلاسهايي را بسادگي با گسترش کلاسهاي ديگر تعريف کرد. و باعث مي شود تا تعريف فاميلي هايي از اشياء داراي عملکرد وابسته به يکديگر را آسان ساخت.

نام کلاسهاي مجرد بصورت حروف کج ظاهر شده تا آنها را از کلاسهاي واقعي تميز دهيم. حروف کج همچنين براي مشخص کردن اعمال مجرد بکار مي رود. يک دياگرام ممکن است شبه برنامه براي پياده سازي يک عمل را شامل باشد که در اين صورت چنين کدي در يک جعبه مستطيلي با گوشه قلابدار که با خط چين به عملي که پياده سازي مي کند متصل مي شود، نمايش داده مي شود.

يک کلاس mixin کلاسي است که به منظور تدارک اينترفيس يا عملکرد اختياري براي کلاسهاي ديگر تعبيه شده است. از اين جهت به کلاسهاي مجرد تشابه دارد که براي نمونه سازي شدن بصورت مستقل تعبيه نشده است. کلاسهاي mixin نياز به داشتن وراثت چندتايي (multiple inheritance) دارند



وراثت کلاسي در مقابل وراثت اينترفيسي
درک تفاوت بين کلاس يک شيء و نوع آن اهميت اساسي دارد.

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

االبته رابطه نزديکي بين کلاس و نوع وجود دارد. چونکه يک کلاس اعماليکه يک شيء انجام مي دهد را تعريف مي کند، همچنين نوع شئ را مشخص مي نمايد. وقتي مي گوييم يک شئ نمونه اي از يک کلاس است، لازم مي دارد که آن شئ اينترفيس تعريف شده بوسيله کلاس را تدارک ببيند.

زبانهايي نظير ++C و Eiffel از کلاسها براي تعيين نوع شئ و همچنين نحوه پياده سازي آن استفاده مي کنند. برنامه هاي زبان smalltalk نوع متغيرها را تعريف نمي کنند، در نتيجه کمپايلر چکي در مورد اينکه اشياء انتساب داده شده به متغيرها زير نوعي از نوع آن متغيرها باشند را انجام نمي دهد. در هنگام ارسال يک پيام اينکه کلاس دريافت کننده آنرا پياده سازي مي کند چک مي گردد ولي چکي در مورد اينکه شئ دريافت کننده نمونه اي از کلاس خاصي است در اين زبان صورت نمي پذيرد.

همچنين درک اختلاف بين وراثت کلاسي و وراثت اينترفيسي (يا زيرتايپ سازي) اهميت دارد. وراثت کلاسي پياده سازي يک شئ را براساس پياده سازي شئ ديگري تعريف مي کند. بطور خلاصه مکانيرمي براي اشتراک کد و نحوه نمايش شئ است. در مقابل، وراثت اينترفيسي (يا زيرتايپ سازي) مشخص مي کند که چه موقع يک شئ مي تواند بجاي ديگري بکار برده شود.

بسادگي ممکن است اين دو مفهوم را باهم اشتباه کرد چراکه بسياري از زبانها تفاوت آنها را به وضوح مشخص نمي کنند. در زبانهايي نظير ++C و Eiffel وراثت به معني هردو وراثت اينترفيس و پياده سازي است. روش استاندارد براي وراثت اينترفيس در ++C وراثت از کلاسي است که دراراي توابع مجازي (pure virtual member functions) است. وراثت اينترفيس خالص در ++C مي تواند با وراثت از کلاسهاي مجرد خالص (pure abstract classes) تقريب زده شود. وراثت پياده سازي يا کلاسي خالص مي تواند با وراثت بصورت خصوصي (private inheritance) تقريب زده شود. در smalltalk وراثت تنها به معناي وراثت پياده سازي است. مي توانيد نمونه هايي از هر کلاس را به يک متغير منسوب کنيد ماداميکه چنين نمونه هايي اعماليکه بر روي مقادير متغيير اعمال مي شوند را تدارک ببينند.

اگرچه بسياري از ربانهاي برنامه سازي تفاوتي بين وراثت اينترفيس و پياده سازي تدارک نمي بينند ولي برنامه سازان در عمل چنين جداسازي را انجام مي دهند. برنامه سازان در زبان smalltalk معمولا به زير کلاسها بصورت زير تايپ نگاه مي کنند (گرچه استثنائات شناخته شده اي وجود دارد ]Coo92[). برنامه نويسان زبان ++C اشياء را از طريق نوعهاي تعريف شده در کلاسهاي مجرد دستکاري مي کنند.

بسياري از الگوهاي طراحي به اين تمايز وابسته اند. بعنوان مثال، اشياء در الگوي زنجيره وظايف (chain of responsibility) بايستي نوع مشترکي داشته باشند، ولي اغلب آنها پياده سازي مشترکي ندارند. در الگوي ترکيب (composite) عنصر (component) يک اينترفيس مشترک تعريف کرده و ترکيب (composite) يک پياده سازي مشترک تعريف مي کند. الگوهاي فرمان (command)، نظاره گر (observer)، حالت (state)، و استراتژي اغلب بتوسط کلاسهاي مجرد که در حقيقت اينترفيس هاي خالص هستند پياده سازي مي شوند.

]پارگراف اضافه شده بوسيله مترجم[ زبان Java بطور واضح تفاوت بين اينترفيس و پياده سازي را داراست. در اين زبان مي توان اينترفيس هايي را تعريف نمود و سپس مشخص کرد کلاسهايي آنها را پياده سازي مي کنند. به همين خاطر پياده سازي اکثر الگوهاي طراحي در اين زبان ساده تر از پياده سازي آنها به زبانهايي نظير ++C و smalltalk است. حتي در اين زبان برخي از الگوها بصورت از پيش تعريف شده وجود دارند (مانند Iterator).

برنامه نويسي براي يک اينترفيس و نه يک پياده سازي
وراتث کلاسي تنها مکانيزمي براي گسترش عملکرد يک کاربرد با بکارگيري استاده مجدد از توابع تعريف شده در يک کلاس پدر است. اين مکانيزم اجازه مي دهد تا نوع جديدي از يک شئ براساس يک نوع قديمي را به سرعت تعريف کرد. اين مکانيزم کمک مي کند تا پياده سازي جديدي را تقريبا بصورت مجاني با وراثت آنچه نياز داريد از کلاسي موجود حاصل کنيد. در هرحال استفاده مجدد از يک پياده سازي تنها نصف داستان است. توانايي تعريف فاميلي هايي از اشياء که داراي اينترفيسهاي يکسان (اغلب با وراثت از يک کلاس مجرد) نيز اهميت دارد. چرا؟ به اين دليل که پلي مورفيسم وابسته به آن است.

هنگاميکه وراثت به دقت (بعضي ها مي گويند بدرستي) مورد استفاده قرار گرفت، تمام کلاسهايي که از يک کلاس مجرد مشتق مي شوند داراي اينترفيس مشترکي مي گردند. اين نکته لازم مي دارد که يک زير کلاس مي تواند اعمالي را اضافه يا بازنويسي کند ولي قادر به حذف عملي از کلاس پدر نيست. به اين ترتيب تمام زير کلاسها مي توانند به درخواستهاي تعريف شده در اينترفيس اين کلاس مجرد پاسخ گويند که بنابراين باعث مي شود تمامي آنها زير تايپي از آن کلاس مجرد گردند.

دو مزيت در کارکردن برروي اشياء که براساس اينترفيس تعريف شده در کلاس مجرد تعريف مي شوند، وجود دارد:
1- مشتريها از نوع هاي مشخص اشيايي که استفاده مي کنند بي خبر نگهداشته مي شوند.
2- مشتريها از کلاسهايي که اين اشياء را پياده سازي مي کنند بي خبر نگهداشته مي شوند. مشتريها تنها از کلاس مجردي که اينترفيس را مي سازد آگاهند.

ايندو مزيت بصورت عمده اي وابستگي هاي پياده سازي بين زير سيستم ها را کاهش مي دهد و ما را به اصل زير در طراحي سيستم هاي شئ گراي قابل استفاده مجدد هدايت مي کند:

براي يک اينترفيس برنامه بنويسيد نه براي يک پياده سازي

متغيرهايي که نمونه هايي از کلاسهاي واقعي مشخصي هستند را تعريف نکنيد. در عوض آنها را براساس يک کلاس مجرد تعريف کنيد. خواهيد ديد که اين اصل تم مشترکي در بين الگوهاي طراحي در اين کتاب است.

به هرحال جايي در درون سيستم خود بايستي کلاسهاي واقعي را نمونه سازي کنيد (يعني اينکه يک پياده سازي مشخص را تعيين کنيد)، و اين کاري است که الگوهاي ايجادي (کارخانه مجرد، سازنده متد کارخانه، نمونه و يگانه) اجازه مي دهند انجام دهيد. اين الگوها با مجرد سازي پروسه ساخت اشياء روشهاي متفاوتي براي انتساب يک اينترفيس به پياده سازي اش در زمان نمونه سازي در اختيار قرار مي دهند. اين الگوها اين اطمينان را حاصل مي کنند که سيستم شما براساس اينترفيس ها نوشته شده و نه براساس پياده سازي ها.

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

وراثت در مقابل ترکيب
دو تکنيک متداول براي استفاده مجدد در سيستم هاي شئ گرا وراثت کلاسي و ترکيب اشياء هستند. همانطوريکه شرح داديم وراثت کلاسي اجازه مي دهد تا پياده سازي يک کلاس را براساس ديگري بيان کنيم. استفاده مجدد از طريق ساختن زير کلاسها اغلب به عنوان استفاده مجدد جعبه سفيد (white-box reuse) مورد رجوع قرار مي گيرد. “جعبه سفيد” به قابليت روئيت مربوط است: با وراثت، درون کلاسهاي پدر اغلب بوسيله زير کلاسها روئيت مي شود.

ترکيب اشياء بديلي براي وراثت کلاسي است. در اينجا، عملکرد جديدي بوسيله اسمبل کردن يا ترکيب اشياء براي حصول يک عملکرد پيچيده تر صورت مي پذيرد. اين مکانيزم لازم دارد تا اشيايي که با هم ترکيب مي شوند داراي اينترفيس خوش-تعريف شده اي باشند. اين نوع از استفاده مجدد، استفاده مجدد جعبه سياه (black-box reuse) ناميده مي شود چراکه جزئيات داخلي اشياء قابل روئيت نيستند. اشياء تنها بصورت “جعبه سياه” ظاهر مي شوند.

وراثت و ترکيب هريک داراي مزايا و معايبي هستند. وراثت کلاسي در زمان کمپايل بصورت استاتيک تعريف شده و بطور سرراست مورد استفاده قرار مي گيرد چرا که مستقيما بوسيله زبان برنامه نويسي تدارک ديده مي شود. وراثت کلاسي همچنين تغيير پياده سازي مورد استفاده را تسهيل مي کند. هنگاميکه يک زير کلاس برخي و نه تمام اعمال را بازنويسي مي کند، درصورتيکه اعمال به ارث برده شده اينگونه اعمال بازنويسي شده را صدا زنند آنها را نيز تحت تاثير قرار مي دهد.

ولي وراثت کلاسي داراي معايبي نيز هست. اول اينکه چون وراثت در زمان کمپايل تعريف مي شود نمي توانيد پياده سازي به ارث برده شده از پدر را در زمان اجرا تغيير دهيد. دليل دوم و عموما بدتر اينکه کلاسهاي پدر اغلب قسمتي از نمايش فيزيکي را تعريف مي کنند. چونکه وراثت پياده سازي پدر را براي زير کلاسها افشاء مي سازد، اغلب گفته مي شود که “وراثت کپسول سازي را نقض مي کند” ]SW86[. پياده سازي يک زير کلاس چنان به پياده سازي پدرش بسته مي شود که هر تغييري در پياده سازي پدر زير کلاس را نيز وادار به تغيير مي کند.

وابستگي هاي پياده سازي مي تواند مسائلي را هنگاميکه سعي داريد زير کلاس را مورد استفاده مجدد قرار دهيد ايجاد کند. اگر جنبه اي از پياده سازي به ارث برده شده براي مسئله جديدي مناسب نباشد کلاس پدر بايستي از نوع بازنويسي شده يا با کلاس مناسبتري جايگزين گردد. اينگونه وابستگي قابليت انعطاف و نهايتا قابليت استفاده مجدد را محدود مي سازد. يک درمان براي اين مسئله وراثت تنها از کلاسهاي مجرد است چرا که آنها معمولا پياده سازي کمي يا اصلا هيچ پياده سازي تدارک نمي بينند.

ترکيب اشياء در زمان اجرا از طريق داشتن ارجاعاتي به ديگر اشياء در درون اشياء ديگر تعريف مي شود. ترکيب نياز دارد تا اشياء به اينترفيس يکديگر احترام گذاشته که لازم مي دارد تا اينترفيس ها را بطور دقيقي تعريف کرد بطوريکه به شما اجازه استفاده از يک شئ در بسياري از اشياء ديگر را بدهد.

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

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

اين دلايل ما را به دومين قانون در طراحي شئ گرا هدايت مي کند:

ترکيب اشياء را به وراثت کلاسي ارجحيت دهيد

در حالت ايده آل نيازي به ساخت اجزاء جديد براي حصول قابليت استفاده مجدد نداريد. بايستي بتوانيد تمام عملکردهاي مورد نياز را با اسمبل کردن اجزاء جديد از طريق ترکيب اشياء حاصل کنيد. ولي چنين حالتي کمتر اتفاق مي افتد. چرا که مجموعه اجزاء موجود به قدر کافي در عمل وجود ندارد. قابليت استفاده مجدد از طريق وراثت، ساخت اجزاء جديدي که بتوانند با اجزاء قديمي ترکيب شوند را ساده تر مي کند. بنابراين وراثت و ترکيب اشياء با يکديگر کار مي کنند.

تجربه ما نشان مي دهد که طراحان از وراثت براي حصول قابليت استفاده مجدد بيش از اندازه استفاده مي کنند. در حاليکه طراحي ها اغلب با ترکيب اشياء است که قابل استفاده مجدد تر (و ساده تر) مي شوند. خواهيد ديد که ترکيب اشياء در الگوهاي طراحي به تکرار اعمال مي شوند.

وکالت (Delegation)
وکالت روشي براي اينکه ترکيب اشياء را به اندازه استفاده مجدد از طريق وراتث قدرتمند سازيم است ]lie 86, jz91[. در وکالت دوشئ در پاسخگويي به يک درخواست دخالت دارند: يک شئ دريافت کننده، اعمالش را به وکيل خود محول مي کند. اين مکانيزم مشابه محول کردن درخواستها به کلاس پدر در زير کلاسهاست. ولي در وراثت يک عمل به ارث برده شده همواره مي تواند به شئ دريافت کننده درخواست از طريق this در ++C و self در smalltalk ارجاع کند. براي حصول اثري يکسان با مکانيزم وکالت، دريافت کننده درخواست ارجاعي به خودش را به وکيل رد کرده تا بدينوسيله عمل در وکيل بتواند به دريافت کننده ارجاع کند.

براي مثال به جاي اينکه کلاس window را زيرکلاسي از کلاس rectangle کنيم (چراکه پنجره ها مربع مستطيل هستند)، کلاس window مي تواند رفتار rectangle را با حفظ متغيري حاوي نمونه اي از يک rectangle و وکالت دادن رفتار مخصوص rectangle به آن نمونه، مورد استفاده مجدد قرار دهد. به ديگر سخن به جاي اينکه window يک rectangle باشد، يک rectangle خواهد داشت. در اين حالت window بايستي درخواستها را به rectangle خود بصورت صريح ارسال کند. در حاليکه قبلا چنين اعمالي را به ارث مي برد.

دياگرام زير نشان مي دهد که کلاس window عمل Area خود را به يک نمونه از rectangle وکالت مي دهد.

يک کمان مشخص مي کند که يک کلاس ارجاعي به نمونه اي از کلاس ديگر را در خود نگه مي دارد. ارجاع داراي يک نام اختياري، rectangle در اينجا، است.

مزيت اصلي وکالت اين است که ترکيب رفتار در زمان اجرا و تغيير روشي که اشياء ترکيب مي شوند را ساده مي سازد. پنجره مان مي تواند در زمان اجرا به شکل دايره درآيد بسادگي با جايگزيني نمونه rectangle آن با يک نمونه circle، با فرض اينکه rectangle و circle داراي نوع يکساني هستند.

وکالت داراي عيبي است که با تکنيکهاي ديگري که از طريق ترکيب اشياء نرم افزار ها را قابل انعطاف تر مي سازند، مشترک است: نرم افزارهاي ديناميک و خيلي پارامتردهي شده سخت تر از نرم افزار ايستا فهميده مي شود. همچنين عدم کارايي زمان اجرا مطرح است، ولي عدم کارايي انسانها در درازمدت مهم تر است. وکالت انتخاب مناسبي است وقتيکه باعث ساده سازي طرح بجاي پيچده کردن آن شود. بسادگي نمي توان قواعدي که موقع استفاده از مکانيزم وکالت را نشان دهند مشخص کرد، چرا که موثر بودن آن بستگي به بستر و ميزان تجارب شما با آن دارد. مکانيزم وکالت هنگاميکه به روشهاي خيلي سبک دهي شده بکار گرفته شود داراي بهترين عملکرد است – يعني در الگوهاي استاندارد.

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

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

مکانيزم وکالت نهايت استفاده از ترکيب اشياء است. و نشان مي دهد که همواره مي توانيد وراثت را با ترکيب اشياء بعنوان مکانيزمي براي قابل استفاده مجدد جايگزين کنيد.

وراثت در مقابل انواع پارامتردهي شده
تکنيک ديگري براي استفاده مجدد (که تکنيک شئ گراي محض نيست) استفاده از انواع پارامتردهي شده (parameterized types) است. اين تکنيک همچنين به نامهايي نظير انواع عمومي (generics) (درAda و Eiffel)، و قالبها (templates) (در ++C) شناخته مي شود. اين تکنيک اجازه مي دهد تا نوعي را بدون تعيين تمام انواع ديگري که مورد استفاده قرار مي دهد تعريف کنيد. نوعهاي مشخص نشده بعنوان پارامترهايي در محل استفاده از آن نوع معيين مي شوند. براي مثال يک کلاس ليست مي تواند با نوع عناصري که شامل مي شود پارامتردهي گردد. براي تعريف ليستي از اعداد صحيح، نوع integer را بعنوان پارامتري به اين نوع فراهم مي کنيد. براي ليستي از رشته ها، نوع string را بعنوان پارامتر تدارک مي بينيد. پياده سازي زبان مورد استفاده يک نسخه مناسب را براي هر نوع مشخص شده توليد مي کند.

انواع پارامتردهي شده روش سومي (اضافه بر وراثت کلاسي و ترکيب اشياء) براي ترکيب رفتار در سيستم هاي شئ گرا در اختيار ما قرار مي دهند. بسياري از طراحي ها مي توانند با هريک از اين سه تکنيک پياده سازي شوند. براي پارامتردهي کردن يک الگوريتم مرتب سازي (sorting) بوسيله عملي که براي مقايسه عناصر بکار مي رود، مي توانيم عمل مقايسه را با هر يک از روشهاي زير پياده سازي کنيم:
1- عملي که بوسيله زير کلاسها پياده سازي مي شود (کاربردي از الگوي template method)
2- وظيفه اي براي شئ که به روتين مرتب سازي رد مي شود (strategy)، ويا
3- آرگومنتي از يک قالب در ++C يا يک نوع عمومي در Ada که نام تابعي که براي مقايسه عناصر صدا مي شود را مشخص مي کند.

تفاوتهاي مهمي بين اين تکنيک ها وجود دارد. ترکيب اشياء اجازه مي دهد تا رفتار يک شئ را در زمان اجرا تغيير داد، ولي همچنين نياز به صدا کردن بصورت غير مستقيم (indirection) دارد که مي تواند کارايي کمتري داشته باشد. وراثت اجازه مي دهد تا پياده سازي پيش فرضي (default) براي يک عمل داشته و اجازه دهيم که زير کلاسها آن را بازنويسي کنند. انواع پارامتردهي شده اجازه مي دهند تا انواعي که يک کلاس مي تواند استفاده کند را تغيير دهيد. ولي نه وراثت و نه انواع پارامتردهي شده در زمان اجرا نمي توانند تغيير کنند. اينکه کداميک از اينها بهترين روش است بستگي به طراحي شما و محدوديتهاي پياده سازي دارد.

هيچيک از الگوهاي اين کتاب با انواع پارامتردهي شده وابستگي ندارند، اگرچه آنها را در مواقعي براي تنظيم يک پياده سازي ++C بکار برده ايم.

انواع پارامتردهي شده بطور کلي در زباني نظير smalltalk که چک انواع را در زمان کمپايل ندارند مورد نياز نيست.

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

اختلاف بين تجمع اشياء (object aggregation) و آشنائي (acquaintance) و اينکه چطور بصورت متفاوتي آنها خود را در زمان کمپايل و اجرا بروز مي دهند را در نظر بگيريد. تجمع اشياء لازم دارد تا يک شئ مالک يا مسئول شئ ديگري باشد. عموما گوئيم يک شئ داراي يا جزئي از شئ ديگري است. تجمع اشياء لازم مي دارد که شئ مشمول شده و مالکش داراي زمان حيات يکساني باشند.

آشنائي لازم مي دارد تا يک شئ در مورد ديگري اطلاع داشته باشد. بعضي اوقات "وابستگي" يا "استفاده کردن" ناميده مي شود. اشياء آشنا با يکديگر ممکن است درخواستي براي اعمال يکديگر داشته باشند، ولي آنها مسئول همديگر نيستند. آشنائي رابطه ضعيف تري نسبت به تجمع اشياء بوده و وابستگي کمتري بين اشياء پيشنهاد مي کند.

در دياگرام هاي مان، يک کمان رابطه آشنائي را نشان مي دهد. يک کمان با يک لوزي در پايه آن تجمع اشياء را نشان مي دهد:

بسادگي ممکن است رابطه هاي تجمع و آشنائي را با يکديگر اشتباه کرد چرا که اغلب بصورت مشابهي پياده سازي مي شوند. در smalltalk تمام متغيرها، ارجاعاتي به ديگر اشياء هستند. هيچ تمايزي در اين زبان مابين اين دو رابطه وجود ندارد. در ++C رابطه تجمع مي تواند با تعريف متغيرهاي member که نمونه هاي واقعي هستند پياده سازي شود. ولي متداول تر اين است که آنها را بصورت اشاره گرها يا ارجاعاتي به نمونه ها تعريف کرد. همچنين در اين زبان رابطه آشنايي را مي توان به توسط اشاره گرها يا ارجاعات پياده سازي کرد.

نهايتا روابط تجمع و آشنائي بيشتر بر اساس نيت بجاي مکانيزم مشخص زبان برنامه نويسي تعيين مي شوند. تمايز ميان آنها ممکن است بسختي در ساختار زمان کمپايل ديده شود ولي اين تمايز اهميت دارد. ارتباطات از نوع تجمع به تناوب کمتري در يک سيستم وجود داشته و دائمي تر هستند. درعوض روابط از نوع آشنائي با نتاوب بيشتري ساخته شده و بعضي اوقات تنها براي قسمتي از يک عمل وجود دارند. روابط آشنائي پويا تر نيز هستند که اين باعث مي شود مشکل تر آنها را در کد يک برنامه تشخيص داد.

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

بسياري از الگوهاي طراحي (بطور مشخص آنهائيکه که در محدوده اشياء هستند) بصورت روشن تمايز بين ساختار زمان کمپايل و اجرا را نشان مي دهند. الگوهاي ترکيب و آذين گر بطور مشخص براي ايجاد ساختارهاي زمان اجراي پيچيده مناسب هستند. مشاهده گر شامل ساختار زمان اجرايي است که اغلب به سختي قابل فهم است مگر اينکه آن الگو را بدانيد زنجيره وظايف الگوهاي ارتباطي که وراثت نمي تواند آشکار سازد را نتيجه مي دهد. بطور کلي ساختار زمان اجرا از روي کد بوضوح ديده نمي شود مگر اينکه الگو را درک کنيد.

طراحي براي تغيير
کليد براي به حداکثر رساندن قابليت استفاده پيش بيني نيازمنديهاي جديد و انجام تغييرات در نيازمنديهاي موجود، و در طراحي سيستم بطوريکه بتوانند همگام باچنين تغييراتي رشد کند، قرار دارد.

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

الگوهاي طراحي از اين دوباره کاري پرهيز کرده، با اطمينان ايجاد کردن از اينکه يک سيستم به روشهاي مشخصي مي تواند تغيير کند. هر الگوي طراحي اجازه مي دهد برخي از جنبه هاي ساختار سيستم بدون وابستگي به جنبه هاي ديگر تغيير کرده و بنابراين سيستم را براي تغييرات مشخصي قوي مي سازد.

در زير برخي از دلايل نياز به طراحي دوباره يک سيستم به همراه الگوهاي طراحي که آنها را آدرس دهي مي کنند آورده شده است:

1- ساخت يک شئ با مشخص کردن يک کلاس بصورت واضح. تعيين نام کلاس وقتيکه يک شئ را مي سازيد شما را به يک پياده سازي مشخص به جاي يک اينترفيس متعهد مي کند. اينگونه تعهدات مي تواند تغييرات آتي را پيچيده سازد. براي پرهيز از اين محدوديت اشياء را بصورت غير مستقيم بسازيد.
الگوهاي طراحي: کارخانه مجرد، متد کارخانه و نمونه
2- وابستگي به اعمال مشخص. وقتيکه يک عمل مشخص را تعيين مي کنيد، به يک روش براي پاسخگويي به يک درخواست متعهد مي شويد. با پرهيز از درخواستهاي hard-coded روشي که به يک درخواست پاسخ داده مي شود را مي توانيد بصورت ساده تري تغيير دهيد.
الگوهاي طراحي: زنجيره وظايف، فرمان
3- وابستگي به پلاتفرم سخت افزاري و نرم افزاري – اينترفيس هاي بيروني سيستم عامل و اينترفيس برنامه هاي کاربردي (APIها) در پلاتفرم هاي سخت افزاري و نرم افزاري متفاوت با هم فرق دارند. نرم افزاري که بستگي به پلاتفرم مشخصي دارد براي انتقال به پلاتفرم هاي ديگر مشکلات بيشتري دارد. حتي ممکن است نگهداري از آن بصورت به هنگام براي پلاتفرمي که براي آن نوشته شده مشکل باشد. بنابراين طراحي سيستم به گونه اي که وابستگي هايش به پلاتفرم خاصي محدود باشد اهميت دارد.
الگوهاي طراحي: کارخانه مجرد، پل
4- وابستگي به نمايشات يا پياده سازيهاي اشياء. مشترهائيکه چگونگي نمايش، نحوه ذخيره سازي يا پياده سازي يک شئ را بدانند ممکن است با تغيير دادن در آن شئ نياز داشته باشند که تغيير کنند. مخفي سازي چنين اطلاعاتي از مشتريها باعث چلوگيري از نياز به تغييرات در قسمتهاي زيادي از سيستم با تغيير يک شئ مي گردد.
الگوهاي طراحي: کارخانه مجرد، پل، خاطره، نماينده
5- وابستگي هاي الگوريتمي. الگوريتم ها اغلب در طول توليدشان و استفاده مجدد از آن توسعه داده شده، بهينه سازي شده و يا جايگزين مي شوند. اشيائي که به يک الگوريتم وابسته هستند هنگاميکه آن الگوريتم تغيير داده مي شود بايستي اصلاح شوند. بنابراينالگوريتمهايي که ممکن است تغيير کنند بايستي جدا سازي شوند.
الگوهاي طراحي: سازنده، تکرار کننده، استراتژي، متد الگو، ملاقات کننده
6- اتصال محکم (tight coupling). کلاسهايي که بصورت قوي به يکديگر متصل هستند را بسختي مي توان بصورت مجزا مورد استفاده مجدد قرار داد. اتصال محکم ما را به ساخت سيستم هاي يکپارچه هدايت مي کند که بدون فهم و تغيير در تعداد زيادي از کلاسها نمي تواند يک کلاس را تغيير داده يا حذف کنيد. سيستم يک توده متراکم مي شود که بسختي مي توان آنرا فهميد، انتقال داد و نگهداري کرد.
اتصال ضعيف (loose coupling) احتمال اينکه يک کلاس بخودي خود بتواند مورد استفاده مجدد قرار بگيرد و اينکه يک سيستم بتواند بصورت راحت تري انتقال داده شده ، تصحيح شده و بسط يابد را افزايش مي دهد. الگوهاي طراحي تکنيک هايي نظير اتصال مجرد (abstract coupling) و لايه بندي را براي ترويج سيستم هاي با اتصالات ضعيف مورد استفاده قرار مي دهند.
الگوهاي طراحي: کارخانه مجرد، پل، زنجيره وظايف، فرمان، نماي خارجي، مياندار، مشاهده گر
7- گسترش علمکرد با زير کلاس کردن. ساخت يک شئ جديد با زير کلاس سازي اغلب کار ساده اي نيست. هر کلاس جديد داراي هزينه پياده سازي اضافي است (نظير کد مربوط به finalization ،initialization و غيره). تعريف يک زير کلاس همچنين نياز به فهم عميق کلاس پدر دارد. براي مثال، بازنويسي يک عمل ممکن است نياز به بازنويسي کردن عملکرد ديگري داشته باشد. يک عمل بازنويسي شده ممکن است نياز داشته باشد تا يک عمل به ارث برده شده را صدا کند. و زير کلاس سازي مي تواند به سيستمي با انبوهي از کلاسها رهنمون سازد چرا که ممکن است مجبور باشيد براي حتي يک گسترش ساده تعداد زيادي زير کلاس بسازيد.
ترکيب اشياء بطور عام و مکانيزم وکالت بطور خاص بديلهائي فابل انعطاف براي ترکيب رفتارها تدارک مي بينند. عملکرد جديدي مي تواند با ترکيب اشياء موجود بجاي تعريف زير کلاسهاي جديد از کلاسهاي موجود، اضافه شود.
در عوض استفاده گسترده از ترکيب اشياء مي تواند باعث سخت تر شدن فهم يک طراحي گردد. بسياري از الگوهاي طراحي طرح هايي توليد مي کنند که مي توانيد عملکرد دلخواه جديدي را تنها با تعريف يک زير کلاس و ترکيب نمونه هايي از آن با کلاسهاي موجود حاصل کنيد.
الگوهاي طراحي: پل، زنجيره وظايف، ترکيب، آذين گر، مشاهده گر، استراتژي
8- عدم توانايي در تغيير کلاسها بصورت راحت. بعضي از مواقع نياز به تغيير کلاسي داريد که به آساني نمي توانيد آنرا مورد تغيير قرار دهيد. مثلا ممکن است براي چنين تغييراتي نياز به کد اصل آن داشته باشيد ولي آنرا در دسترس نداريد (مانند وقتيکه از يک لايبراري تجاري استفاده مي کنيد). و يا ممکن است هر تغييري نياز به تغيير در تعداد زيادي از زير کلاسهاي آن داشته باشد. الگوهاي طراحي روشهائي براي تغييرا کلاسها در چنين شرايطي پيشنهاد مي کنند.
الگوهاي طراحي: وفق دهنده، آذين گر، مشاهده گر

اين مثالها منعکس کننده قابليت انعطافي است که الگوهاي طراحي مي توانند به شما در ساخت نرم افزارتان کمک کنند. به چه ميزان چنين قابليت انعطافي حياتي است بسته به نوع نرم افزاري دارد که مي سازيد. اجازه دهيد به نقشي که الگوهاي طراحي در توسعه سه نوع عمده از کلاسهاي نرم افزاردارند نگاهي بيندازيم: برنامه هاي کاربردي، ابزار (toolkits) و فريم ورکها

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

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

ابزار (toolkits)
اغلب يک کاربرد کلاسهايي از يک يا چندين کتابخانه از پيش تعريف شده از کلاسها که toolkits ناميده مي شوند را بکار مي گيرد. يک toolkit مجموعه اي از کلاسهاي مرتبط و قابل استفاده مجدد است که براي تدارک عملکردهاي مفيد و عمومي تعريف شده اند. مثالي از يک toolkit مجموعه از کلاسهايي براي ليست ها، جداول انجمني (associative tables)، استک ها و نظاير آن است. کتابخانه اعمال ورودي-خروجي در ++C مثال ديگري است. Toolkitها يک طراحي خاصي براي کاربرد شما تحميل نکرده بلکه تنها عملکردي که مي تواند به کاربرد تان کمک کند تا کارش را به انجام برساند، فراهم مي سازند. آنها به شما بعنوان يک پياده ساز کمک مي کنند تا از کدگذاري مجدد عملکرد مشترک پرهيز نمائيد. Toolkitها به استفاده مجدد کد تاکيد دارند. آنها معادل شئ گراي کتابخانه هايي از زيرروتين ها هستند.

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

فريم ورکها
يک فريم ورک مجموعه اي از کلاسهاي همکار است که يک طراحي قابل استفاده مجدد براي کلاسي از نرم افزارها مي سازد ]Deu 89, JF88[ . براي مثال يک فريم ورک مي تواند به منظور ساختن ويراستارهاي گرافيکي براي دامنه هاي متفاوت نظير طرح هاي هنري، ترکيبات موسيقي و CAD ايجاد شده باشد ]VL90, Joh92[. فريم ورک ديگري مي تواند به شما در ساخت کمپايلرهايي براي زبانهاي برنامه نويسي متفاوت و ماشين هاي هدف مختلف کمک کند ]JML92[. و هنوز فريم ورک ديگري ممکن است به شما در ساختن کاربردهاي مالي کمک کند ]BE93[. يک فريم ورک را بصورت يک کاربرد خاص با ساخت زير کلاسهايي مربوط به آن کاربرد از روي کلاسهاي مجرد فريم ورک، ايجاد مي کنيد.

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

استفاده مجدد در اين سطح به يک کنترل معکوس بين کاربرد و نرم افزاري که براساس آن پايه گذاري شده مي انجامد. هنگاميکه از يک toolkit استفاده مي کنيد (يا از يک کتابخانه متداول از زير روتين ها)، بدنه اصلي کاربرد را خودتان نوشته و کدي که مورد استفاده مجدد قرار مي دهيد را صدا مي کنيد. در عوض وقتي که از يک فريم ورک استفاده مي کنيد، بدنه اصلي را مورد استفاده مجدد قرار داده و شما کدي که آن بدنه صدا مي کند را مي نويسيد. شما نياز داريد تا اعمال را با نامها و قراردادهاي صدا زدن خاص توليد کنيد اما اين باعث کاهش در ميزان تصميم هايي که بايستي در طي طراحي اتخاذ کنيد مي شود.

نه تنها مي توانيد کاربردها را با سرعت بيشتري توليد کنيد بلکه همچنين اين کاربردها داراي ساختار يکساني نيز مي گردند، ساده تر مورد نگهداري قرار گرفته و با استفاده کنندگانشان سازگار تر هستند. از طرف ديگر قسمتي از آزادي در بکارگيري خلاقيت خود را از دست مي دهيد، چرا که بسياري از تصميمات طراحي براي شما گرفته شده است.

اگر برنامه هاي کاربردي را به سختي مي توان طراحي کرد، وطراحي toolkitها مشکل تر است، آنگاه طراحي فريم ورکها سخت ترين است. طراح يک فريم ورک قمار اينکه معماري طراحي شده براي هر کاربردي در آن دامنه مناسب است را مي پذيرد. هر تغيير عمده اي در طراحي فريم ورک بطور قابل ملاحظه اي از مزايايش مي کاهد، چرا که مزيت اصلي يک فريم ورک براي يک کاربرد معماري است که تعريف مي کند. بنابراين ضرورت دارد تا فريم ورک به گونه اي طراحي شود که تا حد ممکن قابل انعطاف و قابل گسترش باشد.

بيش از اين، چونکه کاربردها در طراحي شان به فريم ورک وابسته اند، بطور مشخص به تغييراتي در اينترفيس فريم ورک حساس مي باشند. همانطوريکه يک فريم ورک رشد مي کند، کاربردها نيز بايستي با آن رشد کنند. اين امر باعث مي شود تا اتصال ضعيف بيشترين اهميت را دارا باشد، چرا که در غير اينصورت تغيير کوچکي در فريم ورک انعکاسات اساسي در برخواهد داشت.

مسائل طراحي که در بالا بحث شدند براي طراحي يک فريم ورک بسيار حياتي اند. فريم ورکي که اين مسائل را با استفاده از الگوهاي طراحي آدرس دهي کرده بسيار محتمل است که به سطح بالاتري از استفاده مجدد از کد و طراحي دست يابد تا فريم ورکي که از چنين الگوهايي استفاده نمي کند. فريم ورک هاي پيشرفته معمولا چندين الگوي طراحي را بکار مي برند. الگوهاي طراحي باعث مي شوند تا معماري فريم ورک براي کاربردهاي متفاوتي بدون طراحي مجدد مناسب باشد. سود اضافي هنگامي حاصل مي شود که فريم ورک با الگوهاي طراحي که استفاده مي کند مستند سازي شود ]BJ94[. اشخاصيکه الگوها را مي شناسند بصيرت به فريم ورک را با سرعت بيشتري حاصل خواهند کرد. حتي اشخاصيکه چنين الگوهايي را نمي شناسند از ساختاري که آنها به مستندات خواهند بخشيد سود خواهند برد. مستندات کامل براي همه انواع نرم افزارها مفيد است، ولي بطور مشخص براي فريم ورک ها از اهميت خاصي برخوردار مي باشد. فريم ورک ها معمولا داراي منحني يادگيري (learning curve) سختي هستند که بايستي قبل از اينکه بتوانند مورد استفاده قرار بگيرند يادگيري انجام شده باشد. گرچه الگوهاي طراحي چنين منحني يادگيري را مسطح نمي سازند ولي مي توانند آنرا با برجسته سازي عناصر کليدي در طراحي فريم ورک ساده تر کنند.

چونکه الگوهاي طراحي و فريم ورک ها داراي تشابهاتي هستند، افراد اغلب چگونگي اختلاف آنها را بسادگي درک نمي کنند.

آنها در سه روش زير با هم اختلاف دارند:
1- الگوهاي طراحي در سطح تجرد بالاتري از فريم ورکها قرار دارند. فريم ورکها مي توانند در کد مجسم گردند در حاليکه تنها مثالهايي از الگوها مي تواند در کد مجسم گردد. يکي از توانائي هاي فريم ورک ها اين است که مي توانند به زبانهاي برنامه سازي نوشته شده و نه تنها مورد مطالعه قرار گيرند بلکه بطور مستقيم مورد اجرا و استفاده مجدد قرار گيرند. در مقابل، الگوهاي طراحي در اين کتاب بايستي هر بار که مي خواهند مورد استفاده قرارگيرند پياده سازي شوند. الگوهاي طراحي همچنين نيت، مزايا و معايب و نتايج يک طرح را مشخص مي کنند.
2- الگوهاي طراحي عناصر معماري کوچکتري نسبت به فريم ورکها هستند. يک فريم ورک شامل چندين الگوي طراحي است ولي برعکس آن صحيح نيست.
3- الگوهاي طراحي نسبت به فريم ورکها کمتر خصوصي سازي شده اند. فريم ورک ها همواره داراي يک دامنه کاربرد مشخص هستند. يک ويراستار گرافيکي ممکن است در يک کاربرد شبيه سازي بکار رود ولي هيچگاه با يک فريم ورک شبيه سازي اشتباه نمي شود. در عوض الگوهاي طراحي در اين کاتالوگ مي توانند تقريبا در هر نوع کاربرد مورد استفاده قرار گيرند. در حاليکه الگوهاي طراحي خاص تر از آنچه در اين کاتالوگ آمده ممکن است تعريف شوند (مثلا الگوهاي طراحي براي سيستم هاي توزيع شده يا برنامه هاي همزمان) ولي حتي چنين الگوهايي نيز معماري کاربرد را آنگونه که فريم ورک ها انجام مي دهند ديکته نمي کنند.

فريم ورکها بطور روزافزون متداول تر و مهم تر مي شوند. آنها روشي براي حصول بيشترين استفاده مجدد در سيستم هاي شئ گرا هستند. برنامه هاي کاربردي شئ گرا با شامل کردن لايه هايي از فريم ورک ها که با هم همکاري مي کنند به اتمام مي رسند. قسمت عمده اي از کد و طراحي برنامه هاي کاربردي يا از فريم ورک گرفته شده يا تحت تاثير آن قرار مي گيرد.
قسمت قبلي : الگوهاي طراحي، قسمت ششم : ادامه فصل اول، بخش 5-1
قسمت بعدي : الگوهاي طراحي، قسمت هشتم : ادامه فصل اول، بخش 7-1