دیزاین پترن کارخانه ی انتزاعی - Abstract factory

گردآوری و تالیف : مجله آموزشی راکت
تاریخ انتشار : 19 دی 1396
دسته بندی ها : آموزشی

در این جلسه یکی دیگر از سری دیزاین پترن ها را به بررسی می‌گذاریم . 

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

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

به طور مثال کلاس کتابخانه انتزاعیDocumentCreator  که رابطی برای ساخت تعدادی از کتابخانه‌ها (ازجمله createLetter  و createResume ) را فراهم می‌کند. این کلاس کتابخانه می‌تواند به هر تعداد، نسخه‌های کتابخانه‌های مشتق شده از کلاس DocumentCreator  را شامل شود. 

مثل FancyDocumentCreator  یا ModernDocumentCreator  که هر کدام با یک پیاده‌سازی متفاوت از createLetter  و createResume  شئ‌های مربوطه را تولید می کنند. هر کدام از یک کلاس انتزاعی ثابتی مانند Letter  یا Resume  ساخته می‌شوند. برنامه زیرشاخه یک ساخته ازDocumentCreator را دریافت می‌کند که به آن کارخانه انتزاعی گفته می‌شود. اشیاء ساخته شده همگی توسط یک DocumentCreator  ساخته می‌شوند و ساختار مشابهی خواهند داشت. برنامه زیرشاخه، تنها نیاز است بداند که چگونه یک کلاس از نوع Letter  یا Resume  را که از کارخانه می‌گیرد استفاده کند.

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

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

ماهیت کتابخانه انتزاعی را می‌توان این طور تعریف کرد که "رابطی برای ساخت خانواده‌ای از اشیاء مرتبط یا وابسته بدون نیاز به مشخص کردن نوع کلاس آن‌ها.

کارخانه نوع شیءای که قرار است ساخته بشود را تعیین می‌کند. هرچند که تنها یک شیء از آن را بازمی‌گرداند.

این باعث جلوگیری از ساخت شیء توسط برنامه زیرشاخه می‌شود. برنامه زیر شاخه تنها از کارخانه می‌خواهد تا یک شیء از مدل خواسته شده را برای آن بسازد.

از آنجایی که کارخانه تنها یک نشانه‌گر به شیء ساخته شده را بازمی‌گرداند برنامه زیرشاخه نمی‌داند که این شیء از کدام کلاس ساخته شده است. پس نوع شی لازم نیست برای برنامه مشتری تعریف شود. در کل این بدین معنی است که:

همانطور که در الگوی طراحی Factory Method مشاهده شد، این الگو یک عیب دارد، آن هم این است که از کدام Creator باید استفاده شود و مستقیما در کد بایستی ذکر شود.  

class ConcreteCreator : Creator
{
     public override IProduct FactoryMethod(string type)
    {
            switch (type)
           {
                case "A": return  new ConcreteProductA();
                case "B": return  new ConcreteProductB();
                default: throw new ArgumentException("Invalid type", "type");
           }
     }
}

برای حل این مشکل می‌توانیم سراغ الگوی طراحی دیگری برویم که Abstract Factory نام دارد. این الگوی طراحی 4 بخش اصلی دارد که هر کدام از این بخش‌ها را طی مثالی توضیح می‌دهم:

1. Abstract Factory : در کشور، صنعت خودروسازی داریم که خودرو‌ها را در دو دسته‌ی دیزلی و سواری تولید می‌کنند :

public interface IVehicleFactory     {
        IDiesel GetDiesel();
        IMotorCar GetMotorCar();
    }

2. Concrete Factory : دو کارخانه‌ی تولید خودرو داریم که در صنعت خودرو سازی فعالیت دارند و عبارتند از ایران خودرو و سایپا که هر کدام خودرو‌های خود را تولید می‌کنند. ولی هر خودرویی که تولید می‌کنند یا دیزلی است یا سواری. شرکت ایران خودرو، خودروی آرنا را بعنوان دیزلی تولید می‌کند و پژو 206 را بعنوان سواری. همچنین شرکت سایپا خودروی فوتون را بعنوان خودروی دیزلی تولید می‌کند و خودروی پراید را بعنوان خودروی سواری.

public class IranKhodro : IVehicleFactory
    {
        public IDiesel GetDiesel() { return new Arena(); }
        public IMotorCar GetMotorCar() { return new Peugeot206(); }
    }
    public class Saipa : IVehicleFactory
    {
        public IDiesel GetDiesel() { return new Foton(); }
        public IMotorCar GetMotorCar() { return new Peride(); }
    }

3. Abstract Product : خودروهای تولیدی همانطور که گفته شد یا دیزلی هستند یا سواری که هر کدام از این خودروها ویژگی‌های خاص خود را دارند (در این مثال هر دو دسته خودرو برای خود نام دارند)

public interface IDiesel { string GetName();}
public interface IMotorCar { string GetName();}

4. Concrete Product : در بین این خودروها، خودروی پژو 206 و پراید یک خودروی سواری هستند و خودروی فوتون و آرنا، خودروهای دیزلی.

public class Foton : IDiesel { public string GetName() { return "This is Foton"; } }
public class Arena : IDiesel { public string GetName() { return "This is Arena"; } }
public class Peugeot206 : IMotorCar { public string GetName() { return "This is Peugeot206"; } }
public class Peride : IMotorCar { public string GetName() { return "This is Peride"; } }

حال که 4 دسته اصلی این الگوی طراحی را آموختیم می‌توان از آن بصورت زیر استفاده نمود:

IVehicleFactory factory = new IranKhodro();
 Console.WriteLine("***" + factory.GetType().Name + "***");
 IDiesel diesel = factory.GetDiesel();
 Console.WriteLine(diesel.GetName());
 IMotorCar motorCar = factory.GetMotorCar();
 Console.WriteLine(motorCar.GetName());

 factory = new Saipa();
 Console.WriteLine("***" + factory.GetType().Name + "***");
 diesel = factory.GetDiesel();
 Console.WriteLine(diesel.GetName());
 motorCar = factory.GetMotorCar();
 Console.WriteLine(motorCar.GetName());

همانطور که در کد فوق مشاهده میشود، ایراد موجود در الگوی Factory Method اینجا از بین رفته است و برای ساخت آبجکت‌های مختلف از Innterfaceها یا Abstract Classها استفاده می‌کنیم. 

Abstract Factory مزایای زیر را دارد :

  • پیاده سازی و نامگذاری Product در Factory مربوطه متمرکز می‌شود و بدین ترتیب Client به نام و نحوه پیاده سازی Type‌های مختلف Product وابستگی نخواهد داشت.
  • به راحتی می‌توان Concrete Factory مورد استفاده در برنامه را تغییر داد، بدون اینکه تاثیری در عملکرد سایر بخش‌ها داشته باشد.
  • در مواردی که بیش از یک محصول برای هر خانواده وجود داشته باشد، استفاده از Abstract Factory تضمین می‌کند که Product‌های هر خانواده همه در کنار هم قرار دارند و با هم فعال و غیر فعال می‌شوند. (یا همه، یا هیچکدام) 

بزرگترین عیبی که این الگوی طراحی دارد این است که با اضافه شدن فقط یک Product تازه، Abstract Factory باید تغییر کند که این مساله منجر به تغییر همه Concrete Factory‌ها می‌شود.

در استفاده از این الگوی طراحی به این تکنیک‌ها توجه داشته باشید:

  • Factory‌ها معمولا Singleton هستند. زیرا هر Application بطور معمول فقط به یک instance از هر Concrete Factory نیاز دارد.
  • انتخاب Concrete Factory مناسب معمولا توسط پارامترهایی انجام می‌شود.

نمودار کلاسی این الگو نیز بصورت زیر میباشد:

و در نهایت:

Abstract Factory یک interface یا کلاس abstract است که signature متدهای ساخت Object‌ها در آن تعریف شده است و Concrete Factory‌ها آن‌ها را implement می‌نمایند. 

در Abstract Factory Pattern همه Product‌های هم خانواده در Concrete Factory مربوط به آن خانواده پیاده سازی و مجتمع می‌گردند. 

در کدهای برنامه تنها با Abstract Factory و Abstract Product‌ها سر و کار داریم و به هیچ وجه درگیر این مساله که کدام یک از Concrete Class‌ها در برنامه مورد استفاده قرار می‌گیرند، نمی‌شویم.   

interface ButtonInterface
{
    public function Paint();
}

interface GUIFactoryInterface
{
    public function CreateButton();
}

class WinFactory implements GUIFactoryInterface
{
    public function CreateButton()
    {
        return new WinButton();
    }
}

class OSXFactory implements GUIFactoryInterface
{
    public function CreateButton()
    {
        return new OSXButton();
    }
}

class WinButton implements ButtonInterface
{
    public function Paint()
    {
        echo "Windows Button";
    }
}

class OSXButton implements ButtonInterface
{
    public function Paint()
    {
        echo "OSX Button";
    }
}

$appearance = "osx";

$factory = NULL;

switch ($appearance) {
    case "win":
        $factory = new WinFactory();
        break;
    case "osx":
        $factory = new OSXFactory();
        break;
    default:
        break;
}

$button = $factory->CreateButton();
$button->Paint();

مقالات پیشنهادی

دیزاین پترن الگوی کارخانه - Factory method

در برنامه نویسی شی گرا ، الگوی روش کارخانه یک الگوی خلاقانه است که از روش کارخانه برای حل مسئله ایجاد اشیاء بدون نیاز به تعیین دقیق کلاس شئ که ایجاد م...

دیزاین پترن زنجیره - Chain-of-responsibility pattern

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

دیزاین پترن استراتژی - Strategy Pattern

استراتژی پترن درواقع نوعی از دیزاین‌ پترن‌های Behavioral است که به شما اجازه می‌دهد راهکارهایی که برنامهٔ مورد نظرتان باید اجرا کند، بسته به شرایط خاص...

دیزاین پترن پل - Bridge

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