نگاهی به الگوهای طراحی Go4

مقدمهالگوهای طراحی (Go4 (Gangs of Four Design Patterns یک مجموعه متشکل از ۲۳ الگوی طراحی است که این الگوها مبتنی بر کتاب “Design Patterns: Elements of Reusable Object-Oriented Software” می‌باشند.این کتاب، یک کتاب برای معرفی این الگوهاست که توسط ۴ نویسنده‌ی معروف در دنیای نرم‌افزار به نام‌های Erich Gamma، Richard Helm، Ralph Johnson و John Vlissides نوشته شده است.این کتاب برای اولین بار در سال ۱۹۹۴ میلادی منتشر شد و تاکنون یکی از معروف‌ترین و پرفروش‌ترین کتاب‌های الگوهای طراحی در دنیای نرم‌افزار بوده است. همانطور که از نام کتاب هم قابل برداشت است، دلیل نام‌گذاری کتاب به گنگی از ۴ نفر این است که ۴ نویسنده دارد و به این ترتیب نویسندگان اینطور کتاب را نام‌گذاری نموده‌اند.این الگوهای طراحی به سه‌دسته‌ی ایجادی (Creational)، ساختاری (Structural) و رفتاری (Behavioral) تقسیم می‌شوند که هرکدام به شرح زیر هستند.الگوهای ایجادی (Creational)این دسته از الگوهای الگوهایی هستند که به ایجاد یک شی مرتبط هستند. ۵ مورد از الگوهای معرفی‌شده در این کتاب شامل الگوهای ایجادی هستند.الگوهای ساختاری (Structural)این دسته از الگوها شامل الگوهایی هستند که به ساختاری کلاس‌ها مانند وراثت (Inheritance) و ترکیب (Composition) اشاره دارند و ۷ مورد از الگوها به این دسته اختصاص پیدا کرده‌اند.الگوهای رفتاری (Behavioral)این دسته از الگوها الگوهایی هستند که راهکارهایی ارائه می‌دهند تا اشیا بتوانند با یکدیگر تعامل بهتری داشته باشند به ترتیبی که تا حد ممکن بتوانیم coupling و وابستگی بین اشیا را کمتر کنیم و از طرفی کلاس‌ها را به گونه‌ای توسعه دهیم که برای توسعه‌های آتی به راحتی بیشتری بتوان از آنها استفاده کرد.الگوهای ایجادیدر این قسمت به بررسی ۵ دسته از الگوها می‌پردازیم که همگی به صورت الگوهای ایجادی (Creational) در ایجاد اشیا دخیل هستند. در ابتدا، شرح مختصری از این دسته از الگو‌ها ارائه شده است و در ادامه هر الگو را مفصل‌تر بررسی می‌کنیم:Singleton:الگوی singleton مقدار دهی اولیه یک کلاس را محدود می کند تا اطمینان حاصل شود که فقط یک نمونه از کلاس می تواند ایجاد شود.Factory:الگوی کارخانه مسئولیت نمونه سازی یک شی از کلاس به کلاس Factory را بر عهده می گیرد.Abstract Factory:به ما امکان می دهد یک Factory برای کلاس های کارخانه ایجاد کنیم.Builder:ایجاد یک شی گام به گام و روشی برای در نهایت به دست آوردن نمونه شی.Prototype:ایجاد یک نمونه شی جدید از یک نمونه مشابه دیگر به صورت Prototype و سپس اصلاح بر اساس نیازهایی که داریم. حالا به توضیح مفصل‌تری در مورد هریک از این الگوها به صورت زیر می‌پردازیم:الگوی Singletonطرح‌های طراحی Singleton یکی از الگوهای ایجادی است که اطمینان می‌دهد تنها یک نمونه از یک کلاس ایجاد شده و دسترسی global به آن فراهم می‌کند. این الگوی طراحی در مواردی کاربرد دارد که نیاز داریم تعداد نمونه‌های یک کلاس را به یک عدد محدود کنیم، مانند اتصال به پایگاه داده یا ایجاد یک شیء لاگر که در این موارد از این الگو استفاده می‌نماییم.طرح‌های طراحی Singleton با پیروی از چندین اصل کار می‌کنند از جمله:1. تنها یک نمونه از کلاس Singleton باید وجود داشته باشد.2. کلاس Singleton باید یک نقطه دسترسی global به آن نمونه فراهم کند.3. نمونه Singleton باید به صورت lazy مقداردهی اولیه شود، به این معنی که تنها زمانی باید ایجاد شود که برای اولین بار درخواست شود.4. نمونه Singleton باید thread-safe باشد، به این معنی که چندین thread نباید قادر به ایجاد چندین نمونه از کلاس Singleton باشند.در ادامه مثالی از طرح‌های طراحی Singleton در Python آورده شده است:class Singleton: __instance = None def __init__(self): if Singleton.__instance != None: raise Exception(&quotSingleton class can only have one instance&quot) else: Singleton.__instance = self @staticmethod def get_instance(): if Singleton.__instance == None: Singleton() return Singleton.__instanceدر این مثال، کلاس Singleton یک متغیر استاتیک private به نام instance__ دارد که یک نمونه واحد از کلاس را نگه می‌دارد. متد __init__ کلاس بررسی می‌کند که آیا یک نمونه از کلاس وجود دارد یا خیر. اگر وجود داشته باشد، یک Exception پرتاب می‌کند. متد get_instance کلاس نمونه واحد کلاس را برمی‌گرداند. اگر نمونه وجود نداشته باشد، این متد آن را ایجاد می‌کند.برای استفاده از کلاس Singleton، می‌توانیم از متد get_instance استفاده کنیم:s1 = Singleton.get_instance() s2 = Singleton.get_instance() print(s1 == s2) # Trueدر این مثال، s1 و s2 هر دو نمونه‌های کلاس Singleton هستند. به دلیل اینکه متد get_instance همیشه یک نمونه یکسان از کلاس را برمی‌گرداند، s1 و s2 همان شی هستند و خروجی متد print برابر با True است.الگوی Factoryالگوی Factory design یکی از الگوهای طراحی ایجادی است که برای ایجاد شی وابسته به شرایط از یک متد یا تابع جداگانه استفاده می‌کند. در این الگو، یک کلاس انتزاعی به عنوان فابریک ایجادی ساخته می‌شود که مسئول ایجاد شی است. این کلاس فقط نوع شی مورد نظر را دریافت می‌کند و با توجه به آن، شی را به شی مورد نظر تبدیل می‌کند.به طور کلی، الگوی Factory برای جداسازی فرایند ساخت از بخش‌های دیگر برنامه مفید است. این الگو به برنامه‌نویسان امکان می‌دهد تا برنامه‌های پیچیده را با استفاده از شی‌های ساختاری ساده‌تر طراحی کنند و کد را کوتاه‌تر و قابل‌فهم‌تر کنند.برای مثال، فرض کنید که یک برنامه نیاز به ساخت شی‌های مختلفی دارد که هر کدام نیاز به پردازش‌های مختلفی دارند. با استفاده از الگوی Factory، می‌توان یک کلاس فابریک ایجاد کرد که برای هر نوع شی مورد نیاز، یک شی جدید ایجاد کند و پردازش‌های مختلف را برای آن انجام دهد.در زبان پایتون، می‌توان الگوی Factory را با استفاده از توابع یا کلاس‌های Abstract پیاده‌سازی کرد. برای مثال، در کد زیر، یک کلاس فابریک ساده برای ساخت شی‌های مختلف ایجاد شده است:class Animal: def __init__(self, name): self.name = name def speak(self): pass class Dog(Animal): def speak(self): return &quotWoof!&quot class Cat(Animal): def speak(self): return &quotMeow!&quot class AnimalFactory: def create_animal(self, animal_type, name): if animal_type == &quotdog&quot: return Dog(name) elif animal_type == &quotcat&quot: return Cat(name) else: return None factory = AnimalFactory() dog = factory.create_animal(&quotdog&quot, &quotBuddy&quot) cat = factory.create_animal(&quotcat&quot, &quotFluffy&quot) print(dog.speak()) # Output: Woof! print(cat.speak()) # Output: Meow!در این مثال، کلاس Animal به عنوان کلاس پایه برای ساخت شی‌های سگ و گربه استفاده شده است. کلاس AnimalFactory نیز به عنوان کلاس فابریک ایجاد شده است که با دریافت نوع شی مورد نظر، شی مورد نظر را ایجاد می‌کند. در این مثال، با فراخوانی توابع create_animal با پارامترهای مختلف، شی‌های سگ و گربه ساخته شده و متدهای متناسب با آن‌ها صدا زده می‌شوند.الگوی Abstract Factoryالگوی Abstract Factory design یکی از الگوهای طراحی ایجادی است که برای ایجاد مجموعه‌ای از شی‌های مرتبط با یکدیگر استفاده می‌شود. این الگو امکان ایجاد شی‌های مختلفی را با رعایت یک استاندارد مشترک فراهم می‌کند. به عبارت دیگر، الگوی Abstract Factory اجازه می‌دهد تا یک کلاس فابریک ایجاد شود که برای هر نوع شی مورد نیاز، یک شی جدید ایجاد کند و پردازش‌های مختلف را برای آن‌ها انجام دهد.به طور کلی الگوی Abstract Factory برای ساخت برنامه‌های قابل توسعه و قابل تغییر مفید است. این الگو به برنامه‌نویسان امکان می‌دهد تا برنامه‌های پیچیده را با استفاده از شی‌های ساختاری ساده‌تر و با استفاده از استانداردهای مشترک طراحی کنند.برای مثال، فرض کنید که یک برنامه نیاز به ساخت شی‌های گوناگونی دارد که هر کدام نیاز به پردازش‌های مختلفی دارند. با استفاده از الگوی Abstract Factory، می‌توان یک کلاس فابریک ایجاد کرد که برای هر نوع شی مورد نیاز، یک شی جدید ایجاد کند و پردازش‌های مختلف را برای آن‌ها انجام دهد. به عنوان مثال، یک الگوی Abstract Factory برای ساخت شی‌های گوناگونی می‌تواند به کار گرفته شود.در زبان پایتون، می‌توان الگوی Abstract Factory را با استفاده از توابع یا کلاس‌های Abstract پیاده‌سازی کرد. برای مثال، در کد زیر یک کلاس فابریک Abstract برای ساخت دکوراسیون‌های مختلف ایجاد شده است:class AbstractFactory: def create_button(self): pass def create_checkbox(self): pass class WindowsFactory(AbstractFactory): def create_button(self): return WindowsButton() def create_checkbox(self): return WindowsCheckbox() class MacFactory(AbstractFactory): def create_button(self): return MacButton() def create_checkbox(self): return MacCheckbox() class Button: def paint(self): pass class WindowsButton(Button): def paint(self): return &quotRender a button in Windows style&quot class MacButton(Button): def paint(self): return &quotRender a button in Mac style&quot class Checkbox: def paint(self): pass class WindowsCheckbox(Checkbox): def paint(self): return &quotRender a checkbox in Windows style&quot class MacCheckbox(Checkbox): def paint(self): return &quotRender a checkbox in Mac style&quot def client(factory): button = factory.create_button() checkbox = factory.create_checkbox() print(button.paint()) print(checkbox.paint()) client(WindowsFactory()) # Output: Render a button in Windows style, Render a checkbox in Windows style client(MacFactory()) # Output: Render a button in Mac style, Render a checkbox in Mac styleدر این مثال، کلاس AbstractFactory به عنوان کلاس پایه برای ساخت دکوراسیون‌های مختلف استفاده شده است. کلاس WindowsFactory و MacFactory نیز به عنوان کلاس‌های فابریک ایجاد شده‌اند که با دریافت نوع شی مورد نظر، شی مورد نظر را ایجاد می‌کنند. همچنین، کلاس‌های Button و Checkbox نیز به عنوان کلاس‌های پایه برای ساخت دکوراسیون‌ها استفاده شده‌اند. در نهایت، با فراخوانی تابع client با پارامترهای مختلف، دکوراسیون‌های مختلف ساخته شده و متدهای متناسب با آن‌ها صدا زده می‌شوند.الگوی Builderالگوی طراحی Builder یکی از الگوهای طراحی است که برای ساخت شی‌هایی با رفتارهای مختلف و پیچیده مورد استفاده قرار می‌گیرد. در این الگوی طراحی، فرایند ساخت شی از ساختار آن جدا شده است. بدین ترتیب، با تغییر نحوه‌ی ساخت شی ساختار آن تغییر نمی‌کند. برای استفاده از الگوی طراحی Builder، ابتدا یک Builder برای ساخت شی مورد نظر ایجاد می‌شود. سپس یک Director برای کنترل فرایند ساخت شی نیز ایجاد می‌شود. در نهایت، با استفاده از Director، فرایند ساخت شی آغاز می‌شود و پارامترهای مورد نیاز برای ساخت شی به Builder داده می‌شود. سپس Builder شی را ساخته و بازگردانده می‌کند.یک مثال از استفاده از الگوی طراحی Builder در پایتون می‌تواند ساخت یک شی کتاب باشد. در این مثال، ابتدا یک کلاس BookBuilder برای ساخت کتاب ایجاد می‌شود. سپس یک Director برای کنترل فرایند ساخت کتاب نیز ایجاد می‌شود. در این مثال، کتاب شامل عنوان، نویسنده، قیمت و تعداد صفحات است. در نهایت، با استفاده از Director، فرایند ساخت کتاب آغاز می‌شود و پارامترهای مورد نیاز برای ساخت کتاب به Builder داده می‌شود. سپس Builder کتاب را ساخته و بازگردانده می‌کند.کد مثال:class Book: def __init__(self, title, author, price, num_pages): self.title = title self.author = author self.price = price self.num_pages = num_pages class BookBuilder: def __init__(self): self.title = None self.author = None self.price = None self.num_pages = None def set_title(self, title): self.title = title return self def set_author(self, author): self.author = author return self def set_price(self, price): self.price = price return self def set_num_pages(self, num_pages): self.num_pages = num_pages return self def build(self): return Book(self.title, self.author, self.price, self.num_pages) class BookDirector: def __init__(self, builder): self.builder = builder def construct(self, title, author, price, num_pages): return self.builder.set_title(title).set_author(author).set_price(price).set_num_pages(num_pages).build() book_builder = BookBuilder() book_director = BookDirector(book_builder) book = book_director.construct(&quotPython Programming&quot, &quotJohn&quot, 50.0, 300)در این مثال، ابتدا یک کلاس Book برای نمایش کتاب ایجاد می‌شود. سپس کلاس BookBuilder برای ساخت کتاب ایجاد می‌شود که شامل متدهایی است که پارامترهای مورد نیاز برای ساخت کتاب را تعیین می‌کند. سپس کلاس BookDirector برای کنترل فرایند ساخت کتاب ایجاد می‌شود. در نهایت، با استفاده از کلاس BookDirector، فرایند ساخت کتاب شروع می‌شود و مقادیر مورد نیاز برای ساخت کتاب به Builder داده می‌شود. Builder سپس کتاب را ساخته و بازگردانده می‌کند.الگوی Prototypeالگوی طراحی Prototype یک الگوی طراحی ایجادی است که اجازه می دهد با کپی کردن اشیا موجود، اشیا جدیدی را ایجاد کنید. این الگو کمک می کند تا بتوان از ایجاد هزینه برای اشیا جدید جلوگیری کرد و با کپی کردن اشیا موجود و تغییر آنها اشیا جدیدی ایجاد کنیم.الگوی طراحی Prototype جداسازی ایجاد اشیا را از پیاده سازی آنها امکان پذیر می کند و اجازه می دهد با کپی کردن یک نمونه موجود، بتوان نمونه های جدیدی ایجاد کرد. این الگو زمانی مفید است که ایجاد اشیا جدید هزینه‌بر باشد یا زمانی که می خواهیم یک شی جدید با خصوصیات یک شی موجود ایجاد کنیم.در پایتون می توان الگوی طراحی Prototype را با تعریف یک رابط prototype و کلاس های concrete که این رابط را پیاده سازی می کنند پیاده سازی کرد. رابط prototype باید یک متد clone را تعریف کند که یک کپی از شی فعلی را برگرداند. کلاس های concrete باید این متد را با ایجاد یک شی جدید و کپی کردن خصوصیات شی فعلی در شی جدید پیاده سازی کنند.در اینجا یک مثال برای پیاده سازی الگوی طراحی Prototype آمده است.from abc import ABC, abstractmethod class Prototype(ABC): @abstractmethod def clone(self): pass class Book(Prototype): def __init__(self, title, author, pages): self.title = title self.author = author self.pages = pages def clone(self): return Book(self.title, self.author, self.pages) class Bookshelf: def __init__(self): self.books = [] def add_book(self, book): self.books.append(book) def get_books(self): return self.books if __name__ == '__main__': book1 = Book('Design Patterns', 'Gang of Four', 500) book2 = book1.clone() bookshelf = Bookshelf() bookshelf.add_book(book1) bookshelf.add_book(book2) books = bookshelf.get_books() for book in books: print(book.title, book.author, book.pages)الگوهای ساختاری۷ دسته الگوی ساختاری داریم که توضیحات کلی و اختصاری آنها به صورت زیر است و در ادامه، شرح مفصل‌تری از آنها را خواهیم داشت:Adapter:این الگوی ساختاری، به اشیا با رابط‌های ناسازگار و از جنس‌های مختلف، اجازه همکاری و وصل شدن را می‌دهد.Bridge:این الگو به ما امکان می‌دهد که یک کلاس بزرگ یا مجموعه‌ای از کلاس‌های نزدیک به هم را به دو سلسله مراتب مجزا تقسیم کنیم (انتزاع و پیاده سازی) که می‌توانند مستقل از یکدیگر توسعه یابند.Composite:این الگو به ما کمک می‌کند که اشیاء را در ساختارهای درختی ترکیب کنیم و سپس با این ساختارها به گونه‌ای کار کنیم که گویی اشیاء منفرد هستند.Decorator:این الگو به ما کمک می‌کند که به یک سری شیء خاص، رفتارهای جدید منتسب کنیم. این کار از طریق قرار دادن آن اشیاء درون یک wrapper object که آن رفتارها را دارد، انجام می‌شود.Facade:این الگو، یک واسط ساده‌ شده برای ارتباط با یک کتابخانه یا هر مجموعه پیچیده‌ای از کلاس‌ها را تامین می‌کند.Flyweight:در الگوی فلای‌ویت، خصوصیاتی از اشیا که میتوانند به اشتراک گذاشته شوند را جدا میکنیم و سعی میکنیم نمونه‌های کمتری با خصوصیات خاص بسازیم تا حافظه کمتری نیز مصرف کنیم.Proxy:این الگو به ما امکان می‌دهد تا در مواقعی که نیاز به کنترل دسترسی به یک شیء دارید یا نیاز به ایجاد شیء پیچیده‌ای دارید، از یک شیء پروکسی استفاده کنید.در ادامه، این الگوها مفصل‌تر بررسی خواهند شد.الگوی Adapterالگوی طراحی Adapter یکی از الگوهای ساختاری است که کمک می‌کند تا اشیاء با واسط‌ها و رابط‌های متفاوت، بتوانند با هم کار کنند و به یکدیگر اطلاعات و دیتای لازم را منتقل کنند. این کار از طریق یک شیء به نام adapter انجام می‌شود که میانجی بین آن دو شیء نامرتبط است. در واقع، آن شیء به عنوان پلی بین دو رابط ناسازگار عمل می‌کند و رابط یک کلاس را به رابط دیگری که مشتری انتظار دارد تبدیل می‌کند. این الگو به دستیابی به قابلیت همکاری بین کلاس‌ها کمک می‌کند که در غیر این صورت به دلیل رابط‌های ناسازگار آنها امکان‌پذیر نبود.اصول کلیدی این الگو عبارتند از:سازگاری (compatibility): الگوی Adapter سازگاری بین دو رابط نامرتبط یا ناسازگار را با تبدیل رابط یک کلاس به رابط مورد انتظار مشتری تضمین می‌کند.کپسوله‌سازی (encapsulation): آداپتور تبدیل یا ترجمه رابط را محصور می‌کند و مشتری را از پیچیدگی‌های کلاس تطبیق‌شده محافظت می‌کند.مسئولیت منفرد (Single Responsibility): کلاس آداپتور مسئولیت تبدیل واسط را بدون افزودن عملکرد اضافی دارد. به این صورت، این کلاس خاصیت cohesion را هم دارد و فقط یک تخصص و کارایی دارد.اگر بخواهیم برای درک بهتر، مثالی از این الگو را در قالب کد داشته باشیم، می‌توانیم به سناریوی زیر اشاره کنیم. فرض کنید یک کلاس WeatherService موجود داریم که اطلاعات آب و هوا را به واحد فارنهایت ارائه می‌دهد، اما کد مشتری ما انتظار دارد که دما بر حسب سانتیگراد ارائه شود. ما می‌توانیم از الگوی طراحی آداپتور برای ایجاد یک CelsiusWeatherAdapter استفاده کنیم که WeatherService را برای ارائه دما بر حسب سانتیگراد تطبیق می‌دهد. در این مثال، کلاس WeatherService رابط موجود را نشان می‌دهد که دما را به فارنهایت برمی‌گرداند. کلاس CelsiusWeatherAdapter به عنوان یک آداپتور عمل می‌کند و رابط هدف را با بسته‌بندی WeatherService پیاده‌سازی می‌کند. در روش get_temperature دما را از فارنهایت به سلسیوس تبدیل می‌کند.با استفاده از آداپتور، کد سرویس‌گیرنده می‌تواند به طور یکپارچه با نمونه CelsiusWeatherAdapter کار کند، که دما را بر حسب سانتیگراد ارائه می‌کند، حتی اگر WeatherService آن را به فارنهایت برگرداند.class WeatherService: def get_temperature(self): return 75 # Returns temperature in Fahrenheit class CelsiusWeatherAdapter: def __init__(self, weather_service): self.weather_service = weather_service def get_temperature(self): temperature_fahrenheit = self.weather_service.get_temperature() temperature_celsius = (temperature_fahrenheit - 32) * 5 / 9 return temperature_celsius # Usage weather_service = WeatherService() adapter = CelsiusWeatherAdapter(weather_service) print(adapter.get_temperature()) # Output: 23.88888888888889 (Celsius)الگوی Bridgeالگوی طراحی bridge یک الگوی طراحی ساختاری است که یک انتزاع را از اجرای آن جدا می‌کند و به آنها اجازه می‌دهد به طور مستقل متفاوت باشند. این شامل ایجاد دو سلسله مراتب جداگانه، یکی برای انتزاع و دیگری برای پیاده سازی، و اتصال آنها از طریق یک پل است. اصول این الگوی طراحی عبارتند از:انتراع (Abstraction): انتزاع، سطح بالاتری را نشان می‌دهد که رابط کد مشتری را تعریف می‌کند. همچنین، انتراع حاوی ارجاع به شیء پیاده‌سازی است که بتواند در صورت لزوم از آن استفاده کند.پیاده‌سازی (Implementation): اجرای سطح پایین‌تری را نشان می‌دهد که عملکرد مشخصی را برای انتزاع فراهم می‌کند و از طریق پل به انتزاع متصل می‌شود.پل: به عنوان رابط بین انتزاع و اجرا عمل می‌کند. پل اجازه می‌دهد تا انتزاع و پیاده‌سازی به طور مستقل متفاوت باشد.برای درک بهتر این الگو در قالب کد، به سناریوی زیر توجه کنید. فرض کنید نمونه‌ای از سلسله مراتب شکل را داریم که در آن انواع مختلفی از اشکال (دایره، مربع) و پیاده‌سازی‌های مختلف رندر (بردار، شطرنجی) داریم. ما می‌توانیم از الگوی طراحی پل برای جدا کردن سلسله مراتب شکل از سلسله مراتب پیاده‌سازی رندر استفاده کنیم.در مثال زیر، کلاس Shape نشان‌دهنده سلسله مراتب انتزاعی و کلاس Renderer نشان‌دهنده سلسله مراتب پیاده‌سازی است. کلاس Shape شامل یک مرجع به یک شی Renderer است و متد ()draw آن فراخوانی می‌شود که رندر را به متد مربوطه در شی Renderer واگذار می‌کند.کلاس‌های دایره و مربع پیاده‌سازی‌ها و نمونه‌های مشخصی از کلاس Shape هستند و از آن ارث‌بری می‌کنند. آنها متد ()draw را نادیده می‌گیرند و متد رندر مناسب را روی شی Renderer فراخوانی می‌کنند.کلاس‌های VectorRenderer و RasterRenderer پیاده‌سازی‌های عینی کلاس Renderer هستند. آنها عملکرد رندر خاصی را برای دایره‌ها و مربع‌ها ارائه می‌دهند.با استفاده از الگوی طراحی پل، به جداسازی بین سلسله مراتب شکل و سلسله مراتب پیاده‌سازی رندر می‌رسیم. ما به راحتی می‌توانیم بدون تغییر کد موجود، شکل‌های جدید یا پیاده‌سازی‌های رندر را اضافه کنیم و به آنها اجازه دهیم به طور مستقل متفاوت باشند:# Abstraction hierarchy class Shape: def __init__(self, renderer): self.renderer = renderer def draw(self): pass class Circle(Shape): def draw(self): return self.renderer.render_circle() class Square(Shape): def draw(self): return self.renderer.render_square() # Implementation hierarchy class Renderer: def render_circle(self): pass def render_square(self): pass class VectorRenderer(Renderer): def render_circle(self): return &quotDrawing a circle in vector format&quot def render_square(self): return &quotDrawing a square in vector format&quot class RasterRenderer(Renderer): def render_circle(self): return &quotDrawing a circle in raster format&quot def render_square(self): return &quotDrawing a square in raster format&quot # Usage vector_renderer = VectorRenderer() raster_renderer = RasterRenderer() circle = Circle(vector_renderer) print(circle.draw()) # Output: &quotDrawing a circle in vector format&quot square = Square(raster_renderer) print(square.draw()) # Output: &quotDrawing a square in raster format&quotالگوی Compositeالگوی طراحی composite یک الگوی طراحی ساختاری است که به شما امکان می‌دهد اشیاء را در ساختارهای درختی ترکیب کنید و با اشیاء و ترکیبات تکی اشیاء به طور یکنواخت رفتار کنید. این مفهوم روشی را برای نمایش سلسله مراتب جزء از کل ارائه می‌دهد. به عبارت دیگر، الگوی ترکیبی به شما کمک می‌کند که ساختارهای درخت‌مانندی ایجاد کنید که در آن با اشیاء منفرد (برگ‌ها) و ترکیبات اشیاء (کامپوزیت‌ها) به روشی یکنواخت رفتار می‌شوند. مشتری‌ها می‌توانند با استفاده از رابط Component با اشیاء کار کنند بدون اینکه بدانند آیا با یک شی یا ترکیبی از اشیا سروکار دارند. این موضوع، کد مشتری را ساده می‌کند و کار با ساختارهای پیچیده را انعطاف‌پذیرتر می‌کند و به آنها اجازه می‌دهد تا با اشیاء یا گروه‌هایی از اشیاء به روشی ثابت کار کنند.اصول الگوی طراحی ترکیبی، به شرح زیر هستند:مؤلفه (Component): این رابط یا کلاس پایه، انتزاعی است که متدهای رایج را هم برای اشیاء مرکب و هم برای اشیاء برگ (منفرد) تعریف می‌کند. این کلاس پایه، واسطی را برای اشیاء در ترکیب اعلام می‌کند و پیاده‌سازی‌های پیش فرض را برای عملیات مشترک ارائه می‌دهد.برگ (Leaf): این نشان دهنده تک تک اشیاء در ترکیب است. رفتار تعریف شده در رابط Component را پیاده سازی می‌کند.Composite: این مورد، اشیاء ترکیبی را نشان می‌دهد که می توانند اشیاء فرزند داشته باشند. مجموعه ای از مؤلفه‌های فرزند را نگهداری می‌کند و عملیات تعریف شده در رابط مؤلفه را با واگذاری آنها به مؤلفه‌های فرزند خود پیاده‌سازی می‌کند.برای درک بهتر موضوع در قالب پیاده‌سازی و کد، فرض کنید می‌خواهیم یک سلسله مراتب سیستم فایل، متشکل از دایرکتوری‌ها و فایل‌ها را نشان دهیم. ما می‌توانیم از الگوی طراحی ترکیبی برای مدل سازی این سناریو استفاده کنیم. در اینجا یک نمونه پیاده‌سازی در پایتون آورده شده است. در این مثال، رابط FileSystemComponent را به عنوان Component تعریف می‌کنیم. کلاس File، یک شیء برگ و کلاس Directory یک شیء ترکیبی را نشان می‌دهد. کلاس Directory مجموعه‌ای از اجزای فرزند (فرزندان) را نگهداری می‌کند و متد ()display را برای نمایش مجدد محتویات آن پیاده‌سازی می‌کند.بخش استفاده، ایجاد یک سلسله مراتب سیستم فایل را نشان می‌دهد. یک پوشه ریشه ایجاد می‌کنیم و دو زیر شاخه (subdir1 و subdir2) به آن اضافه می‌کنیم. هر زیر شاخه حاوی چند فایل است. وقتی متد ()display را در دایرکتوری ریشه فراخوانی می‌کنیم، کل سلسله مراتب شامل فایل‌ها و زیر شاخه‌ها را نمایش می‌دهد.این مثال نشان می‌دهد که چگونه الگوی ترکیبی به ما کمک می‌کند تا با فایل‌ها و دایرکتوری‌ها به طور یکنواخت رفتار کنیم و مشتریان را قادر می‌سازد تا با آنها به روشی ثابت کار کنند.from abc import ABC, abstractmethod # Component interface class FileSystemComponent(ABC): @abstractmethod def display(self): pass # Leaf class representing a file class File(FileSystemComponent): def __init__(self, name): self.name = name def display(self): print(f&quotFile: {self.name}&quot) # Composite class representing a directory class Directory(FileSystemComponent): def __init__(self, name): self.name = name self.children = [] def add(self, component): self.children.append(component) def remove(self, component): self.children.remove(component) def display(self): print(f&quotDirectory: {self.name}&quot) for child in self.children: child.display() # Usage root = Directory(&quotRoot&quot) subdir1 = Directory(&quotSubdirectory 1&quot) subdir1.add(File(&quotFile 1&quot)) subdir1.add(File(&quotFile 2&quot)) subdir2 = Directory(&quotSubdirectory 2&quot) subdir2.add(File(&quotFile 3&quot)) subdir2.add(File(&quotFile 4&quot)) root.add(subdir1) root.add(subdir2) root.display()الگوی Decoratorالگوی طراحی Decorator یک الگوی طراحی ساختاری است که به شما امکان می‌دهد تا به صورت پویا رفتارها یا مسئولیت‌هایی را به اشیا اضافه کنید، بدون اینکه کد اصلی آنها را تغییر دهید. این الگو، یک جایگزین انعطاف‌پذیر را برای زیر کلاس‌بندی به منظور گسترش عملکرد، ارائه می‌کند.اصول الگوی طراحی دکوراتور، به شرح زیر هستند:Component: این رابط یا کلاس پایه، انتزاعی است که رابط مشترک را برای concrete componentها و دکوراتورها تعریف می‌کند. همچنین، روش‌هایی را که رفتار اصلی را نشان می‌دهند، اعلام می‌کند.Concrete Component: نشان دهنده شیء اصلی است که رفتارهای جدید را می‌توان به صورت پویا به آن اضافه کرد.Decorator: یک کلاس انتزاعی است که رابط Component را پیاده سازی می‌کند و ارجاع به یک شی از نوع Component را حفظ می‌کند. به عنوان یک کلاس پایه برای دکوراتورهای concrete عمل می‌کند و یک رابط مشترک برای همه دکوراتورها فراهم می‌کند.Concrete Decorators: اینها کلاس‌هایی هستند که کلاس Decorator را گسترش می‌دهند و با پیاده‌سازی رابط Component و واگذاری کار واقعی به شی پیچیده شده، رفتار اضافی را به component اضافه می‌کنند.در حقیقت، الگوی Decorator به شما این امکان را می‌دهد که با قرار دادن یک یا چند دکوراتور به یک شی، قابلیت جدیدی به آن اضافه کنید. هر دکوراتور رفتار خود را اضافه می‌کند در حالی که رابط، شی اصلی را دست نخورده نگه می‌دارد.برای درک بهتر الگو در قالب کد، نمونه‌ای از یک ویرایشگر متن را در نظر بگیرید که در آن یک رابط TextComponent داریم که عملکرد اصلی ویرایش متن را نشان می‌دهد. ما می‌خواهیم رفتارهای اضافی مانند بررسی املا، برجسته کردن متن و رمزگذاری را به صورت پویا اضافه کنیم. برای رسیدن به این هدف می‌توانیم از الگوی Decorator استفاده کنیم. در اینجا یک نمونه پیاده سازی در پایتون آورده شده است:from abc import ABC, abstractmethod # Component interface class TextComponent(ABC): @abstractmethod def render(self): pass # Concrete Component class TextEditor(TextComponent): def __init__(self, content): self.content = content def render(self): print(f&quotContent: {self.content}&quot) # Decorator class TextDecorator(TextComponent): def __init__(self, text_component): self.text_component = text_component def render(self): self.text_component.render() # Concrete Decorator: Text Highlighter class TextHighlighter(TextDecorator): def render(self): super().render() self.highlight_text() def highlight_text(self): print(&quotHighlighting text...&quot) # Concrete Decorator: Encryption class Encryption(TextDecorator): def render(self): super().render() self.encrypt_text() def encrypt_text(self): print(&quotEncrypting text...&quot) # Usage text = &quotHello, World!&quot editor = TextEditor(text) decorated_editor = Encryption(TextHighlighter(SpellChecker(editor))) decorated_editor.render()در این مثال، رابط TextComponent نشان دهنده رفتار اصلی ویرایشگر متن است که توسط کلاس TextEditor پیاده‌سازی شده است. کلاس انتزاعی TextDecorator رابط TextComponent را پیاده‌سازی می‌کند و ارجاع به شی پیچیده شده را حفظ می‌کند. دکوراتورهای concrete، یعنی هماندر این کد، TextHighlighter و Encryption، کلاس TextDecorator را گسترش داده و رفتارهای خاص خود را اضافه می‌کنند.در قسمت usage یک نمونه از کلاس TextEditor با متن اولیه ایجاد می‌کنیم. سپس آن را با دکوراتورها به ترتیب خاصی (رمزگذاری، TextHighlighter) احاطه می‌کنیم تا قابلیت‌های بیشتری اضافه کنیم. در نهایت، وقتی متد ()render را در ویرایشگر تزئین شده فراخوانی می‌کنیم، متدهای رندر دکوراتورهای زنجیره را فراخوانی می‌کند و در نتیجه متن با چک کردن املا، متن نمایش داده می‌شود.الگوی Facadeالگوی طراحی نما یک الگوی طراحی ساختاری است که یک رابط ساده برای زیرسیستم پیچیده‌ای از کلاس‌ها فراهم می‌کند و استفاده و درک آن را آسان‌تر می‌کند. در واقع، آن کلاس و رابط، به عنوان یک نقطه ورودی واحد برای دسترسی مشتریان به زیرسیستم عمل می‌کند و درخواست‌های مشتری را به اشیاء مناسب در زیرسیستم محول می‌کند و از مشتری در مقابل پیچیدگی ساختار داخلی زیرسیستم، محافظت می‌کند. همچنین، با ارائه یک رابط یکپارچه که تعاملات با کلاس‌های زیربنایی را در بر می‌گیرد، اتصال آزاد بین مشتریان و زیرسیستم را ترویج می‌کند.اصول الگوی طراحی نما:کپسوله‌سازی (encapsulation): الگوی نما پیچیدگی‌های یک زیرسیستم را در پشت یک رابط واحد و ساده‌شده در بر می‌گیرد.انتراع (Abstraction): یک رابط سطح بالاتر را فراهم می‌کند که جزئیات و پیچیدگی‌های زیر سیستم را انتزاعی می‌کند و یک نمای ساده را به مشتریان ارائه می‌دهد.ساده‌سازی: الگوی نما، استفاده از یک زیرسیستم را با ارائه یک رابط مناسب که تعاملات و پیچیدگی‌ها را در داخل مدیریت می‌کند، ساده می‌کند.برای درک بهتر این الگو در قالب مثال و کد، سناریو زیر را در نظر بگیرید.# Complex subsystem classes class SubsystemA: def operation_a(self): print(&quotSubsystem A: operation A&quot) class SubsystemB: def operation_b(self): print(&quotSubsystem B: operation B&quot) class SubsystemC: def operation_c(self): print(&quotSubsystem C: operation C&quot) # Facade class class Facade: def __init__(self): self.subsystem_a = SubsystemA() self.subsystem_b = SubsystemB() self.subsystem_c = SubsystemC() def operation(self): self.subsystem_a.operation_a() self.subsystem_b.operation_b() self.subsystem_c.operation_c() # Client code facade = Facade() facade.operation()در این مثال، ما یک زیرسیستم پیچیده داریم که از سه کلاس و زیرسیستم SubsystemA ،SubsystemB و SubsystemC تشکیل شده است. هر کلاس عملیات خاص خود را دارد. کلاس Facade به عنوان یک رابط ساده شده برای زیرسیستم‌ها عمل می‌کند. نمونه‌هایی از کلاس‌های زیرسیستم را مقداردهی اولیه می‌کند و یک متد عملیات واحد را ارائه می‌کند، که به صورت داخلی فراخوانی‌ها را به روش‌های زیرسیستم مناسب واگذار می‌کند.کد مشتری از طریق شیء نما، با زیرسیستم‌ها تعامل دارد. با فراخوانی ()facade.operation، مشتری می‌تواند یک سری عملیات را بدون نیاز به دانستن جزئیات هر کلاس زیرسیستم انجام دهد. الگوی نما پیچیدگی زیرسیستم را پنهان می‌کند و یک رابط ساده برای مشتری فراهم می‌کند.الگوی Flyweightالگوی طراحی Flyweight یکی از الگوهای ساختاری است که در آن از به اشتراک گذاری شیء‌ها (Objects) جهت کاهش مصرف حافظه استفاده می‌شود. در این الگو، اشیاء با استفاده از مکانیسمی مشترک، به صورت کمینه (Lightweight) ساخته می‌شوند و اطلاعات مشترک بین آن‌ها به اشتراک گذاشته می‌شود. این الگو به ما کمک می‌کند تا برای شیء‌هایی که دارای ویژگی‌های مشابه هستند، حافظه را بهینه کنیم و مصرف حافظه را کاهش دهیم.این الگو برخی از اصول SOLID که شامل Single Responsibility Principle ، Open/Closed Principle و Dependency Inversion Principle می‌شوند را به خوبی پشتیبانی میکند.اصل SRP می‌گوید که هر کلاس باید فقط یک مسئولیت را داشته باشد. طرح Flyweight از این اصل پشتیبانی می‌کند زیرا به صورت جداگانه مسئولیت‌های مربوط به ساخت و به اشتراک گذاری اشیاء را بر عهده می‌گیرد. در این طرح، کلاس ShapeFactory مسئول ساخت و به اشتراک گذاری اشیاء است و هر شکل هندسی تنها مسئولیت خودش، مثلاً تعیین رنگ، را بر عهده دارد. این جداسازی مسئولیت‌ها باعث افزایش قابلیت استفاده مجدد و تغییر پذیری کد می‌شود.اصل OCP می‌گوید که باید برنامه را به گونه‌ای طراحی کنیم که برای اضافه کردن ویژگی‌های جدید، نیازی به تغییر در کدهای موجود نباشد. طرح Flyweight از این اصل پشتیبانی می‌کند زیرا با ایجاد اشیاء سبک و به اشتراک گذاری آن‌ها، امکان افزودن شکل‌های هندسی جدید بدون نیاز به تغییر در کد موجود وجود دارد. به عبارتی، این طرح امکان توسعه برنامه را با حفظ ساختار موجود فراهم می‌کند.اصل DIP می‌گوید که وابستگی‌ها باید به ابتدا بر عکس باشند. طرح Flyweight از این اصل پشتیبانی می‌کند زیرا با ایجاد شکل‌های هندسی به صورت سبک و به اشتراک گذاشته شده، به جای وابستگی به شکل‌های هندسی خاص، به سمت وابستگی به شکل‌های هندسی اصلی حرکت می‌کند. این وابستگی برعکس به کاهش وابستگی‌های برنامه کمک می‌کند و امکان جابجایی بین شکل‌های هندسی مختلف را فراهم می‌کند.به این ترتیب، طرح طراحی Flyweight با پشتیبانی از اصول SRP، OCP و DIP، به معماری اصولی و منطقی برنامه کمک می‌کند و کد قابل تغییر، قابل استفاده مجدد و قابل نگهداری را فراهم می‌کند. فرض کنید شما یک برنامه برای ساخت شکل‌های هندسی مانند دایره و مستطیل دارید. با استفاده از الگوی Flyweight، می‌توانیم اشیاءی که دارای ویژگی‌های مشابه هستند را با استفاده از شیء‌های خفیف به اشتراک بگذاریم.import random # Shape Class class Shape: def draw(self): pass # Circle Class class Circle(Shape): def __init__(self, color): self.color = color def draw(self): print(f&quotDraw a circle with color {self.color}&quot) # Shape Factory Class class ShapeFactory: #Cancel the last instances circles = {} @staticmethod def get_circle(color): circle = ShapeFactory.circles.get(color) if circle is None: circle = Circle(color) ShapeFactory.circles[color] = circle print(f&quotMake a new Circle with color {color}&quot) return circle # Using the shape factory class colors = [&quotred&quot, &quotblue&quot, &quotgreen&quot, &quotyellow&quot] for _ in range(10): color = random.choice(colors) circle = ShapeFactory.get_circle(color) circle.draw()در این مثال، کلاس Shape کلاس پایه‌ای است که تمام اشکال هندسی آن را پیاده‌سازی می‌کنند. کلاس Circle یکی از اشکال هندسی است که به عنوان یک شیء خفیف ساخته می‌شود و دارای ویژگی رنگ است. کلاس ShapeFactory یک کلاس فاکتوری است که شیء‌های دایره را به اشتراک می‌گذارد. با استفاده از این کلاس، هر بار که یک دایره با رنگ مشخصی ساخته می‌شود، ابتدا چک می‌شود که آیا قبلاً یک دایره با همین رنگ ساخته شده است یا خیر، و در صورت نیاز، یک دایره جدید ایجاد می‌شود.با استفاده از الگوی Flyweight، در این مثال، شیء‌های دایره با رنگ‌های مشابه به اشتراک گذاشته می‌شوند، به این ترتیب مصرف حافظه کاهش می‌یابد.الگوی Proxyالگوی طراحی Proxy یک الگوی ساختاری است که در آن یک کلاس میانجی (Proxy) بین یک کلاس و کاربر آن قرار می‌گیرد. این کلاس میانجی سعی می‌کند تا دسترسی به کلاس اصلی را کنترل کند و عملیات‌هایی مانند محافظت از کلاس اصلی، اضافه کردن عملیات‌های اضافی و یا انجام عملیات‌های پیش‌پردازش را ارائه دهد. این الگو به ما کمک می‌کند تا از کلاس اصلی جدا شویم و قابلیت اضافه کردن قابلیت‌های جدید را داشته باشیم بدون آنکه در کلاس اصلی تغییری ایجاد کنیم.برخی اصول از SOLID که الگوی پروکسی از آن پشتیبانی می‌کند به شرح زیر است:اصل مسئولیت تکی (Single Responsibility Principle - SRP):الگوی Proxy اجازه می‌دهد که مسئولیت‌های مربوط به کنترل دسترسی به یک کلاس را از آن جدا کند. با استفاده از کلاس Proxy، ما می‌توانیم عملیات‌های مربوط به کنترل دسترسی را به جای کلاس اصلی به کلاس Proxy منتقل کنیم.اصل بازبینی معکوس (Dependency Inversion Principle - DIP):الگوی Proxy با ایجاد یک کلاس میانجی (Proxy)، به ما امکان می‌دهد که وابستگی‌ها را به کلاس اصلی (سرویس) کاهش دهیم. به عبارت دیگر، کلاس Proxy به جای وابستگی به کلاس اصلی، به طور معکوس به کلاس اصلی وابسته است و کلاس اصلی هیچ وابستگی به کلاس Proxy ندارد.اصل جانشین‌پذیری لیسکوف (Liskov Substitution Principle - LSP):با استفاده از الگوی Proxy، ما می‌توانیم کلاس Proxy را به جای کلاس اصلی (سرویس) در برنامه استفاده کنیم بدون اینکه عملکرد برنامه تغییر کند. به عبارت دیگر، کلاس Proxy باید قابلیت جانشین‌پذیری با کلاس اصلی را داشته باشد و بتواند به جای آن استفاده شود.به طور خلاصه، الگوی طراحی Proxy با جداسازی مسئولیت‌های کنترل دسترسی و ایجاد یک کلاس میانجی، اصول SRP، DIP و LSP را پشتیبانی می‌کند. این الگو به ما امکان می‌دهد که کلاس اصلی را از کلاس میانجی جدا کنیم و قابلیت اضافه کردن قابلیت‌های جدید را داشته باشیم بدون تغییر در کلاس اصلی. مثال زیر را در نظر بگیرید:فرض کنید شما یک کلاس سرویس را دارید که عملیاتی را انجام می‌دهد. ما می‌توانیم با استفاده از الگوی Proxy، یک کلاس Proxy برای کنترل دسترسی به این کلاس سرویس ایجاد کنیم.# Main Service Class class Service: def operation(self): print(&quotThe process is executing&quot) # Proxy Class class Proxy: def __init__(self): self.service = Service() def operation(self): # We can do preprocess before the execution print(&quotPreprocess executing&quot) # Executing the main process self.service.operation() # Post Process after execution print(&quotPostprocess executing&quot) # Using the proxy class proxy = Proxy() proxy.operation()در این مثال، کلاس Service کلاس اصلی است که عملیات خاصی را انجام می‌دهد. کلاس Proxy کلاس میانجی است که قبل و بعد از اجرای عملیات اصلی، عملیات پیش‌پردازش و پس‌پردازش را انجام می‌دهد. با استفاده از کلاس Proxy، می‌توانیم دسترسی به کلاس Service را کنترل کنیم و عملیات‌های اضافی را انجام دهیم.الگوهای رفتاری۱۰ دسته الگوی رفتاری داریم که توضیحات کلی و اختصاری آنها به صورت زیر است:Chain of Responsibility:در این الگو، هر شیء در زنجیره یک متدی به نام "پردازش درخواست" دارد که درخواست را دریافت می‌کند و بررسی می‌کند که آیا می‌تواند آن را پردازش کند یا نه. اگر بتواند درخواست را پردازش کند، عملیات مربوطه را انجام می‌دهد و در غیر این صورت درخواست را به شیء بعدی در زنجیره انتقال می‌دهد.Command:با استفاده از الگوی کامند، می‌توانید عملیات‌ها را به صورت مجزا مدل‌سازی کنید و در صورت نیاز، آنها را ذخیره و به ترتیب اجرا کنید. همچنین، می‌توانید عملیات‌ها را به صورت پارامتریک کنترل کنید و به عنوان یک شیء قابل استفاده کنید.Iterator:الگوی Iterator در نرم‌افزارها برای دسترسی به عناصر یک مجموعه از اشیا به صورت متوالی و بدون نیاز به اطلاع از داخلیات مجموعه مورد استفاده قرار می‌گیرد.Mediator:الگوی Mediator در نرم‌افزارها برای کاهش ارتباطات مستقیم بین اجزا و تسهیل ارتباط و تعامل میان آن‌ها از طریق یک شیء واسط (Mediator) استفاده می‌شود.Memento:الگوی Memento در نرم‌افزارها برای ذخیره و بازیابی وضعیت یک شیء بدون نیاز به افشای جزئیات داخلی آن استفاده می‌شود.Observer:الگوی Observer در نرم‌افزارها برای ایجاد رابطه ارتباطی یک به چند بین اشیا استفاده می‌شود.State:الگوی State در نرم‌افزارها جهت ایجاد رفتارهای گوناگون برای یک شیء بر اساس حالتی که در آن قرار دارد استفاده می‌شود.Strategy:الگوی Strategy این امکان را به ما می‌دهد که خانواده‌ای از الگوریتم‌ها را تعریف کنیم، هر یک از آن‌ها را در یک کلاس جداگانه قرار دهیم و با استفاده از این کلاس‌ها اشیایی قابل تعویض با یک‌دیگر ایجاد کنیم.Template Method:الگوی Template Method یک الگوی طراحی رفتاری است که اسکلت یک الگوریتم را در سوپرکلاس تعریف می‌کند، اما به زیر کلاس‌های مربوط به آن اجازه می‌دهد تا مراحل خاصی از الگوریتم را بدون تغییر ساختار آن override کنند.Visitor:الگوی Visitor یک الگوی طراحی رفتاری است که به شما امکان می دهد الگوریتم ها را از اشیایی که روی آنها کار می کنند جدا کنید.در ادامه، این الگوها به تفصیل توضیح داده شده اند:الگوی Chain of Responsibilityالگوی Chain of Responsibility برای مدیریت درخواست‌ها استفاده می‌شود. در این الگو، یک زنجیره از مفسران (Handler) وجود دارد که هر کدام از آن‌ها مسئولیتی خاص را بر عهده دارند. هر مفسر، درخواست را پردازش می‌کند و در صورتی که بتواند به درخواست پاسخ دهد، پاسخ را ارسال می‌کند. در صورتی که نتواند به درخواست پاسخ دهد، مسئولیت را به مفسر بعدی در زنجیره منتقل می‌کند. این فرایند ادامه می‌یابد تا زمانی که یکی از مفسران قادر به پردازش درخواست باشد یا تا انتهای زنجیره برسیم.فرض کنید در یک برنامه، درخواست‌های مختلفی برای پردازش اعداد وجود دارد. ما می‌خواهیم از الگوی Chain of Responsibility برای پردازش این درخواست‌ها استفاده کنیم.الگوی Chain of Responsibility اصول SOLID را به خوبی پشتیبانی می‌کند. این اصول شامل Single Responsibility Principle، Open/Closed Principle و Liskov Substitution Principle می‌شوند.اصل SRP می‌گوید که هر کلاس باید فقط یک مسئولیت را داشته باشد. در الگوی Chain of Responsibility، هر گام از زنجیره مسئولیت مسئولیت خاص خود را دارد و فقط آن را انجام می‌دهد. به این ترتیب، هر کلاس در این طرح فقط مسئولیت خود را بر عهده دارد و تغییر در یک گام از زنجیره تنها به آن گام اثر می‌گذارد.اصل OCP می‌گوید که کلاس‌ها باید برای توسعه باز باشند و باید بتوانند با افزودن کدهای جدید، بدون تغییر در کدهای موجود، تغییر کنند. الگوی Chain of Responsibility از این اصل پشتیبانی می‌کند زیرا با افزودن یک گام جدید به زنجیره، می‌توان عملکرد سیستم را تغییر داد ولی بدون نیاز به تغییر در کدهای موجود در سایر گام‌ها.اصل LSP می‌گوید که باید بتوانیم یک شیء را با یک شیء دیگر جایگزین کنیم بدون این که عملکرد کل سیستم تغییر کند. الگوی Chain of Responsibility از این اصل پشتیبانی می‌کند زیرا هر گام از زنجیره باید قابلیت جایگزینی با گام دیگری را داشته باشد، به شرطی که هر دو گام به یک نوع از درخواست‌ها پاسخ دهند.به عنوان مثال، در طراحی Chain of Responsibility می‌توانیم یک زنجیره از مدیران را در نظر بگیریم که هر کدام مسئولیت خاص خود را دارند. مثلاً، یک مدیر می‌تواند درخواست‌هایی را مربوط به مرخصی‌ها بپذیرد، در حالی که مدیر دیگر مسئول درخواست‌های مربوط به ترفیعات باشد. به این ترتیب، هر مدیر فقط مسئولیت خود را بر عهده دارد و تغییر در یک مدیر تنها به آن مدیر تأثیر می‌گذارد، بدون تغییر در سایر مدیران.فرض کنید در یک برنامه، درخواست‌های مختلفی برای پردازش اعداد وجود دارد. ما می‌خواهیم از الگوی طراحی Chain of Responsibility برای پردازش این درخواست‌ها استفاده کنیم.class Handler: def __init__(self, successor=None): self.successor = successor def handle_request(self, request): pass class AddHandler(Handler): def handle_request(self, request): if request[0] == 'add': result = request[1] + request[2] print(f&quotAddHandler processed the request. Result: {result}&quot) elif self.successor is not None: self.successor.handle_request(request) class SubtractHandler(Handler): def handle_request(self, request): if request[0] == 'subtract': result = request[1] - request[2] print(f&quotSubtractHandler processed the request. Result: {result}&quot) elif self.successor is not None: self.successor.handle_request(request) # chain of responsibility structure add_handler = AddHandler() subtract_handler = SubtractHandler(add_handler) # requests requests = [('add', 5, 3), ('subtract', 10, 7), ('multiply', 2, 4)] # process of requests for request in requests: subtract_handler.handle_request(request)در این مثال، ما دو مفسر AddHandler و SubtractHandler را ایجاد کرده‌ایم. هر کدام از این مفسران مسئولیت پردازش درخواست‌های خاصی را بر عهده دارند. اگر مفسری نتواند به درخواست پاسخ دهد، مسئولیت را به مفسر بعدی در زنجیره منتقل می‌کند. در نهایت، ما چندین درخواست را به زنجیره می‌فرستیم و مفسران آن‌ها را پردازش می‌کنند. نتیجه اجرای این برنامه به صورت زیر خواهد بود:AddHandler processed the request. Resultالگوی Commandالگوی Command یکی از الگوهای طراحی نرم‌افزاری است که در آن عملیات مشخصی را در قالب یک شیء قرار می‌دهد و آن را برای استفاده در آینده ذخیره می‌کند. این الگو اجازه می‌دهد تا درخواست‌ها را از مبدا جدا کنید و در قالب یک شیء Command، آن‌ها را به صورت پارامتریزه دریافت و در صف اجرا قرار دهید.اصول اصلی الگوی Command عبارتند از:۱. جداسازی سطح بالا از سطح پایین: این طرح اجازه می‌دهد تا درخواست‌های مختلف را از سطح بالای برنامه جدا کنید و به عنوان یک شیء قابل استفاده در سطح پایین استفاده کنید.۲. تعمیم‌پذیری: این طرح به شما امکان می‌دهد تا درخواست‌های جدید را به راحتی به برنامه اضافه کنید بدون اینکه بخش‌های دیگر را تغییر دهید.۳. قابلیت تاریخچه‌گیری: با استفاده از این طرح، می‌توانید درخواست‌های انجام شده را ذخیره کنید و به آن‌ها دسترسی داشته باشید.این الگو برخی اصول SOLID را نیز پشتیبانی میکند که به شرح زیر است.اصل Single Responsibility به معنای تعریف یک مسئولیت واحد برای هر کلاس است. با اعمال این اصل، هر کلاس فقط باید یک مسئولیت خاص را بر عهده داشته باشد و تغییر در مسئولیت یک کلاس، تغییر در کلاس‌های دیگر را نیازمند نکند.در الگوی Command، هر دستور به صورت یک شیء Command نمایش داده می‌شود و وظیفه‌ی اصلی این شیء، اجرای یک عملیات مشخص است. با این حال، این شیء Command هیچ اطلاعاتی درباره‌ی اجزای دیگر برنامه ندارد و فقط مسئول اجرای دستور است. به عبارت دیگر، هر دستور فقط یک مسئولیت خاص را بر عهده دارد و هماهنگی با بخش‌های دیگر برنامه را درگیر نمی‌کند.با استفاده از الگوی Command، می‌توانیم دستورات مختلف را جدا کنیم و هر دستور را به صورت یک شیء Command نمایش دهیم. این امکان را به ما می‌دهد تا دستورات جدید را به راحتی اضافه کنیم بدون اینکه بخش‌های دیگر برنامه را تغییر دهیم. این تفکر جداسازی به ما اجازه می‌دهد تا کدهای مربوط به هر دستور را در یک کلاس مجزا نگهداری کنیم و تغییر در یک دستور تنها تغییراتی را در کلاس مربوطه ایجاد می‌کند.به عنوان مثال، اگر در آینده یک دستور جدید به سیستم اضافه شود، ما می‌توانیم یک کلاس جدید را برای اجرای آن دستور ایجاد کنیم بدون اینکه کدهای موجود را تغییر دهیم. این امر به ما امکان می‌دهد که کدهای موجود را تغییر ندهیم و خطر به وجود آمدن خطاهای ناخواسته را کاهش دهیم. به این ترتیب، طرح طراحی Command با جداسازی مسئولیت‌ها و اجرای هر دستور در یک شیء مجزا، اصل Single Responsibility را رعایت می‌کند.یک مثال ساده از Command design pattern در زبان پایتون می‌تواند مربوط به یک سیستم سفارش غذا باشد. در این مثال، یک شیء Command برای هر سفارش ایجاد می‌شود و سپس در یک لیست ذخیره می‌شود. وقتی کاربر یک سفارش را درخواست می‌دهد، Command مربوطه از لیست خارج شده و اجرا می‌شود. اینجا یک مثال ساده در پایتون از Command design pattern است:# Command interface class Command: def execute(self): pass # Concrete command class OrderCommand(Command): def __init__(self, order): self.order = order def execute(self): self.order.place_order() # Receiver class Order: def place_order(self): print(&quotسفارش قرار داده شد.&quot) # Invoker class Waiter: def __init__(self): self.commands = [] def take_order(self, command): self.commands.append(command) def place_orders(self): for command in self.commands: command.execute() self.commands = [] # Client if __name__ == &quot__main__&quot: order1 = Order() order2 = Order() command1 = OrderCommand(order1) command2 = OrderCommand(order2) waiter = Waiter() waiter.take_order(command1) waiter.take_order(command2) waiter.place_orders()در این مثال، Command یک واسطه است که تابع execute را دارد. Concrete command یک پیاده‌سازی از Command است که یک شیء از کلاس Order را دریافت می‌کند و تابع execute را بر روی آن فراخوانی می‌کند. Receiver کلاس Order است که یک عملیات مشخص را انجام می‌دهد. Invoker کلاس Waiter است که دستورات را دریافت کرده و در لیستی ذخیره می‌کند و در زمان مناسب، تمام دستورات را اجرا می‌کند.الگوی Iteratorهدف این الگو فراهم کردن روشی برای دسترسی به عناصر یک aggregate object به شکل ترتیبی است. اما این دسترسی نباید اجزای aggregate object را به شکل عمومی در اختیار همه قرار دهد. معمولاً این الگو را بر روی collection ها اجرا می‌کنند و در نتیجه آن چهار عمل first, next, isDone & currentItem در اختیار کاربر قرار می‌گیرد.وجود این الگو می‌توان امکان چندین پیمایش همزمان را بر روی object فراهم می‌کند. در صورتی که بدون وجود این الگو حداکثر یک پیمایش در هر لحظه قابل انجام است. همچنین مزیت دیگر این الگو جلوگیری از نقض encapsulation این object از سوی کاربر است. چرا که دیگر نیازی به دسترسی مستقیم به اجزای داخل آن وجود ندارد.در این الگو کاربر به کمک یک واسط که بر روی aggregate object تعریف می‌شود؛ با آن در ارتباط خواهد بود. بنابراین DIP در آن رعایت شده است. اما خود این واسط باید encapsulation را نقض کرده و به عناصر داخلی aggregate object دسترسی پیدا کند. بنابراین از سمت interface ای که به عنوان iterator تعریف شده است؛ encapsulation و DIP نقض می‌شود.در ادامه شبه‌کد مربوط به این الگو آورده شده است.// The collection interface must declare a factory method for // producing iterators. You can declare several methods if there // are different kinds of iteration available in your program. interface SocialNetwork is method createFriendsIterator(profileId):ProfileIterator method createCoworkersIterator(profileId):ProfileIterator // Each concrete collection is coupled to a set of concrete // iterator classes it returns. But the client isn't, since the // signature of these methods returns iterator interfaces. class Facebook implements SocialNetwork is // ... The bulk of the collection's code should go here ... // Iterator creation code. method createFriendsIterator(profileId) is return new FacebookIterator(this, profileId, &quotfriends&quot) method createCoworkersIterator(profileId) is return new FacebookIterator(this, profileId, &quotcoworkers&quot) // The common interface for all iterators. interface ProfileIterator is method getNext():Profile method hasMore():bool // The concrete iterator class. class FacebookIterator implements ProfileIterator is // The iterator needs a reference to the collection that it // traverses. private field facebook: Facebook private field profileId, type: string // An iterator object traverses the collection independently // from other iterators. Therefore it has to store the // iteration state. private field currentPosition private field cache: array of Profile constructor FacebookIterator(facebook, profileId, type) is this.facebook = facebook this.profileId = profileId this.type = type private method lazyInit() is if (cache == null) cache = facebook.socialGraphRequest(profileId, type) // Each concrete iterator class has its own implementation // of the common iterator interface. method getNext() is if (hasMore()) currentPosition++ return cache[currentPosition] method hasMore() is lazyInit() return currentPosition < cache.length // Here is another useful trick: you can pass an iterator to a // client class instead of giving it access to a whole // collection. This way, you don't expose the collection to the // client. // // And there's another benefit: you can change the way the // client works with the collection at runtime by passing it a // different iterator. This is possible because the client code // isn't coupled to concrete iterator classes. class SocialSpammer is method send(iterator: ProfileIterator, message: string) is while (iterator.hasMore()) profile = iterator.getNext() System.sendEmail(profile.getEmail(), message) // The application class configures collections and iterators // and then passes them to the client code. class Application is field network: SocialNetwork field spammer: SocialSpammer method config() is if working with Facebook this.network = new Facebook() if working with LinkedIn this.network = new LinkedIn() this.spammer = new SocialSpammer() method sendSpamToFriends(profile) is iterator = network.createFriendsIterator(profile.getId()) spammer.send(iterator, &quotVery important message&quot) method sendSpamToCoworkers(profile) is iterator = network.createCoworkersIterator(profile.getId()) spammer.send(iterator, &quotVery important message&quot)الگوی Mediatorدر این الگو یک object به عنوان mediator یا میانجی تعریف می‌شود که در آن چگونگی تعاملات یک مجموعه از object ها با یکدیگر encapsulate می‌شود. بنابراین coupling میان این مجموعه از object ها به شدت کاهش می‌یابد؛ زیرا دیگر یه شکل صریح به یکدیگر reference نداده و این کار را بر عهده میانجی می‌گذارند.پیش از اعمال این الگو تغییرات میان این مجموعه از object ها بسیار منتشر شونده بوده و یک bad smell به نام shotgun surgery به شدت احساس می‌شود. اما پس از اعمال الگوی mediator هر یک از object ها به شکل مستقل امکان تغییر و گسترش دارند. به عبارت دیگر این الگو تعاملات را از شکل توزیع شده به شکل مرکزی تبدیل می‌کند.البته از معایب این الگو می‌توان به ایجاد bottleneck, single point of failure, god class شدن object میانجی اشاره کرد.در ادامه شبه‌کد مربوط به این الگو آورده شده است.// The mediator interface declares a method used by components // to notify the mediator about various events. The mediator may // react to these events and pass the execution to other // components. interface Mediator is method notify(sender: Component, event: string) // The concrete mediator class. The intertwined web of // connections between individual components has been untangled // and moved into the mediator. class AuthenticationDialog implements Mediator is private field title: string private field loginOrRegisterChkBx: Checkbox private field loginUsername, loginPassword: Textbox private field registrationUsername, registrationPassword, registrationEmail: Textbox private field okBtn, cancelBtn: Button constructor AuthenticationDialog() is // Create all component objects by passing the current // mediator into their constructors to establish links. // When something happens with a component, it notifies the // mediator. Upon receiving a notification, the mediator may // do something on its own or pass the request to another // component. method notify(sender, event) is if (sender == loginOrRegisterChkBx and event == &quotcheck&quot) if (loginOrRegisterChkBx.checked) title = &quotLog in&quot // 1. Show login form components. // 2. Hide registration form components. else title = &quotRegister&quot // 1. Show registration form components. // 2. Hide login form components if (sender == okBtn && event == &quotclick&quot) if (loginOrRegister.checked) // Try to find a user using login credentials. if (!found) // Show an error message above the login // field. else // 1. Create a user account using data from the // registration fields. // 2. Log that user in. // ... // Components communicate with a mediator using the mediator // interface. Thanks to that, you can use the same components in // other contexts by linking them with different mediator // objects. class Component is field dialog: Mediator constructor Component(dialog) is this.dialog = dialog method click() is dialog.notify(this, &quotclick&quot) method keypress() is dialog.notify(this, &quotkeypress&quot) // Concrete components don't talk to each other. They have only // one communication channel, which is sending notifications to // the mediator. class Button extends Component is // ... class Textbox extends Component is // ... class Checkbox extends Component is method check() is dialog.notify(this, &quotcheck&quot) // ...الگوی Mementoاین الگو برای ذخیره و بازیابی حالت یک object بدون آشکار شدن implementation آن استفاده می‌شود. برای بازیابی یک حالت نیاز به ذخیره آن در مراحل قبلی وجود دارد. در حالی که بسیار از object ها اطلاعات و جزئیات خود را به شکل private نگهداری می‌کنند. بنابراین ذخیره حالت کل سیستم یا اصطلاحاً گرفتن یک snapshot ممکن نخواهد بود. مشکل دیگر این راه تغییر ویژگی‌های object های مختلف است و این مسئله باعث عدم اعتبار history ذخیره شده از سیستم خواهد شد. پس مشکل اصلی اینجا نقض encapsulation است.برای حل این مشکل باید ایجاد snapshot از هر object را به خود او واگذار کرد. اطلاعات مربوط در یک object به نام memento ذخیره شده و سایر افراد از طریق interface با آن تعامل می‌کنند. بنابراین اطلاعات private دیگر منتشر نخواهد شد. در ادامه این object های memento در یک سری object دیگر به نام caretaker ذخیره می‌شوند. هر گاه نیاز به بازیابی یک حالت خاص بود؛ memento ی مربوط به آن حالت به کلاس مورد نظر pass می‌شود تا حالت خود را بازیابی کند.در ادامه شبه‌کد مربوط به این الگو آورده شده است.// The originator holds some important data that may change over // time. It also defines a method for saving its state inside a // memento and another method for restoring the state from it. class Editor is private field text, curX, curY, selectionWidth method setText(text) is this.text = text method setCursor(x, y) is this.curX = x this.curY = y method setSelectionWidth(width) is this.selectionWidth = width // Saves the current state inside a memento. method createSnapshot():Snapshot is // Memento is an immutable object; that's why the // originator passes its state to the memento's // constructor parameters. return new Snapshot(this, text, curX, curY, selectionWidth) // The memento class stores the past state of the editor. class Snapshot is private field editor: Editor private field text, curX, curY, selectionWidth constructor Snapshot(editor, text, curX, curY, selectionWidth) is this.editor = editor this.text = text this.curX = x this.curY = y this.selectionWidth = selectionWidth // At some point, a previous state of the editor can be // restored using a memento object. method restore() is editor.setText(text) editor.setCursor(curX, curY) editor.setSelectionWidth(selectionWidth) // A command object can act as a caretaker. In that case, the // command gets a memento just before it changes the // originator's state. When undo is requested, it restores the // originator's state from a memento. class Command is private field backup: Snapshot method makeBackup() is backup = editor.createSnapshot() method undo() is if (backup != null) backup.restore() // ...الگوی Observerدر این الگو چند object نسبت به تغییرات یک object دیگر علاقه نشان می‌دهند. بنابراین هرگاه وضعیت object مورد علاقه آنها تغییر کرد؛ به آنها اطلاع داده می‌شود. به عبارت دیگر object های مختلف نزد یک object خاص register کرده و در آینده از تغییرات آن مطلع خواهند شد.هدف این الگو کاهش coupling میان این object ها است؛ زیرا که دیگر نیازی نیست که observer object ها همواره برای بررسی تغییر به subject object مراجعه کنند. بلکه خود subject آنها را notified خواهد کرد.در ادامه شبه‌کد مربوط به این الگو آورده شده است.// The base publisher class includes subscription management // code and notification methods. class EventManager is private field listeners: hash map of event types and listeners method subscribe(eventType, listener) is listeners.add(eventType, listener) method unsubscribe(eventType, listener) is listeners.remove(eventType, listener) method notify(eventType, data) is foreach (listener in listeners.of(eventType)) do listener.update(data) // The concrete publisher contains real business logic that's // interesting for some subscribers. We could derive this class // from the base publisher, but that isn't always possible in // real life because the concrete publisher might already be a // subclass. In this case, you can patch the subscription logic // in with composition, as we did here. class Editor is public field events: EventManager private field file: File constructor Editor() is events = new EventManager() // Methods of business logic can notify subscribers about // changes. method openFile(path) is this.file = new File(path) events.notify(&quotopen&quot, file.name) method saveFile() is file.write() events.notify(&quotsave&quot, file.name) // ... // Here's the subscriber interface. If your programming language // supports functional types, you can replace the whole // subscriber hierarchy with a set of functions. interface EventListener is method update(filename) // Concrete subscribers react to updates issued by the publisher // they are attached to. class LoggingListener implements EventListener is private field log: File private field message: string constructor LoggingListener(log_filename, message) is this.log = new File(log_filename) this.message = message method update(filename) is log.write(replace('%s',filename,message)) class EmailAlertsListener implements EventListener is private field email: string private field message: string constructor EmailAlertsListener(email, message) is this.email = email this.message = message method update(filename) is system.email(email, replace('%s',filename,message)) // An application can configure publishers and subscribers at // runtime. class Application is method config() is editor = new Editor() logger = new LoggingListener( &quot/path/to/log.txt&quot, &quotSomeone has opened the file: %s&quot) editor.events.subscribe(&quotopen&quot, logger) emailAlerts = new EmailAlertsListener( &quotadmin@example.com&quot, &quotSomeone has changed the file: %s&quot) editor.events.subscribe(&quotsave&quot, emailAlerts)الگوی stateدر این الگو به یک object اجازه داده می‌شود رفتار خود را با توجه به تغییر وضعیت داخلی خود تغییر دهد. در واقع رفتار این object ها وابسته به حالت یا state آنها است.یکی از راهکارهای موجود، استفاده از subclassing است. اما در این روش درخت توارثی بسیار عمیق شده و تنها برگ‌های آن موجودیت‌های قابل مشاهده در سیستم خواهند بود. همچنین اگر وضعیت یک object تغییر کند؛ باید آن object از بین رفته و یک object از کلاس جدید ساخته شود. مشکلات پیش آمده به دلیل subclassing بر اساس یک عامل متغیر است. برای حل این مشکل از الگوی state استفاده می‌شود که در آن رفتار object به object داخلی مربوط به آن deligate می‌شود. این مسئله تقویت‌کننده یکی از اصول مهندسی نرم‌افزار به نام Composite Reuse Principle یا همان CRP است.پس از استفاده از این الگو تغییر حالت و در نتیجه تغییر رفتار object ها کم عارضه خواهد بود.در ادامه شبه‌کد مربوط به این الگو آورده شده است.// The AudioPlayer class acts as a context. It also maintains a // reference to an instance of one of the state classes that // represents the current state of the audio player. class AudioPlayer is field state: State field UI, volume, playlist, currentSong constructor AudioPlayer() is this.state = new ReadyState(this) // Context delegates handling user input to a state // object. Naturally, the outcome depends on what state // is currently active, since each state can handle the // input differently. UI = new UserInterface() UI.lockButton.(this.clickLock) UI.playButton.(this.clickPlay) UI.nextButton.(this.clickNext) UI.prevButton.(this.clickPrevious) // Other objects must be able to switch the audio player's // active state. method changeState(state: State) is this.state = state // UI methods delegate execution to the active state. method clickLock() is state.clickLock() method clickPlay() is state.clickPlay() method clickNext() is state.clickNext() method clickPrevious() is state.clickPrevious() // A state may call some service methods on the context. method startPlayback() is // ... method stopPlayback() is // ... method nextSong() is // ... method previousSong() is // ... method fastForward(time) is // ... method rewind(time) is // ... // The base state class declares methods that all concrete // states should implement and also provides a backreference to // the context object associated with the state. States can use // the backreference to transition the context to another state. abstract class State is protected field player: AudioPlayer // Context passes itself through the state constructor. This // may help a state fetch some useful context data if it's // needed. constructor State(player) is this.player = player abstract method clickLock() abstract method clickPlay() abstract method clickNext() abstract method clickPrevious() // Concrete states implement various behaviors associated with a // state of the context. class LockedState extends State is // When you unlock a locked player, it may assume one of two // states. method clickLock() is if (player.playing) player.changeState(new PlayingState(player)) else player.changeState(new ReadyState(player)) method clickPlay() is // Locked, so do nothing. method clickNext() is // Locked, so do nothing. method clickPrevious() is // Locked, so do nothing. // They can also trigger state transitions in the context. class ReadyState extends State is method clickLock() is player.changeState(new LockedState(player)) method clickPlay() is player.startPlayback() player.changeState(new PlayingState(player)) method clickNext() is player.nextSong() method clickPrevious() is player.previousSong() class PlayingState extends State is method clickLock() is player.changeState(new LockedState(player)) method clickPlay() is player.stopPlayback() player.changeState(new ReadyState(player)) method clickNext() is if (event.doubleclick) player.nextSong() else player.fastForward(5) method clickPrevious() is if (event.doubleclick) player.previous() else player.rewind(5)الگوی Strategyفرض کنید که درون یک برنامه نرم‌افزاری کلاسی داریم که کار خاصی را به روش‌های مختلفی انجام می‌دهد ( برای مثال عملیات ریاضی مختلف انجام می‌دهد یا این که کلاسی در یک برنامه مسیریابی داریم که مسیریابی را به شیوه‌های گوناگونی انجام می‌دهد). در این صورت الگوی استراتژی پیشنهاد می‌کند که در برنامه نرم‌افزاری موردنظر کلاس توصیف شده را پیدا کرده، هر یک از الگوریتم‌هایی که اجرا می‌کند را استخراج کنیم و هر یک از آن‌ها را در قالب کلاسی جدید تعریف کنیم. در حقیقت با این کار یک کلاس context برای انجام دادن کاری مشخص خواهیم داشت و برای هر یک از الگوریتم‌های مختلف کلاسی مجزا بایستی تشکیل دهیم.برای مثال در برنامه مسیریابی که پیش‌تر مثال زدیم یک کلاس برای مسیریابی بایستی داشته باشیم و تعدادی دیگری کلاس برای اجرای الگوریتم‌های مختلف مسیریابی بایستی طراحی کنیم.در این حالت در کلاس context، که کلاس اصلی ما برای اجرای کاری مشخص است، بایستی فیلدی را برای ارجاع به یکی از کلاس‌های strategy در نظر بگیریم ( توجه داشته باشید که کلاس‌های استراتژی همان کلاس‌هایی هستند که هر کدام وظیفه اجرای الگوریتم خاصی را بر عهده دارند.) در این صورت و با تعبیه کردن ارجاعی در هر شی از جنس context، شی موردنظر انجام کار و الگوریتم را به همان کلاسی که به آن ارجاع داده است می‌سپرد.توجه داشته باشید که در این الگوی مهندسی نرم‌افزار کلاس context اطلاع زیادی در رابطه با کلاس‌های strategy و الگوریتم مناسب برای اجرای خواسته مشتری ندارد. در حقیقت مشتری و کاربر بایستی در ابتدای کار الگوریتم و استراتژی مناسب برای اجرای خواسته خود را به context معرفی کنند. به بیان دیگر کلاس context با تمامی کلاس‌های استراتژی که الگوریتم‌های مختلفی برای اجرای یک کار مشخص را پیاده کرده‌اند، با یک interface واحد ارتباط برقرار می‌کند که تنها یک روش برای راه اندازی الگوریتم محصور شده در استراتژی انتخاب شده می‌باشد. با این کار، عملا کلاس context از کلاس‌ها استراتژی مستقل شده ( زیرا این کلاس با توجه به درخواست کاربر الگوریتم موردنظر را انتخاب نکرده و اطلاعی راجع به آن ندارد) و همین امر سبب می‌شود که در آینده راحت‌تر بتوان کلاس‌های strategy دیگری به برنامه اضافه کرد. هم‌چنین در صورتی که در آینده یکی از الگوریتم‌های تعریف شده در یکی از کلاس‌های استراتژی نیاز به تغییر داشته باشد، این کار مستقل از کلاس context و بدون نیاز به تغییر آن قابل انجام خواهد بود.در تصویر زیر می‌توانید یک شمای کلی از این الگو را در رابطه با یک نرم‌افزار مسیریابی مشاهده کنید.در ادامه شبه کد مربوط به این الگو آورده شده است.الگوی Template methodالگوی Template method پیشنهاد می کند که یک الگوریتم مشخص و خاص را به یک سری مراحل و گام‌های مختلف از هم تقسیم کنید، سپس هر یک از این مراحل و گام‌ها را به متد تبدیل کنید و یک سری فراخوانی به این متدها را در یک template method قرار دهید. توجه داشته باشید که در حین تعریف گام‌ها و مراحل، ممکن است که این مراحل یا انتزاعی باشند یا دارای اجرای پیش فرض باشند. برای استفاده از الگوریتم، مشتری قرار است زیر کلاس خود را ارائه دهد، تمام مراحل انتزاعی را پیاده سازی کند و در صورت نیاز برخی از موارد اختیاری را لغو کند (اما نه بر روی خود template method).برای مثال تصور کنید که در حال ایجاد یک برنامه داده کاوی هستید که اسناد موجود در یک شرکت را تجزیه و تحلیل می‌کند. کاربران اسناد برنامه را در قالب‌های مختلف (PDF، DOC، CSV) به برنامه تحویل داده و برنامه موردنظر سعی می‌کند داده‌های معنی‌داری را از این اسناد در قالب یکسان استخراج کند. توجه داشته باشید که استخراج داده‌هایی با قالب یکسان از تعدادی فایل با فرمت‌های مختلف احتمالا دارای مراحلی یکسان برای همه انواع فایل‌ها می‌باشد. در واقع صرفا تفاوت داده کاوی بر روی فایل‌های مختلف، نحوه خواندن اطلاعات از روی آن‌ها است و پس از آن بسیاری از آن‌ها مراحل یکسانی را بایستی طی کنند.حال بیایید ببینیم که چگونه می‌توانیم از الگوی template method برای پیاده‌سازی این نرم‌افزار استفاده کنیم. برای این کار ما می توانیم یک کلاس پایه برای هر سه الگوریتم تجزیه ایجاد کنیم. این کلاس یک template method را تعریف می‌کند که شامل یک سری فراخوانی مراحل مختلف پردازش سند است.در ابتدا، ما می‌توانیم تمام مراحل را انتزاعی مشخص کنیم و کلاس‌های فرعی را مجبور کنیم تا پیاده‌سازی‌های خود را برای این روش‌ها ارائه کنند. در مورد ما که می‌خواستیم برنامه‌ای برای داده‌کاوی بر روی فایل‌های مختلف ارائه کنیم، کلاس‌های فرعی از قبل همه پیاده‌سازی‌های لازم را دارند، بنابراین تنها کاری که ممکن است نیاز باشد این است که signature متدها را برای مطابقت با متدهای سوپر کلاس تنظیم کنیم.حال می‌خواهیم ببینیم برای خلاص شدن از شر کدهای تکراری چه کاری می‌توانیم انجام دهیم. به نظر می‌رسد کد باز کردن/بستن فایل‌ها و استخراج/تجزیه داده‌ها برای فرمت‌های مختلف داده متفاوت است، بنابراین دست زدن به این متدها فایده‌ای ندارد. با این حال پیاده سازی مراحل دیگر، مانند تجزیه و تحلیل داده‌های خام و نوشتن گزارش، برای انواع مختلف فایل‌ها احتمالا بسیار شبیه به یک‌دیگر خواهد بود بنابراین می توان آن را به کلاس پایه، جایی که کلاس های فرعی می توانند آن کد را به اشتراک بگذارند، بالا برد.همانطور که می بینید، ما دو نوع مرحله داریم:مراحل abstract باید توسط هر زیر کلاس پیاده سازی شود.مراحل اختیاری قبلاً دارای پیاده‌سازی پیش فرض بودند اما همچنان در صورت نیاز می‌توان آنها را نادیده گرفت.نوع دیگری از مراحل به نام hook وجود دارد. Hook یک مرحله اختیاری با بدنه خالی است. یک template method حتی اگر یک hook نادیده گرفته نشود نیز کار می کند.به طور کلی ساختار الگوی template method به شکل زیر می‌باشد. در ادامه شبه کد مربوط به این الگو آورده شده است.الگوی Visitorالگوی visitor پیشنهاد می‌کند که رفتار جدید را به جای تلاش برای ادغام آن در کلاس‌های موجود، در یک کلاس جداگانه به نام visitor قرار دهید. در این صورت Object اصلی که باید این رفتار را انجام دهد، اکنون به عنوان آرگومان به یکی از متدهای visitor منتقل می‌شود و روش دسترسی به تمام داده‌های ضروری موجود در شیء را فراهم می‌سازد.حال ممکن است این سوال به وجود بیاید که ممکن است behavior مورد بحث را بتوان بر روی اشیا از کلاس‌های مختلفی اجرا کرد. در این حالت نحوه پیاده‎‌سازی بایستی به چه گونه‌ای باشد؟ الگوی visitor این مشکل را برطرف می‌کند. این الگور از تکنیکی به نام Double Dispatch استفاده می‌کند که به اجرای method مناسب بر روی یک شی بدون شرط های دست و پا گیر کمک می‌کند. به جای اینکه به کاربر اجازه دهیم نسخه مناسبی از متد را برای فراخوانی انتخاب کند، چطور این انتخاب را به اشیایی که به عنوان آرگومان به بازدیدکننده ارسال می‌کنیم، واگذار کنیم؟ از آنجایی که اشیاء کلاس‌های خود را می‌شناسند، می توانند متد مناسبی را برای visitor انتخاب کنند. آنها یک visitor را "می‌پذیرند" و به او می‌گویند که کدام روش visit باید اجرا شود.در تصویر زیر می‌توانید ساختار کلی این الگو را مشاهده کنید.هم‌چنین در رابطه با این الگو می‌توانید شبه کد زیر را مشاهده نمایید.مراجعhttps://www.digitalocean.com/community/tutorials/gangs-of-four-gof-design-patterns#creational-design-patternshttp://www.blackwasp.co.uk/gofpatterns.aspxhttps://www.dotnettricks.com/learn/designpatterns/singleton-design-pattern-dotnethttps://fa.wikipedia.org/wiki/%D8%A7%D9%84%DA%AF%D9%88%DB%8C_%DB%8C%DA%AF%D8%A7%D9%86%D9%87https://fa.wikipedia.org/wiki/%D8%A7%D9%84%DA%AF%D9%88%DB%8C_%D9%86%D8%A7%D8%B8%D8%B1https://fa.wikipedia.org/wiki/%D8%A7%D9%84%DA%AF%D9%88%DB%8C_%D9%85%DB%8C%D8%A7%D9%86%D8%AC%DB%8Chttps://fa.wikipedia.org/wiki/%D8%A7%D9%84%DA%AF%D9%88%DB%8C_%D8%A2%D8%AF%D8%A7%D9%BE%D8%AA%D9%88%D8%B1https://fa.wikipedia.org/wiki/%D8%A7%D9%84%DA%AF%D9%88%DB%8C_%D8%A2%D8%B0%DB%8C%D9%86%DA%AF%D8%B1