نمط مصنع تجريدي

نمط المصنع المجرد في هندسة البرمجيات هو نمط تصميم يوفر طريقة لإنشاء مجموعات من الكائنات المترابطة دون فرض فئاتها الملموسة، وذلك بتغليف مجموعة من المصانع الفردية ذات سمة مشتركة دون تحديد فئاتها الملموسة. [ 1 ] وفقًا لهذا النمط، يقوم مكون برمجي للعميل بإنشاء تطبيق ملموس للمصنع المجرد، ثم يستخدم الواجهة العامة للمصنع لإنشاء الكائنات الملموسة التي تُشكل جزءًا من المجموعة. لا يعرف العميل أي الكائنات الملموسة التي يتلقاها من كل مصنع من هذه المصانع الداخلية، لأنه يستخدم فقط الواجهات العامة لمنتجاته. [ 1 ] يفصل هذا النمط تفاصيل تنفيذ مجموعة من الكائنات عن استخدامها العام، ويعتمد على تركيب الكائنات، حيث يتم تنفيذ إنشاء الكائنات في طرق مُتاحة في واجهة المصنع. [ 2 ]
يُمكّن استخدام هذا النمط من إمكانية تبديل التطبيقات الملموسة دون تغيير الشيفرة التي تستخدمها، حتى أثناء التشغيل . مع ذلك، قد يؤدي استخدام هذا النمط، كما هو الحال مع أنماط التصميم المشابهة ، إلى تعقيد غير ضروري وجهد إضافي في كتابة الشيفرة الأولية. إضافةً إلى ذلك، قد تؤدي مستويات الفصل والتجريد الأعلى إلى أنظمة يصعب تصحيح أخطائها وصيانتها.
ملخص
يُعد نمط تصميم المصنع المجرد أحد الأنماط الـ 23 الموصوفة في كتاب أنماط التصميم لعام 1994. ويمكن استخدامه لحل مشاكل مثل: [ 3 ]
- كيف يمكن لتطبيق ما أن يكون مستقلاً عن كيفية إنشاء كائناته؟
- كيف يمكن أن يكون الصنف مستقلاً عن كيفية إنشاء الكائنات التي يتطلبها؟
- كيف يمكن إنشاء عائلات من الكائنات المرتبطة أو التابعة؟
يُعدّ إنشاء الكائنات مباشرةً داخل الفئة التي تتطلبها عمليةً غير مرنة. إذ يُلزم ذلك الفئة بكائنات مُحددة، ويجعل من المستحيل تغيير عملية إنشاء الكائنات لاحقًا دون تعديل الفئة نفسها. كما يمنع ذلك إعادة استخدام الفئة في حال الحاجة إلى كائنات أخرى، ويُصعّب اختبارها لأنّه لا يُمكن استبدال الكائنات الحقيقية بكائنات وهمية.
المصنع هو موقع فئة محددة في الكود حيث تُنشأ الكائنات . يهدف تطبيق هذا النمط إلى عزل إنشاء الكائنات عن استخدامها، وإنشاء مجموعات من الكائنات ذات الصلة دون الاعتماد على فئاتها المحددة. [ 2 ] وهذا يسمح بإدخال أنواع مشتقة جديدة دون أي تغيير في الكود الذي يستخدم الفئة الأساسية .
يوضح هذا النمط كيفية حل مثل هذه المشكلات:
- قم بتغليف عملية إنشاء الكائنات في كائن منفصل (كائن مصنع) عن طريق تحديد وتنفيذ واجهة لإنشاء الكائنات.
- قم بتفويض عملية إنشاء الكائنات إلى كائن مصنع بدلاً من إنشاء الكائنات مباشرةً.
هذا يجعل الفئة مستقلة عن كيفية إنشاء كائناتها. يمكن تهيئة الفئة بكائن مصنع، والذي تستخدمه لإنشاء الكائنات، ويمكن استبدال كائن المصنع أثناء وقت التشغيل.
تعريف
يصف كتاب أنماط التصميم نمط المصنع المجرد بأنه "واجهة لإنشاء عائلات من الكائنات ذات الصلة أو التابعة دون تحديد فئاتها الملموسة". [ 4 ]
الاستخدام
يُحدد المصنع النوع الملموس للكائن المراد إنشاؤه، وهنا يتم إنشاء الكائن فعليًا. مع ذلك، لا يُعيد المصنع سوى مرجع (في جافا، على سبيل المثال، باستخدام عامل التشغيل new ) أو مؤشرًا من نوع مجرد إلى الكائن الملموس الذي تم إنشاؤه.
هذا يعزل كود العميل عن إنشاء الكائنات من خلال جعل العملاء يطلبون من كائن المصنع إنشاء كائن من النوع المجرد المطلوب وإرجاع مؤشر مجرد إلى الكائن. [ 5 ]
مثال على ذلك هو فئة مصنع مجردة DocumentCreatorتوفر واجهات لإنشاء عدد من المنتجات (مثلًا، createLetter()و createResume()). سيحتوي النظام على أي عدد من النسخ الملموسة المشتقة من DocumentCreatorالفئة، مثل FancyDocumentCreatorأو ModernDocumentCreator، لكل منها تطبيق مختلف لـ createLetter()، createResume()والتي ستنشئ كائنات مقابلة مثل FancyLetterأو ModernResume. كل منتج من هذه المنتجات مشتق من فئة مجردة بسيطة مثل Letterأو Resumeالتي يعرفها العميل. سيحصل كود العميل على نسخة مناسبة من ، DocumentCreatorويستدعي أساليب المصنع الخاصة بها . سيتم إنشاء كل كائن من الكائنات الناتجة من نفس DocumentCreatorالتطبيق، وستشترك في سمة مشتركة. يحتاج العميل فقط إلى معرفة كيفية التعامل مع الفئة المجردة Letterأو Resume، وليس النسخة المحددة التي أنشأها المصنع الملموس.
بما أن المصنع لا يُعيد سوى مرجع أو مؤشر إلى نوع مجرد، فإن كود العميل الذي طلب الكائن من المصنع لا يعلم - ولا يتحمل عبء - النوع الملموس الفعلي للكائن الذي تم إنشاؤه. مع ذلك، يعرف المصنع المجرد نوع الكائن الملموس (وبالتالي نوع المصنع الملموس). على سبيل المثال، قد يقرأ المصنع نوع الكائن من ملف تهيئة. لا يحتاج العميل إلى تحديد النوع، لأنه مُحدد مسبقًا في ملف التهيئة. وهذا يعني تحديدًا ما يلي:
- لا يمتلك كود العميل أي معرفة بالنوع الملموس ، فلا يحتاج إلى تضمين أي ملفات رأسية أو تعريفات فئات متعلقة به. يتعامل كود العميل فقط مع النوع المجرد. صحيح أن المصنع يُنشئ كائنات من نوع ملموس، لكن كود العميل لا يصل إلى هذه الكائنات إلا من خلال واجهاتها المجردة . [ 6 ]
- تتم إضافة أنواع ملموسة جديدة بتعديل كود العميل لاستخدام مصنع مختلف، وهو تعديل لا يتجاوز عادةً سطرًا واحدًا في ملف واحد. يقوم المصنع الجديد بإنشاء كائنات من نوع ملموس مختلف، ولكنه يُعيد مؤشرًا من نفس النوع المجرد السابق، مما يعزل كود العميل عن التغيير. يُعد هذا أسهل بكثير من تعديل كود العميل لإنشاء نوع جديد. يتطلب ذلك تغيير كل موقع في الكود حيث يتم إنشاء كائن جديد، بالإضافة إلى ضمان معرفة جميع هذه المواقع بالنوع الملموس الجديد، على سبيل المثال، عن طريق تضمين ملف رأس فئة ملموسة. إذا تم تخزين جميع كائنات المصنع بشكل عام في كائن أحادي النسخة ، ومرّ كود العميل عبر هذا الكائن للوصول إلى المصنع المناسب لإنشاء الكائنات، فإن تغيير المصانع يصبح بنفس سهولة تغيير الكائن الأحادي النسخة. [ 6 ]
بناء
مخطط UML

في مخطط فئات UML أعلاه ، لا تقوم الفئة التي تتطلب كائنات بإنشاء مثيلات من الفئتين و مباشرةً. بدلاً من ذلك، تشير إلى واجهة إنشاء الكائنات، مما يجعل مستقلة عن كيفية إنشاء الكائنات (أي الفئات الملموسة التي يتم إنشاء مثيلاتها). تُنفذ الفئة الواجهة من خلال إنشاء مثيلات من الفئتين و .ClientProductAProductBProductA1ProductB1ClientAbstractFactoryClientFactory1AbstractFactoryProductA1ProductB1
يُظهر مخطط تسلسل UML التفاعلات أثناء التشغيل. يستدعي الكائن الكائنَ ، الذي يُنشئ كائنًا ويعيده . بعد ذلك، يستدعي الكائنُ الكائنَ ، الذي يُنشئ كائنًا ويعيده .ClientcreateProductA()Factory1ProductA1ClientcreateProductB()Factory1ProductB1
المتغيرات
يعتمد الهيكل الأصلي لنمط المصنع المجرد، كما تم تعريفه في عام 1994 في كتاب أنماط التصميم ، على فئات مجردة للمصنع المجرد والمنتجات المجردة المراد إنشاؤها. أما المصانع والمنتجات الملموسة فهي فئات تخصص الفئات المجردة باستخدام الوراثة. [ 4 ]
يعتمد تصميم أحدث لهذا النمط على واجهات تُعرّف المصنع المجرد والمنتجات المجردة المراد إنشاؤها. يستخدم هذا التصميم الدعم الأصلي للواجهات أو البروتوكولات في لغات البرمجة الشائعة لتجنب الوراثة. في هذه الحالة، تكون المصانع والمنتجات الملموسة عبارة عن فئات تُحقق الواجهة من خلال تطبيقها. [ 1 ]
مثال
يعتمد هذا التطبيق C++23 على تطبيق ما قبل C++98 الموجود في الكتاب.
استيراد std ؛باستخدام std :: array ؛ باستخدام std :: shared_ptr ؛ باستخدام std :: unique_ptr ؛ باستخدام std :: vector ؛class MapSite { public : enum class Direction : char { NORTH , SOUTH , EAST , WEST , };virtual void enter () = 0 ; virtual ~ MapSite () = default ; };class Room : public MapSite { private : int roomNumber ; shared_ptr < array < MapSite , 4 >> sides ; public : explicit Room ( int n = 0 ) : roomNumber { n } {}~ Room () = default ;Room & setSide ( MapSite :: Direction d , MapSite & ms ) { sides [ std :: to_underlying ( d )] = std :: move ( ms ); std :: println ( "Room::setSide {} ms" , d ); return * this ; }virtual void enter () override = 0 ;Room ( const Room & ) = delete ; Room & operator = ( const Room & ) = delete ; };class Wall : public MapSite { public : explicit Wall ( int n = 0 ) : MapSite ( n ) {}~ Wall () = default ;void enter () override { // ... } };class Door : public MapSite { private : shared_ptr < Room > room1 ; shared_ptr < Room > room2 ; public : explicit Door ( int n = 0 , shared_ptr < Room > r1 = nullptr , shared_ptr < Room > r2 = nullptr ) : MapSite ( n ), room1 { std :: move ( r1 )}, room2 { std :: move ( r2 )} {}~ Door () = default ;void enter () override { // ... }Door ( const Door & ) = delete ; Door & operator = ( const Door & ) = delete ; };class Maze { private : vector < shared_ptr < Room >> rooms ; public : Maze () = default ; ~ Maze () = default ;Maze & addRoom ( shared_ptr < Room > r ) { std :: println ( "Maze::addRoom {}" , reinterpret_cast < void *> ( r . get ())); rooms . push_back ( std :: move ( r )); return * this ; } shared_ptr < Room > roomNo ( int n ) const { for ( const Room & r : rooms ) { // منطق البحث الفعلي هنا... } return nullptr ; } };class MazeFactory { public : MazeFactory () = default ;virtual ~ MazeFactory () = default ;[[ nodiscard ] ] unique_ptr <Maze> makeMaze ( ) const { return std :: make_unique <Maze> ( ) ; }[[ nodiscard ] ] shared_ptr <Wall> makeWall ( ) const { return std :: make_shared <Wall> ( ) ; }[ [ nodiscard ] ] shared_ptr <Room> makeRoom ( int n ) const { return std :: make_shared <Room> ( new Room ( n ) ) ; } [ [ nodiscard ] ] shared_ptr <Door> makeDoor ( shared_ptr <Room> r1 , shared_ptr <Room> r2 ) const { return std :: make_shared <Door> ( std :: move ( r1 ) , std :: move ( r2 ) ) ; } } ;// إذا تم تمرير كائن كمعامل إلى الدالة createMaze لإنشاء الغرف والجدران والأبواب، فيمكنك تغيير فئات الغرف والجدران والأبواب بتمرير معامل مختلف. هذا مثال على نمط المصنع المجرد (99).class MazeGame { public : Maze () = default ; ~ Maze () = default ;[ [ nodiscard ] ] unique_ptr <Maze> createMaze ( MazeFactory & factory ) { unique_ptr <Maze> maze = factory.makeMaze ( ) ; shared_ptr <Room> r1 = factory.makeRoom ( 1 ) ; shared_ptr <Room> r2 = factory.makeRoom ( 2 ) ; shared_ptr <Door> door = factory.makeDoor ( r1 , r2 ) ; maze- > addRoom ( r1 ) .addRoom ( r2 ) .setSide ( MapSite :: Direction :: NORTH , factory.makeWall ( ) ) . setSide ( MapSite :: Direction :: EAST , door ) .setSide ( MapSite :: Direction :: SOUTH , factory.makeWall ( ) ) . setSide ( MapSite :: Direction :: WEST , factory.makeWall ( ) ) .setSide ( MapSite :: الاتجاه :: الشمال , المصنع . makeWall ()) . setSide ( MapSite :: الاتجاه :: الشرق , المصنع . makeWall ()) . setSide ( MapSite :: الاتجاه :: الجنوب , المصنع . makeWall ()) . setSide ( MapSite :: الاتجاه :: الغرب ، الباب )؛ متاهة العودة ؛ } };int main ( int argc , char * argv []) { MazeGame game ; unique_ptr < Maze > maze = game . createMaze ( MazeFactory ()); }مخرجات البرنامج هي:
المتاهة::addRoom 0x1317ed0 المتاهة::addRoom 0x1317ef0 الغرفة::setSide 0 0x1318340 الغرفة::setSide 2 0x1317f10 الغرفة::setSide 1 0x1318360 الغرفة::setSide 3 0x1318380 الغرفة::setSide 0 0x13183a0 الغرفة :: setSide 2 0x13183c0 الغرفة :: setSide 1 0x13183e0 الغرفة :: setSide 3 0x1317f10انظر أيضاً
مراجع
- 1 2 3 فريمان، إريك؛ روبسون، إليزابيث؛ سييرا، كاثي؛ بيتس، بيرت (2004). هندريكسون، مايك؛ لوكيدس، مايك (محرران). أنماط التصميم من البداية (غلاف ورقي) . المجلد 1. أورايلي. ص 156. ISBN 978-0-596-00712-6تم الاطلاع عليه بتاريخ 12-09-2012 .
- 1 2 فريمان، إريك؛ روبسون، إليزابيث؛ سييرا، كاثي؛ بيتس، بيرت (2004). هندريكسون، مايك؛ لوكيدس، مايك (محررون). أنماط التصميم من البداية (غلاف ورقي) . المجلد 1. أورايلي. ص 162. ISBN 978-0-596-00712-6تم الاطلاع عليه بتاريخ 12-09-2012 .
- ↑ "نمط تصميم المصنع المجرد - المشكلة والحل والتطبيق" . w3sDesign.com . تم الاطلاع عليه بتاريخ 11 أغسطس 2017 .
- 1 2 غاما، إريك؛ ريتشارد هيلم؛ رالف جونسون؛ جون إم. فليسيدس (23-10-2009). "أنماط التصميم: المصنع المجرد" . informIT. مؤرشف من الأصل في 16-05-2012 . تم الاسترجاع في 16-05-2012 .
إنشاء الكائنات: المصنع المجرد: الغرض: توفير واجهة لإنشاء مجموعات من الكائنات ذات الصلة أو التابعة دون تحديد فئاتها الملموسة.
{{cite web}}: CS1 maint: bot: حالة عنوان URL الأصلي غير معروفة ( رابط ) - ↑ فينيمان، ديفيد (23 أكتوبر 2009). "تصميم الكائنات للمتحيرين" . مشروع الكود. مؤرشف من الأصل في 21 فبراير 2011. تم الاسترجاع في 16 مايو 2012.
يعزل المصنع العميل عن التغييرات التي تطرأ على المنتج أو كيفية إنشائه، ويمكنه توفير هذا العزل عبر الكائنات المشتقة من واجهات مجردة مختلفة تمامًا.
{{cite web}}: CS1 maint: bot: حالة عنوان URL الأصلي غير معروفة ( رابط ) - 1 2 "المصنع المجرد: التنفيذ" . OODesign.com . تم الاسترجاع في 16-05-2012 .
- "نمط تصميم المصنع المجرد - الهيكلة والتعاون" . w3sDesign.com . تم الاطلاع عليه بتاريخ 12 أغسطس 2017 .
- ↑ "نمط تصميم المصنع المجرد - الهيكلة والتعاون" . w3sDesign.com . تم الاطلاع عليه بتاريخ 12 أغسطس 2017 .
روابط خارجية
الوسائط المتعلقة بمصنع Abstract على ويكيميديا كومنز- المصنع المجرد مثال تنفيذي للمصنع المجرد
- أنماط تصميم البرمجيات
