Factory design pattern


السلام عليكم و رحمه الله تعالى و بركاته

هذا الموضوع يعتمد على ماتم شرحه فى سلسله جافاسكربت الموجهه بالكائنات .

غالبا ما يحتوى الصنف class على العديد من الكائنات objects التى يتم انشاءها من اصناف اخرى ، من السهل انشاء هذه الكائنات عن طريق الكلمه المفتاحيه new و ال function constructor ، لكن هذا يؤدى إلى اعتماد هذا الصنف على الاصناف التى تم توليد منها هذا الكائنات ، فى هذا الموضوع سننظر إلى النمط Factory الذى يقلل من اعتماد الاصناف على بعضها ، وجب التنويه ان نمط التصميم factory يعتمد على الواجهات البرمجيه Interfaces بشكل كبير جدا ، يمكنك قراءه موضوع ال Interface من هنا ، حتى تتضح الفكره اكثر لنفرض ان هناك متجر لبيع السيارات –  لها نفس ال Interface – يمكننا تمثيله كما يوضح الكود البسيط التالى :

/* CarShop Class */
var CarShop = function(){};
CarShop.prototype = {
    sellCar:function(model){
        var car;
        switch(model){
            case "bmw":
                car = new BMW();
                berak;
            case "mercedes":
                car = new Mercedes();
                break;
            case "jaguar":
                car = new Jaguar();
                break;
            case "lexus":
                car = new Lexus();
                break;
            default:
                car = new Fiat();
        }
        Interface.ensureImplements(car,Car);
        car.assemble();
        car.wash();
        return car;
    }
};

الصنف CarShop يحتوى على الوظيفه sellCar التى تقبل عباره model تمثل نوع السياره ، يتم انشاء كائن جديد يمثل نوع السياره ، لكن لا يتم ارجاعها من الوظيفه sellCar إلا بعد التأكد من انها تطابق ال Interface الذى بإسم Car عن طريق Interface.ensureImplements التى شرحتها من قبل هنا ، يمكن تمثيل السياره BMW كما يوضح الكود التالى :

/* Car Interface */
var Car = new Interface("Car",['start','stop','accelerate','openDoor','closeDoor']);
/* BMW Class */
var BMW = function(){};
/* implements Car Interface */
BMW.prototype = {
    start:function(){/*code*/},
    stop:function(){/*code*/},
    accelerate:function(){/*code*/},
    openDoor:function(){/*code*/},
    closeDoor:function(){/*code*/},
    /* ...rest of BMW features... */
}

يمكنك الان عمل متجر سيارات و البيع كما يوضح الكود التالى :

var myShop = new CarShop();
var yourCar = myShop.sellCar("bmw");

يمكننى انشاء متجر اخر و بيع السيارات منه :

var keepondevShop = new CarShop();
var myCar = myShop.sellCar("lexus");

لكن ماذا اذا كنت اريد اضافه سياره جديده ؟ سأضطر إلى تغيير الكود فى CarShop بالرغم من ان وظائفه لن تتغير ، ثم ان صنع السيارات ليس مسؤليه CarShop ، و بهذا نحن نخالف مبدأ من مبادىء البرمجه الموجهه بالكائنات : your class should have only one reason to change لابد من سبب منطقى لتغير اى صنف ، الحل الافضل هو انشاء مصنع سيارات CarFactory يتولى صنع السيارات و بذلك اضافه سياره جديده سيكون فى المكان الطبيعى لصنعها و ليس فى متجر السيارات ، يمكننا انشاء CarFactory عن طريق نمط التصميم Singleton ، بحيث سيعطى namespace للوظيفه createCar كما يوضح المثال البسيط التالى :

CarFactory = {
    createCar:function(model){
        var car;
        switch(model){
            case "bmw":
                car = new BMW();
                berak;
            case "mercedes":
                car = new Mercedes();
                break;
            case "jaguar":
                car = new Jaguar();
                break;
            case "lexus":
                car = new Lexus();
                break;
           case "bugati": /* new car added */
               car = new Buggati();
               break;
            default:
                car = new Fiat();
        }
        Interface.ensureImplements(car,Car);
        return car;
    }
};

و سيتغير الكود ب CarShop كالتالى :

/* CarShop Class */
var CarShop = function(){};
CarShop.prototype = {
    sellCar:function(model){
        var car = CarFactory.createCar(model);
        car.assemble();
        car.wash();
        return car;
    }
};

و بذلك يمكننا صنع و بيع السيارات كالتالى :

var yourShop = new CarShop();
var myBrandNewCar = yourShop.sellCar("buggati");

الان يمكننى عمل اكثر من متجر من CarShop و اضافه سيارات جديده للمصنع CarFatory ، و بالطبع يمكن للمتاجر الاخرى التعامل مع المصنع CarFactory .

نمط تصميم Factory الحقيقى يختلف عن ال CarFactory الذى انشأناه من Singleton فى ان مسئوليه انشاء الكائنات داخل الصنف يتولاها صنف فرعى Subclass و ليس صنف اخر منفصل .

Factory Pattern is to “Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses.

و عليه يمكننا انشاء CarShop فى صوره abstract class لا يمكن انشاءه ، به وظيفه createCar لاتؤدى وظيفه و سوف نعطيها وظيفه فى ال subClass كما يوضح الكود التالى :

/* abstract class */
var CarShop = function(){};
CarShop.prototype = {
    sellCar:function(model){
        var car = this.createCar(model);
        car.assemble();
        car.wash();
        return car;
    },
    createCar:function(){
        throw new Error("method not implemented in abstract class");
    }
}

هذه الوظيفه بمثابه abstract class لا يمكن انشائها ووظيفه createCar لا تؤدى وظيفه بها ، يمكننا ادراج هذه الوظيفه فى ال subClass من خلال extend كما يلى :

var BMWShop = function(){};
extend(BMWShop,CarShop);
BMWShop.prototype.createCar=function(model){
    var car;
    switch (model){
        case "city":
            car = new BMW1Series();
            break;
        case "compact":
            car = new BMW3Series();
            break;
        case "executive":
            car = new BMW5Series();
            break;
        case "luxury":
            car = new BMW7Series();
            break;
    }
    Interface.ensureImplements(car,Car);
    return car;
}

هذا المتجر يبيع سيارات BMW يمكننا عمل متجر اخر يبيع سيارات Mercedes بمثابه subClss ل CarShop عن طريق extend كما يوضح الكود التالى :

var MercedesShop = function(){};
extend(MercedesShop,CarShop);
MercedesShop.prototype.createCar=function(model){
    var car;
    switch (model){
        case "city":
            car = new MercedesBClass();
            break;
        case "compact":
            car = new MercedesCClass();
            break;
        case "executive":
            car = new MercedesEClass();
            break;
        case "luxury":
            car = new MercedesSClass();
            break;
    }
    Interface.ensureImplements(car,Car);
    return car;
}

و يمكننا استخدام المتجرين كما يلى :

var myShop = new BMWShop();
myShop.sellCar("luxury"); // BMW 7 Series car

var yourShop = new MercedesShop();
yourShop.sellCar("luxury"); // Mercedes S class

متى تستخدم نمط تصميم Factory ؟
اذا كان هناك اصناف كتير لها نفس ال Interface كما فى مقال CarShop السابق يمكنك تسهيل عمليه انشاء الصنف المطلوب من بين تلك الاصناف المتشابه فى Interface عن طريق نقل كود الانشاء داخل Factory ، او عندما تكون الاصناف يتم انشائها وقت التشغيل كما فى حاله تطبيقات Ajax حيث ان هناك اكثر من كائن يمكن انشاءه حسب البيئه التى يعمل بها التطبيق ، مثلا فى متصفحات ال W3c يمكنك استخدام XMLHttpRequest ، انا فى متصفحات IE يمكنك استخدام Microsoft.XMLHTTP او Msxml2.XMLHTTP ، فى كتاب APRESS:JavaScript design patterns قام الكاتب بعمل ثلاث انواع من الاتصالات التى تعتمد على Ajax ، الاول SimpleHandler يستخدم للسرعات العاليه ، و QueueHandler يستخدم اذا كانت السرعه بطئيئه ، و الثالث OfflineHandler يستخدم اذا كان الاتصال مقطوع ، و قام بعمل Factory لتحديد اى من هذه الاصناف سوف يتم انشاءه على حسب ظروف المتصفح الذى يستخدمه الزائر ، يستخدم ايضا Factory لتجميع الكائنات الصغيره لعمل كائن كبيره ، مثلا فى مثال CarFactory تم تجميع السياره مباشره ، يمكنك تجميع السياره من كائنات صغيره يحددها خيارات المستخدام مثل الاطارات بأنواعها و الجنوط بأنواعها و المواتير و انواعها وغيره من مواصفات السياره .

فوائد نمط نصميم Factory ؟
يقوم بمحو اى اعتماد بين الكائنات بين بعضها الى اقصى حدود ، و بذلك سيكون التغيير فى الكود متمركز فى المنطقه التى تتغير فيها الخاصيه او الميزه ، كما انه يجمع الكود الخاص بإنشاء الكائنات فى مكان واحد و بذلك يمكن انشاء هذه الكائنات ديناميكيا وقت التشغيل اعتمادا على الظروفه المحيطه او خيارت المستخدم .

متى لا تستخدم نمط التصميم Factory ؟
اذا كان هناك صنف واحد فقط لا يمكن تغيره بأصناف اخرى ، فى هذه الحاله إنشاء الكائن عن طريق new مباشرا افضل بدلا من متابعه ال factory عن طريق debugger او غيره لمعرفه اى كائن تم انشاءه ، لأن زياده الكود لن تزيد اى مرونه ولا فائده .

الموضوع تلخيص الفصل السابع من كتاب APRESS: Pro JavaScript design patterns .

خارج النص : انا ان شاء الله هروح مؤتمر WordCamp Egypt 2009 يوم 28 مارس ، اللى عايز ييجى معايا او نتقابل يكلمنى على الموبايل او يبعتلى ميل من صفحه contact ، سلااااااااااااام .

الأوسمة: , , ,

3 تعليقات to “Factory design pattern”

  1. almhajer Says:

    بارك الله فيك الله بيعنك ويوفقك

  2. Ahmed Mahmoud Says:

    احتمال كبير احضر ان شاء الله. و ان شاء الله لو هحضر هتصل بيك و يكون ليا الشرف اننا نتقابل

  3. mostafa farghaly Says:

    @Almhajer:شكرا لمتابعتك
    @Amhmed mahmoud: ده يشرفنى يا باشا اننا نروح مع بعض

أضف تعليقاً

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s


%d مدونون معجبون بهذه: