أهلاً! سنواصل اليوم دراسة أنماط التصميم وسنناقش نمط طريقة المصنع. سوف تكتشف ما هو وما هي المهام التي يناسبها هذا النمط. سننظر في نمط التصميم هذا عمليًا وندرس هيكله. للتأكد من أن كل شيء واضح، عليك أن تفهم المواضيع التالية:
- الميراث في جاوة.
- الأساليب والطبقات المجردة في جافا
ما هي المشكلة التي تحلها طريقة المصنع؟
تحتوي جميع أنماط تصميم المصانع على نوعين من المشاركين: المبدعون (المصانع نفسها) والمنتجات (الأشياء التي أنشأتها المصانع). تخيل الموقف التالي: لدينا مصنع ينتج سيارات تحمل علامة CodeGym. يعرف كيفية إنشاء نماذج للسيارات بأنواع مختلفة من الأجسام:- سيارات السيدان
- محطة العربات
- كوبيه
- سيارات السيدان CodeGym
- عربات محطة CodeGym
- كوبيهات CodeGym
- سيارات السيدان OneAuto
- عربات محطة OneAuto
- وان أوتو كوبيه
قليلا عن نمط المصنع
اسمحوا لي أن أذكركم أننا قمنا سابقًا ببناء مقهى افتراضي صغير. بمساعدة مصنع بسيط، تعلمنا كيفية صنع أنواع مختلفة من القهوة. اليوم سوف نقوم بإعادة صياغة هذا المثال. دعونا نتذكر كيف كان شكل المقهى الخاص بنا بمصنعه البسيط. كان لدينا فئة القهوة:public class Coffee {
public void grindCoffee(){
// Grind the coffee
}
public void makeCoffee(){
// Brew the coffee
}
public void pourIntoCup(){
// Pour into a cup
}
}
والعديد من الفئات المخصصة للأطفال والتي تتوافق مع أنواع محددة من القهوة التي يمكن أن ينتجها مصنعنا:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
لقد أنشأنا التعداد لتسهيل تقديم الطلبات:
public enum CoffeeType {
ESPRESSO,
AMERICANO,
CAFFE_LATTE,
CAPPUCCINO
}
بدا مصنع القهوة نفسه كما يلي:
public class SimpleCoffeeFactory {
public Coffee createCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new Americano();
break;
case ESPRESSO:
coffee = new Espresso();
break;
case CAPPUCCINO:
coffee = new Cappuccino();
break;
case CAFFE_LATTE:
coffee = new CaffeLatte();
break;
}
return coffee;
}
}
وأخيرًا، أصبح المقهى نفسه بهذا الشكل:
public class CoffeeShop {
private final SimpleCoffeeFactory coffeeFactory;
public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
this.coffeeFactory = coffeeFactory;
}
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = coffeeFactory.createCoffee(type);
coffee.grindCoffee();
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Here's your coffee! Thanks! Come again!");
return coffee;
}
}
تحديث مصنع بسيط
المقهى الخاص بنا يعمل بشكل جيد للغاية. لدرجة أننا نفكر في التوسع. نريد فتح بعض المواقع الجديدة. نحن جريئون ومغامرون، لذلك لن نفتح المقاهي المملة. نريد أن يكون لكل متجر لمسة خاصة. وفقًا لذلك، في البداية، سنفتح موقعين: أحدهما إيطالي والآخر أمريكي. لن تؤثر هذه التغييرات على التصميم الداخلي فحسب، بل ستؤثر أيضًا على المشروبات المقدمة:- في المقهى الإيطالي، سوف نستخدم ماركات القهوة الإيطالية حصريًا، مع طحن وتحميص خاص.
- الموقع الأمريكي سيكون به كميات أكبر، وسنقدم المارشميلو مع كل طلب.
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
ولكن الآن سيكون لدينا 8:
public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}
public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
وبما أننا نريد الحفاظ على نموذج العمل الحالي، فإننا نريد orderCoffee(CoffeeType type)
أن تخضع الطريقة لأقل عدد ممكن من التغييرات. نلقي نظرة على ذلك:
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = coffeeFactory.createCoffee(type);
coffee.grindCoffee();
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Here's your coffee! Thanks! Come again!");
return coffee;
}
ما هي الخيارات التي لدينا؟ حسنًا، نحن نعرف بالفعل كيفية كتابة مصنع، أليس كذلك؟ إن أبسط ما يتبادر إلى الذهن على الفور هو كتابة مصنعين متشابهين، ثم تمرير التنفيذ المطلوب إلى منشئ المقهى الخاص بنا. ومن خلال القيام بذلك، لن تتغير فئة المقهى. أولاً، نحتاج إلى إنشاء فئة مصنع جديدة، وجعلها ترث مصنعنا البسيط، ثم تجاوز الطريقة createCoffee(CoffeeType type)
. لنكتب مصانع صنع القهوة على الطريقة الإيطالية والقهوة على الطريقة الأمريكية:
public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {
@Override
public Coffee createCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new ItalianStyleAmericano();
break;
case ESPRESSO:
coffee = new ItalianStyleEspresso();
break;
case CAPPUCCINO:
coffee = new ItalianStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new ItalianStyleCaffeLatte();
break;
}
return coffee;
}
}
public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{
@Override
public Coffee createCoffee (CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new AmericanStyleAmericano();
break;
case ESPRESSO:
coffee = new AmericanStyleEspresso();
break;
case CAPPUCCINO:
coffee = new AmericanStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new AmericanStyleCaffeLatte();
break;
}
return coffee;
}
}
يمكننا الآن تمرير تنفيذ المصنع المطلوب إلى CoffeeShop. دعونا نرى كيف سيبدو رمز طلب القهوة من المقاهي المختلفة. على سبيل المثال، الكابتشينو على الطريقة الإيطالية والطريقة الأمريكية:
public class Main {
public static void main(String[] args) {
/*
Order an Italian-style cappuccino:
1. Create a factory for making Italian coffee
2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
3. Order our coffee
*/
SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
/*
Order an American-style cappuccino
1. Create a factory for making American coffee
2. Create a new coffee shop, passing the American coffee factory to it through the constructor
3. Order our coffee
*/
SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
}
}
لقد أنشأنا مقهيين مختلفين، وقمنا بتمرير المصنع المطلوب إلى كل منهما. من ناحية، لقد حققنا هدفنا، ولكن من ناحية أخرى... هذا لا يرضي رواد الأعمال بطريقة ما... فلنكتشف ما هو الخطأ. أولاً: كثرة المصانع. ماذا؟ الآن، لكل موقع جديد، من المفترض أن ننشئ مصنعًا خاصًا به، بالإضافة إلى ذلك، نتأكد من نقل المصنع ذي الصلة إلى المنشئ عند إنشاء مقهى؟ ثانيا، لا يزال مصنعا بسيطا. تم تحديثه قليلاً فقط. لكننا هنا لنتعلم نمطًا جديدًا. ثالثا، أليس من الممكن اتباع نهج مختلف؟ سيكون أمرا رائعا لو تمكنا من وضع جميع المسائل المتعلقة بإعداد القهوة في الفصل CoffeeShop
من خلال ربط عمليات إعداد القهوة وطلبات الخدمة، مع الحفاظ في الوقت نفسه على مرونة كافية لصنع أنماط مختلفة من القهوة. الجواب هو نعم، نستطيع. وهذا ما يسمى نمط تصميم طريقة المصنع.
من مصنع بسيط إلى طريقة المصنع
لحل المهمة بأكبر قدر ممكن من الكفاءة:- نعيد
createCoffee(CoffeeType type)
الطريقة إلىCoffeeShop
الفصل. - سوف نجعل هذه الطريقة مجردة.
CoffeeShop
سوف يصبح الفصل نفسه مجردًا .CoffeeShop
سيكون للفصل فصول الأطفال .
CoffeeShop
التي تطبق الطريقة createCoffee(CoffeeType type)
وفقًا لأفضل تقاليد صانعي القهوة الإيطاليين. الآن، خطوة واحدة في كل مرة. الخطوة 1. قم بعمل Coffee
ملخص للفصل. لدينا عائلتين كاملتين من المنتجات المختلفة. ومع ذلك، فإن القهوة الإيطالية والأمريكية لها سلف مشترك - الطبقة Coffee
. سيكون من المناسب جعلها مجردة:
public abstract class Coffee {
public void makeCoffee(){
// Brew the coffee
}
public void pourIntoCup(){
// Pour into a cup
}
}
الخطوة 2. اصنع CoffeeShop
الملخص باستخدام createCoffee(CoffeeType type)
الطريقة المجردة
public abstract class CoffeeShop {
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = createCoffee(type);
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Here's your coffee! Thanks! Come again!");
return coffee;
}
protected abstract Coffee createCoffee(CoffeeType type);
}
الخطوة 3. قم بإنشاء مقهى إيطالي، وهو سليل المقهى المجرد. نقوم بتنفيذ createCoffee(CoffeeType type)
الطريقة فيه مع مراعاة خصوصيات المستلمين الإيطاليين.
public class ItalianCoffeeShop extends CoffeeShop {
@Override
public Coffee createCoffee (CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new ItalianStyleAmericano();
break;
case ESPRESSO:
coffee = new ItalianStyleEspresso();
break;
case CAPPUCCINO:
coffee = new ItalianStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new ItalianStyleCaffeLatte();
break;
}
return coffee;
}
}
الخطوة 4. نفعل الشيء نفسه بالنسبة للمقهى ذو الطراز الأمريكي
public class AmericanCoffeeShop extends CoffeeShop {
@Override
public Coffee createCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new AmericanStyleAmericano();
break;
case ESPRESSO:
coffee = new AmericanStyleEspresso();
break;
case CAPPUCCINO:
coffee = new AmericanStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new AmericanStyleCaffeLatte();
break;
}
return coffee;
}
}
الخطوة 5. تحقق من شكل اللاتيه الأمريكي والإيطالي:
public class Main {
public static void main(String[] args) {
CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
}
}
تهانينا. لقد قمنا للتو بتنفيذ نمط تصميم طريقة المصنع باستخدام المقهى الخاص بنا كمثال.
المبدأ الكامن وراء أساليب المصنع
الآن دعونا نفكر بمزيد من التفصيل في ما حصلنا عليه. ويوضح الرسم البياني أدناه الفئات الناتجة. الكتل الخضراء هي فئات المنشئين، والكتل الزرقاء هي فئات المنتجات. ما هي الاستنتاجات التي يمكن أن نتوصل إليها؟- جميع المنتجات هي تطبيقات للفئة المجردة
Coffee
. - جميع المبدعين هم تطبيقات للفئة المجردة
CoffeeShop
. - نرى تسلسلين هرميين متوازيين للفئات:
- التسلسل الهرمي للمنتجات. نرى أحفادًا إيطاليين وأحفادًا أمريكيين
- التسلسل الهرمي للمبدعين. نرى أحفادًا إيطاليين وأحفادًا أمريكيين
- لا تحتوي الفئة
CoffeeShop
الفائقة على معلومات حول المنتج المحدد (Coffee
) الذي سيتم إنشاؤه. - تقوم الطبقة
CoffeeShop
المتفوقة بتفويض إنشاء منتج معين إلى أحفادها. CoffeeShop
يطبق كل سليل للفئةcreateCoffee()
طريقة المصنع وفقًا لميزاته المحددة. بمعنى آخر، تقوم تطبيقات فئات المنتجين بإعداد منتجات محددة بناءً على تفاصيل فئة المنتجين.
هيكل طريقة المصنع
يوضح الرسم البياني أعلاه الهيكل العام لنمط طريقة المصنع. ما هو المهم هنا؟- تطبق فئة Creator جميع الأساليب التي تتفاعل مع المنتجات، باستثناء طريقة المصنع.
factoryMethod()
يجب تنفيذ الطريقة المجردة من قبل جميع أحفادCreator
الفصل.ConcreteCreator
يطبق الفصل الطريقةfactoryMethod()
التي تقوم بإنشاء المنتج مباشرة.- هذه الفئة مسؤولة عن إنشاء منتجات محددة. هذا هو الفصل الدراسي الوحيد الذي لديه معلومات حول إنشاء هذه المنتجات.
- يجب أن تطبق جميع المنتجات واجهة مشتركة، أي يجب أن تكون متحدرة من فئة منتج مشتركة. يعد ذلك ضروريًا حتى تتمكن الفئات التي تستخدم المنتجات من العمل عليها كتجريدات، بدلاً من تطبيقات محددة.
GO TO FULL VERSION