یک راهنمای اولیه برای دیزاین پترن ها

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

دیزاین پترن ها چی هستند ؟

دیزاین پترن‌ها یه راه حل بهینه و قایل استفاده مجدد برای مسایل برنامه نویسی هستند که ما همه روزه باهاش سرکار داریم . دیزاین پترن یه کلاس یا کتابخانه نیست که ما به پروژمون اضافه کنیم , و خیلی بیشتر از اینهاست . یه قالب هست که باید تو شرایط درست به کار بگیریم. به زیان برنامه نویسی هم به کار میگرید اصلا ربط نداره . یه دیزاین پترن خوب باید تو خیلی از زبانهای برنامه نویسی و الیته نه همه قابل استفاده باشه البته به قابلیت‌های زبانتون هم وابسته هست. دیزاین پترن مثل یه شمشیر دو لبه هست , آگه تو جای اشتباهی به کار گرفته بشه ممکنه خیلی مشکلات واستون پیش بیاره ولی در عوض تو جای درست و زمان درست میتونه نجات دهندتون هم باشه.

سه تا دیزاین پترن پایه وجود داره :

۱- structural ( ساختاری )

۲- creational ( اینو نتونستم فارسی چیزی پیدا کنم گفتم بنویسم خلقتی دیدم نمیشه، ساختنی بازم نمیشه , اگه چیزی یافتید بگید جایگزین کنم من فعلا از آفرینشی استفاده میکنم تو ادامه پست)

۳- behavioral ( رفتاری )

structural patterns اصولا بین موجودیت ها (entities) ارتباط برقرار می‌کنند و کارکردنشان با هم را راحت می‌کنند.

Creational patterns یه مکانیزم نمونه سازی ( instantiation mechanisms ) فراهم می‌کنند , ساخت شی هارو (objects) رو به صورتی که برای شرایط مناسب باشه رو راحت میکنند.

Behavioral patterns تو ارتباط برقرار کردن موجودیت‌ها استفاده میشه و انعطاف پذیری و راحتی زیادی هنگام ارتباط بین موجودیت‌ها بهتون میده.

چرا ما باید از اینها استفاده کنیم ؟

دیزاین پترن‌ها بر اساس اصول هستند و یه راه حل خوب و تفکرشده برای مشکلات برنامه نویسی ارایه میدند . خیلی از برنامه نویس‌ها با این مشکلات موجه شده‌اند و یا استفاده از این ‘راه حل‌ها’ مشکلشون رو حل کردند . اگه شما هم با این نوع مشکلات روبرو شدید چرا باید به دنبال کشف راه حل باشید در حالی که یه راه حل ثابت شده براش وجود داره ؟‌ (چرخ رو اختراع نکنیم)

مثال

فرض کنید می‌خواید دو تا کلاس رو که هر کدوم کار مختلفی رو بسته به شرایط انجام می‌دند رو با هم قاطی کنید . این دو تا کلاس تو جاهای مختلف تو سیستم فعلی استفاده شدند، و خیلی سخته که بخواید این دوتا کلاس رو حذف کنید و یه کلاس جدید بنویسید. اگه هم بخواید اینکا رو بکنید باید بعدش شروع کنید همه جاهایی که این کلاس‌ها اضافه شدند رو تست کنید و اصولا این جور تغییرات که به خیلی کامپوننت‌ها وابستس باعث میشه یه سری باگ‌های جدید متولد بشوند . به جای این کارها شما می‌تونید از استراتژی پترن و آداپتر پترن (strategy pattern و adapter pattern) که به راحتی این جور سناریو‌ها رو واستون هندل میکنه استفاده کنید.


خیلی آسونه , نه ؟ حالآ بیاید یه نیگا به استراتژی پترن بندازیم .

استراتژی پترن (Strategy Pattern)

strategyintro

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

تو مثال بالا , استراتژی ما بر اساس مقداری هست که $context میگره و بر اساس اون کلاس نمونش ایجاد میشه (یعنی یک نمونه از کلاس ساخته میشه) . اگه شما class_one رو به context بدین اون از class_one یه نمونه میسازه و برعکس.

strategyفرض کنید دارید یه کلاس می‌نویسید که هم آپدیت و هم ساخت کاربر رو بر عهده داره , این در هر حالت نیاز به یه سری ورودی مانند (name, address, mobile number, …) داره، ولی نسبت به شرایط داره از متدهای مختلفی برای آپدیت و ساخت استفاده میکنه. حالا، شما احتمالا از if-else برای هندل کردن این استفاده می‌کنید، خب اگه الان بخواید از این کلاس تو یه جا دیگه استفاده کنید؟ تو این حالت، شما باید اون if-else رو دوباره کلشو از اول بنویسید، راحت‌تر نمیشد اگه شما فقط شرایطتتون رو مشخص میکردید‌؟

حالا، استراتژی پترن “معمول” شامل اینه که شما الگوریتم هاتون رو به صورت کپسوله شده تو یه کلاس دیگه بنویسید. ولی تو این حالت، یه کلاس دیگه یه کار بیهوده‌ای هست . یادتون باشه شما قرار نیست همیشه قالب رو پیروی کنید. کارهای متفاوت می‌تونه جوابگو باشه اگه که مفهوم کار همون باقی بمونه و مشکل رو حل کنه.

adapterintro

آداپتر پترن یه دیزاین پترن ساختاری هست که به شما اجازه میده که یه کلاس رو با یه اینترفیس تغییر هدف بدید , این کار به سیستم اجازه میده که با فرخوانی یه سری متدهای متفاوت از اون استفاده کنه‌.

این همچنین به شما اجازه تغییر یه سری ورودی هایی که از کلاس کلاینت دریافت میشه رو میده و سازگار با متدهای کلاس آداپته میکنه.

چه جوری میتونم از این استفاده کنم ؟

adapter

 

یه اصطلاح دیگه واسه کلاس آداپتر wrapper هست، که اساسا به شما اجازه میده یه عمل رو با یه کلاس ‘پنهان’ بکنید و این عملیات رو تو شرایط مناسب مجددا استفاده بکنید . یه مثال کلاسیک که میشه واسه این زد زمانی هست که شما یه کلاس دامنه (Domian class) برای جدول کلاسهاتون (table classes) می‌سازید . به جای اینکه برای هر جدول کلاسهاتون نمونه بسازید و یکی یکی متدهاشون رو فراخوانی کنید . شما می تونید همه این متدها رو تو یه متد آداپتر کلاس کپسوله کنید. این هم به شما اجازه میده که هر عملیاتی که می‌خواید انجام بدید و هم جلوگیری می‌کنه از اینکه بخواید کد تکراری بنویسید واسه همون عملیاتی که یه جا دیگه می‌خواید استفاده کنید.

این دو تا پیاده سازی رو مقایسه کنید :

روش بدون آداپتر


اگه ما نیاز داشته باشیم همین کدها رو یه جا دیگه استفاده کنیم باید از اول همشو بنویسیم تا کارهایی که میخوایم انجام بشه.

روش بهتر 


تو این وضعیت ما یه کلاس پوشاننده (wrapper) داریم که یه کلاس دامنه Account خواهد بود:


تو این روش ما یه کلاس Acoount داریم که همیشه می‌تونید ازش برای ساختن اکانت استفاده کنید همچنین می‌تونید کلاسهای دیگر و هم با این کلاس پوشش بدید.

فکتوری متد پترن (Factory Method Pattern)

factoryintro

فکتوری متد پترن یه دیزاین پترن آفرینشی هست که دقیقا کاری میکنه که اسمش میگه، یه کلاس هست که مانند کارخونه ای از نمونه‌های شی کار میکنه.

هدف اصلی این پترن اینه که کپسوله کردن روش آفرینشی هست که باعث میشه چنتا کلاس محدود به یک متد بشوند . با فراهم کردن شرایط و ورودی‌های مناسب برای فکتوری متد , اون میتونه یه شی مناسب رو برای شما برگردونه.

کی میتونم از این استفاده کنم ؟

factory

بهترین زمان برای استفاده از فکتوری متد پترن زمانیه که شما انواع مختلفی از یک موجودیت (entity) رو دارید. بیاین اینجوری در نظر بگیریم که شما یک کلاس دکمه (button) دارید، این کلاس انواع مختلفی داره، مانند ImageButton, InputButton و FlashButton . بر اساس مکان، شما باید از دکمه‌های مختلف استفاده بکنید، اینجا دقیقا همونجاست که شما باید از فکتوری متد پترن استفاده کنید تا دکمه‌ها رو براتون ایجاد کنه.

خوب بیاین اون سه تا کلاس دکمه هامون رو بسازیم :


حالا، بیاید فکتوری کلاسمون رو بسازیم :


حالا تو کدمون اینجوری میتونیم ازش استفاده کنیم :


خروجی باید یه HTML از انواع دکمه های که داریم باشه . با این روش می تونید انواع دکمه ها رو تو هر شرایطی که میخواید استفاده کنید و فقط اون دکمه رو ایجاد کنید نه چیز بیشتر .

دکوریتور پترن (Decorator Pattern)

decoratorintro

 

دکوریتور پترن یه دیزان پترن ساختاری هست که به ما این امکان رو میده که موقع اجرا برای هر کلاس یه سری رفتار جدید یا اضافی رو بسته به شرایط اضافه کنیم .

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

برای پیاده سازی دکوریتور پترن، ما می‌تونیم از مرحله‌های زیر پیروی کنیم:

  1. یه زیرکلاس از نوع “دکوریتور” کلاس از “کلاساصلی” درست کنید.
  2. تو دکریتور کلاستون یه اشاره‌گر (pointer) از کلاس اصلی به عنوان یه فیلد درست کنید.
  3. کلاس اصلی رو به سازنده (constructor) دکوریتور کلاستون پاس بدید تا اشاره‌گر کلاس اصلی تو دکوریتور کلاس تعریف بشه یا میشه گفت مقدار دهی اولیه (initialize) بشه.
  4. تو دکوریتور کلاس همه متدهای کلاس اصلی رو به اشاره گر کلاس اصلی هدایت (redirect) کنید.
  5. و تو دکوریتور کلاس همه اون متدهایی که میخواید تغییر داده بشه رو override کنید. override کردن یعنی نام متدتون تو زیر کلاس همون نام کلاس اصلی هست ولی کارایی که میکنه متفاوت‌تر از کلاس اصلی هست و حتی کلا میتونه یه کار دیگه بکنه.

این مراحل از مراحل تعریف شده در http://wikipedia.org/wiki/Decorator_pattern می باشد.

کی میتونم از این استفاده کنم ؟

decorator

بهترین مکان برای استفاده از دکوریتور پترن زمانیه که شما میخواید یه موجودیت تو شرایط خاص رفتار جدیدی از خودش داشته باشه. بیاین در نظر بگیرم شما یه لینک HTML دارید، یه لینک خروج از سیستم، که می‌خواید تو صفحه جاری یکمی متفاوت عمل کنه. برای این ما از دکوریتور پترن استفاده می‌کنیم.

اولش بیاین “دکوراسیون” های که نیاز خواهیم داشت رو تعریف کنیم.

  • اگه تو صفحه خانه (home page) باشیم و وارد سیستم شده باشیم (logged in) ، میخوایم این لینک توی یه تگ h2 قرار بگیره.
  • اگه تو صفحه های دیگه باشیم و وارد سیستم شده باشیم، میخوایم این لینک توی یه تگ underline قرار بگیره.
  • اگه به سیستم وارد شدیم، میخوایم این لینک توی یه تگ strong قرار بگیره.

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


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


اینجا میتونید ببینید که ما چطوری اگه چنتا دکوریتور پترن نیاز داشته باشیم میتونیم ترکیبشون کنیم و استفاده کنیم . تا زمانی که همه دکوریتور پترن‌ها از __call متد جادویی (magic function)  استفاده می‌کنند ما می تونیم متدهای کلاس اصلی رو فراخوانی کنیم. اگه فرض کنیم ما در حال حاضر تو صفحه خانه هستیم و به سیستم وارد شدیم پس خروجی ما یه چیزی شبیه زیر میشه:

سینگلتون پترن (Singleton Pattern)

singletonintro

سینگلتون دیزاین پترن یه دیزاین پترن آفرینشی هست که باعث میشه همیشه یک نمونه از یک کلاس خاص در زمان اجرا داشته باشید و نه بیشتر و یه نقطه دسترسی سراسری (global) به همون یه نمونه برای ما فراهم میکنه.

این همین طور برای متغییرهایی که از نمونه سینگلتون استفاده می‌کنند یه نقطه “هماهنگی” نیز ایجاد می‌کنه و باعث میشه هر نمونه سینگلتون همیشه برای هر چیزی که اون رو فراخوانی میکنه مثل هم باشه.

کی میتونم از این استفاده بکنم ؟

singleton

اگه ما بخوایم یه نمونه از یه کلاس رو به یه کلاس دیگه پاس کنیم از سینگتون می‌تونید استفاده کنید تا از اینکه نمونه رو با سازنده یا یکی از آرگومان‌ها به کلاس بفرستیم جلوگیری کنیم. فرض کنید یه کلاس نشست (Session) ساختید که کارش شبیه سازی آرایه سراسری _SESSION هست .تا زمانی که نیاز باشه این کلاس فقط یه بار نمونه سازی بشه پس ما می‌تونیم سینگلتون پترن رو پیاده سازی کنیم:


با انجام این کار ما به نمونه کلاس Session مون همه جا حتی تو کلاس‌های دیگه هم میتونیم دسترسی داشته باشیم . داده ما همیشه تو همه فراخوانی‌ها ثابت هست.

نتیجه گیری

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

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

این پست ترجمه شده این پست هست، اگه جاهایی هست که به خاطر ترجمه فکر می‌کنید نا‌مفهوم هست، می‌تونید از پست اصلی مطالعه کنید.

امیر حبیب زاده

علی رغم بسیاری کسایی که تو زمینه وب و برنامه نویسی فعال هستند IT خوندم , طراحی و تولید وب اپلیکیشن با زبان PHP بلدم و قدیما با یه نیمچه فریم‌ورکی که خودم با کمک دوستانم ساخته بودم کد میزدم بعد رفتم سراغ zend دیدم اوهههه کی میخواد اینو یاد بگیره دیگه دست آخر لاراول شد فریم‌ورک مورد علاقم.

همچنین ممکن است دوست داشته باشید ...

۱۳ واکنش

  1. محمد جواد گفت:

    مهندس خیلی ممنون
    خوب و مفید و جامع 🙂

  2. علی.ب گفت:

    سلام ممنون از پست خوبتون
    توی این زمینه کناب فوق العاده Head First Design Patterns رو برای مطالعه ببشتر پیشنهاد میکنم.

  3. فوق العاده بود 🙂 . یه دنیا ممنون

  4. مهدی علیپور گفت:

    خیلی خوب بود امیر. دستت درد نکنه واقعن مفید بود برام

  5. بهنام گفت:

    از این که این همه وقت گذاشتی تا مطلب جامعی را بنویسی ممنونم 🙂

  6. داریوش گفت:

    خیلی خوشحالم که بالاخره یکی شروع کرد این موضوعو 🙂

  7. Hadi گفت:

    امیر جان عالی!
    من Swift کار می کنم اگه میشه مطالب بیشتری در مورد OOA, OOD, Design Pattern بزارید , دمتون گــــــــرم ☺️

  8. حامد گفت:

    ترجمتون خیلی مشکل داره دوست عزیز.

  9. creational -> ساختی
    structural -> ساختاری

  10. محمد گفت:

    ما که نفهمیدیم که شما از آدرس زیر کپی کرده اید و یا آنها از شما کپی …..

    در هر صورت ….
    http://alihossein.ir/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-%D8%AF%DB%8C%D8%B2%D8%A7%DB%8C%D9%86-%D9%BE%D8%AA%D8%B1%D9%86-%D9%87%D8%A7%DB%8C-php/

    • بهروز خضری گفت:

      در مورد این مقاله خاص، به زمان انتشارشون دقت می‌کردید متوجه می‌شدید که این مقاله سال ۹۳ منتشر شده و تاریخ اون مقاله ۲۰۱۶ هست.
      نمی‌گیم که اونا کپی کردن شاید از یک مقاله انگلیسی ترجمه شده باشن.
      به هر حال …

  11. مختار احمدی گفت:

    مرسی عزیزم عالی بود

  12. هدف نت گفت:

    اقا واقعا ممنون و دستتوت درد نکنه بسیار آموزنده و مفید و به زبان ساده بود
    خسته نباشید

  1. ۱۸-اسفند , ۱۳۹۳

    […] مطلب قبلی گفتیم که دیزاین پترن ها به سه دسته تقسیم می شوند و […]

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *