الرئيسيةعريقبحث

نمط طريقة المصنع


☰ جدول المحتويات


في البرمجة القائمة على الصنف class-based programming، يعد نمط طريقة المصنع factory method pattern نمطًا إبداعيًا يستخدم أساليب المصنعfactory methods للتعامل مع مشكلة إنشاء الكائنات دون الحاجة إلى تحديد صنف الكائن الذي سيتم إنشاؤه بالضبط. يتم ذلك عن طريق إنشاء كائنات عن طريق استدعاء طريقة المصنع - إما محدد في واجهة interfaceويتم تنفيذه بواسطة اصناف فرعية child classes، أو يتم تنفيذه في صنف أساسي base classويتم تجاوزه اختياريًا بواسطة الاصناف المشتقةoptionally overridden by derived classes - بدلاً من استدعاء مُنشئ .constructor

نظرة عامة

يعد نمط تصميم طريقة المصنع [1] أحد أنماط التصميم "عصابة الأربعة" التي تصنفclass كيفية حل مشكلات التصميم المتكررة لتصميم برامج مرنة وموجهة object-orientedقابلة لإعادة الاستخدام، أي الكائنات التي يسهل تنفيذهاimplement أو تغييرها أو اختبارها، وإعادة استخدامها.

يتم استخدام نمط تصميم طريقة المصنع بدلاً من مُنشئ الصنف العادي regular class constructor للاحتفاظ بمبدأ SOLID للبرمجة، وفصل بناء الكائنات من الكائنات نفسها. هذا له المزايا التالية ومفيد للحالات التالية، من بين أمور أخرى: [2]

    • يسمح ببناء الاصنافconstruction of classes بمكونcomponent من نوع type لم يتم تحديده مسبقًا predetermined ، ولكن يتم تعريفه defined فقط في "واجهة" interface أو التي يتم تعريفها على أنها نوع ديناميكيdynamic type.
وبالتالي، على سبيل المثال، يمكن إنشاء كلاس او صنفclass مركبة التي تحتوي على محرك عضوmember من interface واجهة IMotor ، ولكن لا يوجد نوع ملموسconcrete type من المحرك معرف defined مسبقًا، عن طريق إخبار مُنشئ السيارة Vehicle constructor باستخدام محرك كهربائي أو محرك بنزين. يستدعي كود مُنشئ المركبة Vehicle constructor بعد ذلك طريقة مصنع المحرك Motor factory method، لإنشاء المحرك المطلوب الذي يتوافق complies مع واجهة interface IMotor.
  • يسمح ببناء الاصناف الفرعيةsubclasses إلى أحد الوالدينparent الذي لم يتم تحديد نوع مكوناتهcomponent type مسبقًا، ولكن تم تعريفهdefined فقط في واجهة interface، أو تم تعريفهdefined على أنه نوع ديناميكي dynamic type.
على سبيل المثال، يمكن لمركبة من الصنف ذات محرك class Vehicle التي لديها عضو member محدد بنوع ديناميكي dynamic type ، أن تحتوي على اصناف فرعيةsubclasses من النوع ElectricPlane و OldCar تم تصميم كل منها بنوعtype مختلف من المحركات. يمكن تحقيق ذلك accomplished عن طريق بناء constructing الاصناف الفرعيةsubclasses بطريقة مصنع المركباتVehicle factory method ، مع توفير نوع المحركmotor type. في مثل هذه الحالات قد يكون المنشئconstructor مخفيًا.
  • يسمح لكود أكثر قابلية للقراءة في الحالات التي توجد فيها منشآت constructors متعددة، كل منها لسبب مختلف.
على سبيل المثال إذا كان هناك نوعان من constructors المنشئات Vehicle(make:string, motor:number) و Vehicle(make:string, owner:string, license:number, purchased:date) فإن بناءconstruction أكثر قابلية للقراءة من الاصناف classes يكون لاستخدام Vehicle.CreateOwnership(make:string, owner:string, license:number purchased: date) vsمقابل Vehicle.Create(make:string, motor:number)

يسمح للصنفclass بتأجيل التمثيل instantiation إلى الاصناف الفرعية subclasses، ومنع التمثيلinstantiation المباشر لكائن من نوع الصنف الأصلparent class type.

على سبيل المثال، يمكن منع المركبة Vehicle من أن يتم تمثيلها instantiate مباشرة لأنها لا تحتوي على مُنشئconstructor ، ويمكن إنشاء الاصناف الفرعية subclasses فقط مثل ElectricPlane أو OldCar عن طريق استدعاء طريقة مصنع السيارة (الثابتة)Vehicle (static) factory method في مُنشئconstructor الصنف الفرعيةsubclass أو المُهيئinitializer.

إنشاء كائن مباشرة داخل الصنف يتطلب أو يستخدم الكائن غير مرن لأنه يلزم الصنف بكائن معين ويجعل من المستحيل تغيير التمثيلinstantiation بشكل مستقل عن الصنف. يتطلب الممثل instantiator في تغييرًا في كود الصنف class code الذي نفضل عدم لمسه. ويشار إلى هذا باسم اقتران الكود code couplingويساعد نمط طريقة المصنع في فصل الكودdecoupling .

يتم استخدام نمط تصميم طريقة المصنع من خلال تحديد عملية منفصلة أولاً، طريقة المصنعfactory method, ، لإنشاء كائن، ثم استخدام طريقة المصنع هذه عن طريق استدعاءها لإنشاء الكائنobject. يتيح ذلك كتابة الاصناف الفرعية subclasses التي تحدد كيفية إنشاء كائن أصلparent object ونوع الكائنات التي يحتوي عليها الأصل. انظر الرسم التخطيطي لصنف UML أدناه.

تعريف

"حدد واجهة interface لإنشاء كائن، لكن دع الصنف الفرعيsubclasses يقرر أي صنف يريد إنشاء مثيل لهاwhich class to instantiate. تتيح طريقة المصنع للصنف تأجيل التمثيل instantiation الذي تستخدمه في الصنفclass الفرعية ". ( عصابة الأربعة )Gang Of Four)

غالبًا ما يتطلب إنشاء كائن عمليات معقدة غير مناسبة لتضمينها في كائن إنشاءcomposing object. قد يؤدي إنشاء الكائن إلى ازدواجية كبيرة في التعليمات البرمجية، وقد يتطلب معلومات لا يمكن الوصول إليها للكائن المؤلف، وقد لا يوفر مستوى كافًا من التجريدabstraction ، أو قد لا يكون جزءًا من شؤون الكائن المؤلف composing object's concerns. يعالج نمط تصميم طريقة المصنع هذه المشاكل من خلال تحديد طريقة منفصلة لإنشاء الكائنات، والتي يمكن للاصناف الفرعية تجاوزها لتحديد نوع المنتج المشتق<a href="https://en.wikipedia.org/wiki/Subtype" rel="mw:ExtLink.html" title="Subtype" class="mw-redirect cx-link" data-linkid="45">derived type</a> of product الذي سيتم إنشاؤه.

يعتمد نمط طريقة المصنع على الوراثةinheritance ، حيث يتم تفويضdelegated إنشاء الكائن إلى الاصناف الفرعية subclasses التي تطبق طريقة المصنع لإنشاء الكائنات. [3]

بناء

مخطط صنف UML class diagram

رسم تخطيطي لصنف UML لنمط تصميم طريقة المصنع. [4]

في الرسم التخطيطي لصنف UML أعلاه، الصنف Creator الذي يتطلب كائن Product

لا يمثلinstantiate الصنفclass Product1 مباشرة.

بدلاً من ذلك، يشير Creator إلى الدالة المنفصلة factoryMethod() لإنشاء كائن منتج، مما يجعل Creator مستقلاً حيث يتم إنشاء صنف ملموسة منهconcrete class is instantiated. يمكن للاصناف الفرعية من Creator إعادة تعريف صنفclass التي يجب إنشاء مثيلinstantiate لها. في هذا المثال، يقوم الصنف الفرعي Creator1 بتطبيق implements الدالة المجردةfactoryMethod() abstract عن طريق إنشاءinstantiating صنف Product1 .

مثال

يمكن لعب لعبة متاهة في وضعين، أحدهما يحتوي على غرف عادية متصلة فقط بالغرف المجاورة، والآخر بغرف سحرية تسمح بنقل اللاعبين بشكل عشوائي.

هيكل Structure

New WikiFactoryMethod.png

Room هو الصنف الأساسي للمنتج النهائي ( MagicRoom أو OrdinaryRoom ). تعلن شركة MazeGame عن طريقة المصنع المجردة declares the abstract factory methodلإنتاج مثل هذا المنتج الأساسي.

MagicRoom و OrdinaryRoom هماsubclasses اصناف فرعية MagicRoom للمنتج الأساسي الذي ينفذimplementing المنتج النهائي. وهكذا تقوم factory methodsطرق المصنع decouple callers فصل المتصلين ( MazeGame ) من تنفيذimplementation أصناف محددةconcrete classes. وهذا يجعل عامل التشغيل "الجديد""new" Operator زائداً عن الحاجةredundant ، ويسمح بالالتزام بمبدأ فتح / إغلاق Open/closed principleويجعل المنتج النهائي أكثر مرونة في حالة التغيير.

تطبيقات المثال

C#

نفس اكود ل C #

//Empty vocabulary of actual object public interface IPerson { string GetName(); } public class Villager : IPerson { public string GetName() { return "Village Person"; } } public class CityPerson : IPerson { public string GetName() { return "City Person"; } } public enum PersonType { Rural, Urban } /// <summary> /// Implementation of Factory - Used to create objects /// </summary> public class Factory { public IPerson GetPerson(PersonType type) { switch (type) { case PersonType.Rural: return new Villager(); case PersonType.Urban: return new CityPerson(); default: throw new NotSupportedException(); } } }

في الكود أعلاه، يمكنك رؤية إنشاء واجهة interface واحدة تسمى IPerson وتنفيذين يسمى Villager و CityPerson. استنادًا إلى النوع الذي تم تمريره إلى كائن المصنع، فإننا نعيد الكائن المحدد الأصلي original concrete object كواجهة interface IPerson. طريقة المصنع هي مجرد إضافة إلى صنف المصنع. يقوم بإنشاء كائن من الصنف من خلال واجهاتinterfaces ولكن من ناحية أخرى، فإنه يتيح أيضًا للاصناف الفرعية subclass تحديد الصنف التي يتم إنشاء مثيلinstantiated لها.

public interface IProduct { string GetName(); string SetPrice(double price); } public class Phone : IProduct { private double _price; public string GetName() { return "Apple TouchPad"; } public string SetPrice(double price) { this._price = price; return "success"; } } /* Almost same as Factory, just an additional exposure to do something with the created method */ public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct(); public IProduct GetObject() // Implementation of Factory Method. { return this.MakeProduct(); } } public class PhoneConcreteFactory : ProductAbstractFactory { protected override IProduct MakeProduct() { IProduct product = new Phone(); //Do something with the object after you get the object. product.SetPrice(20.30); return product; } }

يمكنك أن ترى أننا استخدمنا MakeProduct في مصنع التحديد concreteFactory. نتيجة لذلك، يمكنك بسهولة استدعاء MakeProduct () منه للحصول على IProduct. يمكنك أيضًا كتابة منطقك المخصص custom logicبعد الحصول على الكائن في طريقة مصنع التحديد concrete Factory Method. يتم إجراء GetObject مجردة في واجهة interface المصنع.

جافا

يشبه مثال Java هذا أحد الأمثلة الموجودة في كتاب أنماط تصميم Design Patterns..

يستخدم MazeGame الغرف ولكنه يضع مسؤولية إنشاء الغرف على الاصناف الفرعيةsubclasses التي تنشئ اصناف ملموسةcreate the concrete classes.. يمكن أن يستخدم وضع اللعبة العاديregular game mode طريقة القالب template method هذه:

public abstract class Room { abstract void connect(Room room); } public class MagicRoom extends Room { public void connect(Room room) {} } public class OrdinaryRoom extends Room { public void connect(Room room) {} } public abstract class MazeGame { private final List<Room> rooms = new ArrayList<>(); public MazeGame() { Room room1 = makeRoom(); Room room2 = makeRoom(); room1.connect(room2); rooms.add(room1); rooms.add(room2); } abstract protected Room makeRoom(); }

في المقتطف أعلاه، MazeGame مُنشئ (constructor ) MazeGame طريقة قالب template methodتجعل بعض المنطق شائعًا common logic. يشير إلى factory methodطريقة المصنع makeRoom التي تتضمن إنشاء الغرف بحيث يمكن استخدام الغرف الأخرى في اصناف فرعيةsubclass. لتنفيذ وضع اللعبة game mode الآخر الذي يحتوي على غرف سحريةmagic rooms ، يكفي تجاوز override makeRoom :

public class MagicMazeGame extends MazeGame { @Override protected Room makeRoom() { return new MagicRoom(); } } public class OrdinaryMazeGame extends MazeGame { @Override protected Room makeRoom() { return new OrdinaryRoom(); } } MazeGame ordinaryGame = new OrdinaryMazeGame(); MazeGame magicGame = new MagicMazeGame();

بي أتش بيPHP

يتبع مثال آخر في PHP ، هذه المرة باستخدام تطبيقات الواجهةinterface implementations بدلاً من التصنيف الفرعي subclassing (ومع ذلك يمكن تحقيق نفس الشيء من خلال التصنيف الفرعي). من المهم ملاحظة أنه يمكن أيضًا تعريف طريقة المصنع على أنها عامة واستدعاؤها مباشرةً بواسطة كودالعميل client code(على النقيض من مثال Java أعلاه).

/* Factory and car interfaces */ interface CarFactory { public function makeCar() : Car; } interface Car { public function getType() : string; } /* Concrete implementations of the factory and car */ class SedanFactory implements CarFactory { public function makeCar() : Car { return new Sedan(); } } class Sedan implements Car { public function getType() : string { return 'Sedan'; } } /* Client */ $factory = new SedanFactory(); $car = $factory->makeCar(); print $car->getType();

بايثونPython

نفس مثال جافا.

from abc import ABC, abstractmethod class MazeGame(ABC): def __init__(self) -> None: self.rooms = [] self._prepare_rooms() def _prepare_rooms(self) -> None: room1 = self.make_room() room2 = self.make_room() room1.connect(room2) self.rooms.append(room1) self.rooms.append(room2) def play(self) -> None: print('Playing using "{}"'.format(self.rooms[0])) @abstractmethod def make_room(self): raise NotImplementedError("You should implement this!") class MagicMazeGame(MazeGame): def make_room(self): return MagicRoom() class OrdinaryMazeGame(MazeGame): def make_room(self): return OrdinaryRoom() class Room(ABC): def __init__(self) -> None: self.connected_rooms = [] def connect(self, room) -> None: self.connected_rooms.append(room) class MagicRoom(Room): def __str__(self): return "Magic room" class OrdinaryRoom(Room): def __str__(self): return "Ordinary room" ordinaryGame = OrdinaryMazeGame() ordinaryGame.play() magicGame = MagicMazeGame() magicGame.play()

الاستخدامات

  • في ADO. NET ، IDbCommand. CreateParameter هو مثال على استخدام طريقة المصنعfactory method لربط التسلسلات الهرمية للصنف المتوازيةconnect parallel class hierarchies.
  • في Qt ، QMainWindow :: createPopupMenu هي طريقة مصنع factory method تم التصريحdeclared عنها في إطار عملframework يمكن تجاوزه في رمز التطبيق overridden in application code.
  • في جافا ، يتم استخدام العديد من المصانعseveral factories في حزمةpackage javax.xml.parsers . مثل javax.xml.parsers. DocumentBuilderFactory أو javax.xml.parsers. SAXParserFactory.
  • في HTML5 DOM API ، تحتوي واجهة المستند Document interface علىfactory method طريقة مصنع createElement لإنشاء عناصر محددة لواجهة interface HTMLElement.

مقالات ذات صلة

المراجع

  1. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. صفحات 107ff.  . مؤرشف من الأصل في 18 مايو 2020.
  2. "The Factory Method design pattern - Problem, Solution, and Applicability". w3sDesign.com. مؤرشف من الأصل في 18 مايو 202017 أغسطس 2017.
  3. Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Hendrickson; Loukides (المحررون). Head First Design Patterns. 1. O'REILLY. صفحة 162.  . مؤرشف من الأصل في 30 أبريل 202012 سبتمبر 2012.
  4. "The Factory Method design pattern - Structure and Collaboration". w3sDesign.com. مؤرشف من الأصل في 18 مايو 202012 أغسطس 2017.

روابط خارجية

موسوعات ذات صلة :