نمط طريقة المصنع
في البرمجة كائنية التوجه ، يُعد نمط طريقة المصنع نمط تصميم يستخدم طرق المصنع لمعالجة مشكلة إنشاء الكائنات دون الحاجة إلى تحديد فئاتها بدقة . فبدلاً من استدعاء دالة البناء ، يتم ذلك عن طريق استدعاء طريقة المصنع لإنشاء الكائن. يمكن تحديد طرق المصنع في واجهة وتنفيذها بواسطة الفئات الفرعية، أو تنفيذها في فئة أساسية مع إمكانية إعادة تعريفها بواسطة الفئات الفرعية. وهو أحد أنماط التصميم الكلاسيكية الـ 23 المذكورة في كتاب أنماط التصميم ، ويُصنف ضمن أنماط الإنشاء . [ 1 ]
ملخص
يحل نمط تصميم طريقة المصنع مشاكل مثل:
- كيف يمكن للفئات الفرعية للكائن إعادة تعريف تنفيذه اللاحق والمتميز؟ يتضمن النمط إنشاء طريقة مصنع داخل الفئة الأصلية تؤجل إنشاء الكائن إلى طريقة المصنع الخاصة بالفئة الفرعية.
- كيف يمكن تأجيل إنشاء كائن إلى فئة فرعية؟ أنشئ كائنًا عن طريق استدعاء دالة المصنع بدلاً من استدعاء الدالة البانية مباشرةً.
وهذا يتيح إنشاء فئات فرعية يمكنها تغيير الطريقة التي يتم بها إنشاء كائن (على سبيل المثال، عن طريق إعادة تعريف الفئة التي سيتم إنشاء مثيل لها).
تعريف
وفقًا لكتاب أنماط التصميم: عناصر البرمجيات القابلة لإعادة الاستخدام والموجهة للكائنات : "حدد واجهة لإنشاء كائن، ولكن دع الفئات الفرعية تقرر أي فئة سيتم إنشاء مثيل لها. تسمح طريقة المصنع للفئة بتأجيل إنشاء المثيل إلى الفئات الفرعية." [ 2 ]
غالبًا ما يتطلب إنشاء كائن عمليات معقدة لا يُناسب تضمينها ضمن كائن مُركِّب. قد يؤدي إنشاء الكائن إلى تكرار كبير في التعليمات البرمجية، أو قد يتطلب معلومات لا يمكن للكائن المُركِّب الوصول إليها، أو قد لا يوفر مستوى كافيًا من التجريد، أو قد لا يكون ضمن اهتمامات الكائن المُركِّب . يعالج نمط تصميم طريقة المصنع هذه المشكلات من خلال تعريف طريقة منفصلة لإنشاء الكائنات، والتي يمكن للفئات الفرعية تجاوزها لتحديد نوع المنتج المشتق الذي سيتم إنشاؤه.
يعتمد نمط طريقة المصنع على الوراثة، حيث يتم تفويض إنشاء الكائنات إلى الفئات الفرعية التي تُنفذ طريقة المصنع لإنشاء الكائنات. [ 3 ] يمكن أن يعتمد النمط أيضًا على تنفيذ واجهة .
بناء
مخطط فئات UML

في مخطط فئات UML أعلاه ، لا تقوم الفئة التي تتطلب كائنًا بإنشاء مثيل لها مباشرةً. بدلاً من ذلك، تشير إلى فئة منفصلة لإنشاء كائن منتج، مما يجعل الفئة مستقلة عن الفئة الملموسة التي يتم إنشاء مثيل لها. يمكن للفئات الفرعية إعادة تعريف الفئة التي سيتم إنشاء مثيل لها. في هذا المثال، تُنفذ الفئة الفرعية الفئة المجردة من خلال إنشاء مثيل لها .CreatorProductProduct1CreatorfactoryMethod()CreatorCreatorCreator1factoryMethod()Product1
أمثلة
بناء
يمكن لعب لعبة المتاهة في وضعين، أحدهما بغرف عادية متصلة فقط بالغرف المجاورة، والآخر بغرف سحرية تسمح للاعبين بالانتقال بشكل عشوائي.

Roomتُعدّ الفئة الأساسية للمنتج النهائي ( MagicRoomأو OrdinaryRoom). MazeGameتُعلن عن دالة المصنع المجردة لإنتاج هذا المنتج الأساسي. MagicRoomو OrdinaryRoomهما فئتان فرعيتان من المنتج الأساسي تُنفّذان المنتج النهائي. MagicMazeGameو OrdinaryMazeGameهما فئتان فرعيتان من MazeGameتُنفّذان دالة المصنع التي تُنتج المنتجات النهائية. وبذلك، تفصل دوال المصنع المُستدعين ( MazeGame) عن تنفيذ الفئات الملموسة. هذا يجعل newالمُعامل زائداً عن الحاجة، ويسمح بالالتزام بمبدأ الانفتاح والإغلاق، ويجعل المنتج النهائي أكثر مرونة في حالة التغيير.
أمثلة على التطبيقات
يعتمد هذا التطبيق بلغة C++23 على تطبيق ما قبل C++98 الموجود في كتاب أنماط التصميم . [ 5 ]
استيراد std ؛باستخدام std :: unique_ptr ؛// يحدد واجهة الكائنات التي تنشئها دالة المصنع. class Product { public : enum class Id : char { MINE , YOURS , };virtual void print () = 0 ; virtual ~ Product () = default ; };// تُنفّذ واجهة المنتج. class ConcreteProductMine : public Product { public : void print () { std :: println ( "this={} print MINE" , this ); } };// تُنفّذ واجهة المنتج. class ConcreteProductYours : public Product { public : void print () { std :: println ( "this={} print YOURS" , this ); } };// يُعلن عن دالة المصنع، التي تُعيد كائنًا من نوع Product. class Creator { public : virtual unique_ptr <Product> create ( Product :: Id id ) { switch ( id ) { case Product :: Id :: MINE : return std :: make_unique <ConcreteProductMine> ( ); case Product :: Id :: YOURS : return std :: make_unique <ConcreteProductYours> ( ); // كرر ذلك للمنتجات المتبقية } std :: unreachable () ; }virtual ~ Creator () = default ; };int main ( int argc , char * argv []) { unique_ptr < Creator > creator = std :: make_unique < Creator > (); unique_ptr < Product > product = creator -> create ( Product :: Id :: MINE ); product -> print ();product = creator -> create ( Product :: Id :: YOURS ); product -> print (); }مخرجات البرنامج تشبه
this = 0x6e5e90 اطبع "خاصتي" this = 0x6e62c0 اطبع "خاصتك"// مفردات فارغة للكائن الفعلي 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" ; } }نوع الشخص العام { ريفي ، حضري }/// <summary> /// تنفيذ المصنع - يُستخدم لإنشاء الكائنات. /// </summary> public class PersonFactory { public IPerson GetPerson ( PersonType type ) { switch ( type ) { case PersonType . Rural : return new Villager (); case PersonType . Urban : return new CityPerson (); default : throw new NotSupportedException (); } } }يوضح الكود أعلاه إنشاء واجهة تسمى IPersonوتطبيقين يسمى Villagerو CityPerson. بناءً على النوع المُمرر إلى PersonFactoryالكائن، يتم إرجاع الكائن الأصلي الملموس كواجهة IPerson.
إنّ طريقة المصنع هي مجرد إضافة إلى PersonFactoryالفئة. فهي تُنشئ كائنًا من الفئة من خلال الواجهات، ولكنها تسمح أيضًا للفئة الفرعية بتحديد الفئة التي سيتم إنشاء مثيل لها.
public interface IProduct { string GetName (); bool SetPrice ( double price ); }public class Phone : IProduct { private double _price ;public string GetName () { return "Apple TouchPad" ; }public bool SetPrice ( double price ) { _price = price ; return true ; } }// يشبه إلى حد كبير Factory، ولكنه يوفر إمكانية إضافية للتعامل مع الطريقة المُنشأة. public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct ();public IProduct GetObject () // تنفيذ طريقة المصنع. { return this . MakeProduct (); } }public class PhoneConcreteFactory : ProductAbstractFactory { protected override IProduct MakeProduct () { IProduct product = new Phone (); // تنفيذ إجراء ما على الكائن بعد استلامه product . SetPrice ( 20.30 ); return product ; } }في هذا المثال، MakeProductيُستخدم في concreteFactory. ونتيجةً لذلك، MakeProduct()يمكن استدعاء من أجل استرداده من IProduct. يمكن تشغيل منطق مخصص بعد الحصول على الكائن في طريقة المصنع الملموسة. GetObjectيتم جعل مجرد في واجهة المصنع.
هذا المثال بلغة جافا مشابه لأحد الأمثلة الموجودة في كتاب أنماط التصميم .
![]()
MazeGameيستخدم هذا الأسلوب القالب Room، لكنه يفوض مسؤولية إنشاء Roomالكائنات إلى فئاته الفرعية التي بدورها تنشئ الفئات الملموسة. يمكن لنمط اللعبة العادي استخدام هذا الأسلوب القالب:
استيراد java.util.ArrayList ؛ استيراد java.util.List ؛فئة مجردة Room { دالة مجردة connect ( Room room ); }class MagicRoom extends Room { public void connect ( Room room ) {} }class OrdinaryRoom extends Room { public void connect ( Room room ) {} }فئة مجردة MazeGame { قائمة خاصة نهائية < غرفة > غرف = قائمة جديدة <> ();public MazeGame ( ) { Room room1 = makeRoom ( ); Room room2 = makeRoom ( ); room1.connect ( room2 ) ; rooms.add ( room1 ) ; rooms.add ( room2 ) ; }abstract protected Room makeRoom (); }الدالة MazeGameالبانية هي دالة نموذجية تُضيف بعض المنطق المشترك. وهي تُشير إلى makeRoom()دالة المصنع التي تُغلف عملية إنشاء الغرف بحيث يُمكن استخدام غرف أخرى في فئة فرعية. ولتنفيذ نمط اللعبة الآخر الذي يحتوي على غرف سحرية، makeRoomيُمكن إعادة تعريف هذه الدالة.
class MagicMazeGame extends MazeGame { @Override protected MagicRoom makeRoom () { return new MagicRoom (); } }class OrdinaryMazeGame extends MazeGame { @Override protected OrdinaryRoom makeRoom () { return new OrdinaryRoom (); } }MazeGame normalGame = new OrdinaryMazeGame (); MazeGame magicGame = new MagicMazeGame ();يُظهر مثال PHP هذا تطبيقات الواجهات بدلاً من التوريث (مع ذلك، يمكن تحقيق نفس النتيجة من خلال التوريث). كما يمكن تعريف دالة المصنع publicواستدعاؤها مباشرةً من قِبل كود العميل (على عكس مثال Java السابق).
/* واجهات المصنع والسيارة */واجهة مصنع السيارات { دالة عامة makeCar () : سيارة ; }واجهة السيارة { دالة عامة getType () : سلسلة نصية ؛ }/* تطبيقات عملية للمصنع والسيارة */class SedanFactory implements CarFactory { public function makeCar () : Car { return new Sedan (); } }class Sedan implements Car { public function getType () : string { return 'Sedan' ; } }/* عميل */$factory = new SedanFactory (); $car = $factory- > makeCar (); print $car- > getType ();يستخدم مثال بايثون هذا نفس الأسلوب الذي استخدمه مثال جافا السابق.
from abc import ABC , abstractmethodclass 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 ( f " Playing using { self.rooms [ 0 ] } " ) ;@abstractmethod def make_room ( self ): raise NotImplementedError ( "يجب عليك تنفيذ هذا!" )class MagicMazeGame ( MazeGame ): def make_room ( self ) -> "MagicRoom" : return MagicRoom ()class OrdinaryMazeGame ( MazeGame ): def make_room ( self ) -> "OrdinaryRoom" : return OrdinaryRoom ()class Room ( ABC ): def __init__ ( self ) -> None : self . connected_rooms = []دالة الاتصال ( self , room : " Room " ) - > None : self.connected_rooms.append ( room )class MagicRoom ( Room ): def __str__ ( self ) -> str : return "الغرفة السحرية"class OrdinaryRoom ( Room ): def __str__ ( self ) -> str : return "غرفة عادية"normalGame = OrdinaryMazeGame ( ) normalGame.play ( )magicGame = MagicMazeGame ( ) magicGame.play ( )الاستخدامات
- في ADO.NET ، يعد IDbCommand.CreateParameter مثالاً على استخدام طريقة المصنع لربط التسلسلات الهرمية للفئات المتوازية.
- في Qt ، QMainWindow::createPopupMenu المؤرشف في 2015-07-19 في Wayback Machine هو أسلوب مصنع تم تعريفه في إطار عمل ويمكن تجاوزه في كود التطبيق .
- في لغة جافا ، يتم استخدام العديد من المصانع في حزمة javax.xml.parsers ، مثل javax.xml.parsers.DocumentBuilderFactory أو javax.xml.parsers.SAXParserFactory.
- في واجهة برمجة تطبيقات HTML5 DOM ، تحتوي واجهة Document على طريقة مصنع createElement() لإنشاء عناصر محددة من واجهة HTMLElement.
انظر أيضاً
- أنماط التصميم ، الكتاب المؤثر للغاية
- نمط التصميم ، نظرة عامة على أنماط التصميم بشكل عام
- نمط المصنع المجرد ، وهو نمط يتم تنفيذه غالبًا باستخدام أساليب المصنع.
- نمط البناء ، وهو نمط إبداعي آخر
- نمط أسلوب القالب ، والذي قد يستدعي أساليب المصنع
- فكرة جوشوا بلوخ عن طريقة المصنع الثابتة التي يدعي بلوخ أنه لا يوجد لها مكافئ مباشر في أنماط التصميم .
ملحوظات
- ^ جاما وآخرون. 1995 ، ص. 107.
- ↑ غاما، إريك ؛ هيلم، ريتشارد ؛ جونسون، رالف ؛ فليسيدس، جون (1995). أنماط التصميم: عناصر البرمجيات القابلة لإعادة الاستخدام والموجهة للكائنات . أديسون-ويسلي. ISBN 0-201-63361-2.
- ↑ فريمان، إريك؛ روبسون، إليزابيث؛ سييرا، كاثي؛ بيتس، بيرت (2004). هندريكسون، مايك؛ لوكيدس، مايك (محررون). أنماط التصميم من Head First: دليل سهل الفهم (غلاف ورقي) . المجلد 1 (الطبعة الأولى ). دار نشر أورايلي ميديا. ص 162. ISBN 978-0-596-00712-6تم الاطلاع عليه بتاريخ 12-09-2012 .
- ↑ "نمط تصميم طريقة المصنع - الهيكلة والتعاون" . w3sDesign.com . تم الاطلاع عليه بتاريخ 12 أغسطس 2017 .
- ^ جاما وآخرون. 1995 ، ص. 122.
مراجع
- مارتن فاولر ؛ كينت بيك ؛ جون برانت ؛ ويليام أوبدايك ؛ دون روبرتس (يونيو 1999). إعادة هيكلة الكود: تحسين تصميم الكود الحالي . أديسون-ويسلي. ISBN 0-201-48567-2.
- كوكس، براد ج. (1986). البرمجة كائنية التوجه: منهج تطوري . أديسون-ويسلي. ISBN 978-0-201-10393-9.
- كوهين، تال؛ جيل، جوزيف (2007). "بناء أفضل باستخدام المصانع" (ملف PDF) . مجلة تكنولوجيا الكائنات . 6 (6). برتراند ماير : 103. doi : 10.5381/jot.2007.6.6.a3 . تاريخ الاسترجاع : 12 مارس 2007 .
روابط خارجية
- نمط تصميم المصنع ( مؤرشف بتاريخ 10 يناير 2018 في أرشيف الإنترنت ) - التنفيذ بلغة جافا
- أسلوب المصنع في لغة النمذجة الموحدة (UML) وفي LePUS3 (لغة وصف التصميم)
- انظر إلى أساليب المصنع الثابتة لجوشوا بلوخ
- أنماط تصميم البرمجيات
- طريقة (برمجة الحاسوب)
