زمان تخمینی مطالعه: 13 دقیقه

الگوهای طراحی Creational دسته‌ای از Design Pattern های نرم افزار هستند که با مکانیسم‌های ایجاد شی سروکار دارند. آنها راه‌ها و تکنیک‌های مختلفی را برای ایجاد اشیاء به روشی ارائه می‌دهند که انعطاف پذیر و قابل استفاده مجدد بوده و بتواند اصول طراحی بهتر را ترویج کند. در ادامه به تشریح متد Factory از این الگو می‌پردازیم.

متد Factory

متد Factory که با نام سازنده مجازی نیز شناخته می‌شود، یک الگوی طراحی ایجادی(Creational) است که یک رابط(Interface) برای ایجاد اشیاء در یک سوپرکلاس فراهم می‌کند، اما به زیر کلاس‌ها اجازه می‌دهد تا نوع اشیایی که ایجاد می‌شوند را تغییر دهند.

Factory Method pattern

بیان مسئله: تصور کنید که در حال ایجاد یک برنامه کامپیوتری برای مدیریت حمل و نقل(Logistics) هستید. اولین نسخه برنامه شما فقط قادر است تا حمل و نقل با کامیون را انجام دهد، بنابراین بخش عمده کد شما در کلاس Truck قرار دارد. پس از مدتی، برنامه شما بسیار محبوب و پر استفاده می‌شود و هر روز ده‌ها درخواست از شرکت‌های حمل و نقل دریایی برای گنجاندن روش حمل و نقل و تدارکات دریایی در برنامه دریافت می کنید.

Adding a new transportation class to the program causes an issue

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

الگوی طراحی Factory Method پیشنهاد می‌کند که فراخوانی‌های ساخت مستقیم شی (با استفاده از اپراتور جدید) را با فراخوانی به روش Factory خاص جایگزین کنید. شاید این موضوع باعث ایجاد نگرانی شود ولی نگران نباشید: اشیاء هنوز هم از طریق اپراتور جدید ایجاد می‌شوند، اما از روش Factory فراخوانی می‌شوند. اشیایی که با روش Factory بازگردانده می‌شوند اغلب به عنوان محصولات شناخته می‌شوند.

The structure of creator classes

در نگاه اول، این تغییر ممکن است بی معنی به نظر برسد. در واقع ما فقط فراخوان سازنده(Constructor) را از یک قسمت برنامه به قسمت دیگر منتقل کردیم. با این حال، این را در نظر بگیرید که اکنون می‌توانید روش Factory را در یک زیر کلاس بازنویسی(Override) کنید و کلاس محصولات ایجاد شده توسط آن متد را تغییر دهید. با این حال یک محدودیت جزئی باقی می‌ماند و آن این است که کلاس‌های فرعی ممکن است انواع مختلفی از محصولات را تنها در صورتی برگردانند که این محصولات دارای یک کلاس پایه یا اینترفیس مشترک باشند. همچنین متد Factory در کلاس پایه باید نوع بازگشتی خود را به عنوان این اینترفیس اعلام کند.

The structure of the products hierarchy

برای مثال، هر دو کلاس Truck و Ship باید اینترفیس Transport را پیاده‌سازی کنند، که متدی به نام deliver را اعلام می‌کند. هر کلاس این روش را به صورتی متفاوت اجرا می‌کند: کامیون‌ها محموله را از طریق زمینی و کشتی‌ها محموله را از طریق دریا تحویل می‌دهند. متد Factory در کلاس RoadLogistics اشیاء کامیون را برمی‌گرداند، در حالی که متد Factory در کلاس SeaLogistics کشتی‌ها را برمی‌گرداند.

The structure of the code after applying the factory method pattern

کدی که از متد Factory استفاده می‌کند (که اغلب کد مشتری(Client Code) نامیده می‌شود) تفاوتی بین محصولات واقعی بازگردانده شده توسط زیر کلاس‌های مختلف نمی‌بیند. مشتری با تمام محصولات به عنوان Transport انتزاعی رفتار می‌کند. مشتری می‌داند که همه اشیاء حمل و نقل قرار است متد deliver را داشته باشند، اما نحوه عملکرد دقیق آن برای مشتری مهم نیست.

شبه کد(Pseudocode)

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

The structure of the Factory Method pattern example

کلاس پایه Dialog از عناصر UI مختلف برای ارائه پنجره خود استفاده می‌کند. در محیط سیستم عامل‌های مختلف، این عناصر ممکن است کمی متفاوت به نظر برسند، اما همچنان باید به طور مداوم رفتار کنند. یک دکمه در ویندوز هنوز یک دکمه در لینوکس است. هنگامی که متد Factory وارد عمل می‌شود، دیگر نیازی به بازنویسی منطق کلاس Dialog برای هر سیستم عامل به طور مجزا ندارید. اگر یک متد Factory را تعریف کنیم که دکمه‌هایی را در داخل کلاس پایه Dialog تولید می‌کند، بعداً می‌توانیم یک کلاس فرعی ایجاد کنیم که دکمه‌های سَبکِ ویندوز را از متد Factory بر می‌گرداند. سپس کلاس‌های فرعی اکثر کدهای خود را از کلاس پایه به ارث می‌برد، اما به لطف متد Factory، می‌تواند دکمه‌های شبیه به ویندوز را روی صفحه نمایش دهد. برای اینکه این الگو به درستی کار کند، کلاس‌ پایه Dialog باید با دکمه‌های انتزاعی کار کند: یک کلاس پایه یا یک اینترفیس که همه دکمه‌های Concrete از آن پیروی می‌کنند. به این ترتیب، کد Dialog با هر نوع دکمه‌ای که کار می‌کند، کاربردی باقی می‌ماند. البته، می‌توانید این رویکرد را در سایر عناصر رابط کاربری نیز اعمال کنید. با این حال، با هر متد Factory جدیدی که به Dialog اضافه می‌کنید، به الگوی Abstract Factory نزدیک می شوید که بعداً در مورد این الگو صحبت خواهیم کرد.

// The creator class declares the factory method that must
// return an object of a product class. The creator's subclasses
// usually provide the implementation of this method.
class Dialog is
    // The creator may also provide some default implementation
    // of the factory method.
    abstract method createButton():Button

    // Note that, despite its name, the creator's primary
    // responsibility isn't creating products. It usually
    // contains some core business logic that relies on product
    // objects returned by the factory method. Subclasses can
    // indirectly change that business logic by overriding the
    // factory method and returning a different type of product
    // from it.
    method render() is
        // Call the factory method to create a product object.
        Button okButton = createButton()
        // Now use the product.
        okButton.onClick(closeDialog)
        okButton.render()


// Concrete creators override the factory method to change the
// resulting product's type.
class WindowsDialog extends Dialog is
    method createButton():Button is
        return new WindowsButton()

class WebDialog extends Dialog is
    method createButton():Button is
        return new HTMLButton()


// The product interface declares the operations that all
// concrete products must implement.
interface Button is
    method render()
    method onClick(f)

// Concrete products provide various implementations of the
// product interface.
class WindowsButton implements Button is
    method render(a, b) is
        // Render a button in Windows style.
    method onClick(f) is
        // Bind a native OS click event.

class HTMLButton implements Button is
    method render(a, b) is
        // Return an HTML representation of a button.
    method onClick(f) is
        // Bind a web browser click event.


class Application is
    field dialog: Dialog

    // The application picks a creator's type depending on the
    // current configuration or environment settings.
    method initialize() is
        config = readApplicationConfigFile()

        if (config.OS == "Windows") then
            dialog = new WindowsDialog()
        else if (config.OS == "Web") then
            dialog = new WebDialog()
        else
            throw new Exception("Error! Unknown operating system.")

    // The client code works with an instance of a concrete
    // creator, albeit through its base interface. As long as
    // the client keeps working with the creator via the base
    // interface, you can pass it any creator's subclass.
    method main() is
        this.initialize()
        dialog.render()

قابلیت‌ها و کاربرد‌ها

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

نحوه پیاده‌سازی

مزایا و معایب متد Factory

این الگوی طراحی دارای مزایا و معایبی به شرح زیر است:

مزایا

– معایب

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

ارتباط با الگوهای دیگر

نمونه کد متد Factory

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

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