طريقة المصنع ومصنع الملخص في الكون سويفت و iOS

تعتبر كلمة "مصنع" واحدة من أكثر الكلمات شيوعًا التي يستخدمها المبرمجون عند مناقشة برامجهم (أو غيرها). لكن المعنى المضمن فيه يمكن أن يكون مختلفًا تمامًا: يمكن أن يكون فئة تولد كائنات (متعددة الأشكال أو لا) ؛ وطريقة تنشئ مثيلات من أي نوع (ثابتة أم لا) ؛ يحدث ذلك ، وحتى مجرد أي طريقة توليد (بما في ذلك الصانعين ).

بالطبع ، ليس كل شيء يولد مثيلات أي شيء يمكن أن يطلق عليه كلمة "مصنع". علاوة على ذلك ، يمكن إخفاء نمطين مختلفين من ترسانة عصابة الأربعة - "طريقة المصنع" و "المصنع التجريدي" ، الذي أود الخوض في تفاصيله ، مع إيلاء اهتمام خاص لفهمهم الكلاسيكي وتنفيذه.

استلهمتني من كتابة هذا المقال لجوشوا كيريفسكي (رئيس "المنطق الصناعي" ) ، أو بالأحرى كتابه "إعادة تشكيل الأنماط" ، الذي نُشر في بداية القرن كجزء من سلسلة من الكتب التي أسسها مارتن فاولر (مؤلف مشهور لكتاب البرمجة الكلاسيكية الحديثة - كتاب " إعادة بيع المباني " ). إذا كان شخص ما لم يقرأ أو حتى سمع الأول (وأنا أعرف الكثير من هؤلاء) ، فتأكد من إضافته إلى قائمة القراءة الخاصة بك. هذا تكملة تستحق كل من إعادة بيع الكتب وكتاب أكثر كلاسيكية ، تقنيات تصميم الهدف. أنماط التصميم . "

يحتوي الكتاب ، من بين أشياء أخرى ، على عشرات الوصفات للتخلص من "الروائح" المختلفة في الكود باستخدام أنماط التصميم . بما في ذلك ثلاثة (على الأقل) "وصفات" حول الموضوع قيد المناقشة.

مصنع مجردة


يقدم Kerivsky في كتابه حالتين حيث يكون استخدام هذا القالب مفيدًا.

الأول هو تغليف المعرفة حول فئات محددة متصلة بواسطة واجهة مشتركة. في هذه الحالة ، فقط النوع الذي هو المصنع سيكون لديه هذه المعرفة. ستتألف واجهة برمجة التطبيقات العامة للمصنع من مجموعة من الأساليب (ثابتة أم لا) التي تعرض مثيلات من نوع واجهة شائعة ولديها بعض أسماء "الحديث" (من أجل فهم الطريقة التي يجب استدعاؤها لغرض معين).

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

لتكون أقرب إلى موضوع التطوير ضمن "iOS" ، فمن الملائم ممارسة الفئات الفرعية من UIViewController . في الواقع ، يعد هذا بالتأكيد أحد أكثر الأنواع شيوعًا في تطوير "iOS" ، فهو دائمًا "موروث" دائمًا قبل الاستخدام ، وغالبًا ما لا تكون فئة فرعية معينة مهمة حتى لرمز العميل.
سأحاول الإبقاء على أمثلة الكود أقرب ما يمكن للتطبيق الكلاسيكي من كتاب Gang of Four ، ولكن في الواقع الفعلي غالباً ما يتم تبسيط الكود بطريقة أو بأخرى. وفهم كافٍ للقالب فقط يفتح الباب أمام استخدامه مجانًا.

مثال مفصل


لنفترض أننا نتاجر في تطبيق ما ، وأن التعيين يعتمد على نوع السيارة المحددة: سنستخدم فئات فرعية مختلفة من UIViewController للمركبات المختلفة. بالإضافة إلى ذلك ، تختلف جميع المركبات في الحالة (الجديدة والمستعملة):

 enum VehicleCondition{ case new case used } final class BicycleViewController: UIViewController { private let condition: VehicleCondition init(condition: VehicleCondition) { self.condition = condition super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("BicycleViewController: init(coder:) has not been implemented.") } } final class ScooterViewController: UIViewController { private let condition: VehicleCondition init(condition: VehicleCondition) { self.condition = condition super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("ScooterViewController: init(coder:) has not been implemented.") } } 

وبالتالي ، لدينا مجموعة من الكائنات من مجموعة واحدة ، حيث يتم إنشاء أنواع من الأنواع في نفس الأماكن اعتمادًا على حالة معينة (على سبيل المثال ، نقر المستخدم على منتج في القائمة ، واعتمادًا على ما إذا كان سكوترًا أو دراجة هوائية ، فإننا إنشاء وحدة تحكم مناسبة). لدى صانعي أجهزة التحكم بعض المعلمات التي يجب أيضًا تعيينها في كل مرة. هل هاتان الوسيطتان تؤيدان إنشاء "مصنع" يتمتع وحده بمعرفة منطق إنشاء وحدة التحكم الصحيحة؟

بالطبع ، المثال بسيط للغاية ، وفي مشروع حقيقي في حالة مماثلة ، سيكون إدخال "مصنع" صريحًا "هندسة مفرطة" . ومع ذلك ، إذا تخيلنا أنه ليس لدينا نوعان من المركبات ، وأن المصممين لديهم أكثر من معلمة واحدة ، فإن مزايا "المصنع" ستصبح أكثر وضوحًا.

لذلك ، دعنا نعلن عن واجهة تلعب دور "المصنع التجريدي":

 protocol VehicleViewControllerFactory { func makeBicycleViewController() -> UIViewController func makeScooterViewController() -> UIViewController } 

(يوصي "دليل" قصير إلى حد ما بتصميم "API" في Swift باستدعاء أساليب المصنع بدءًا من الكلمة make.)

(يوجد مثال في كتاب العصابات الأربعة في "C ++" ويستند إلى الميراث والوظائف "الافتراضية" . باستخدام "Swift" ، بالطبع ، نموذج البرمجة الموجهة للبروتوكول أقرب إلينا.)

تحتوي واجهة المصنع التجريدية على طريقتين فقط: لإنشاء وحدات تحكم لبيع الدراجات والدراجات البخارية. تُرجع الطرق مثيلات ليس لفئات فرعية محددة ، ولكن لفئة أساسية شائعة. وبالتالي ، يقتصر نطاق المعرفة حول أنواع محددة على المجال الذي هو ضروري حقا.

بوصفنا "مصانع خرسانية" ، سوف نستخدم تطبيقين لواجهة المصنع المجردة:

 struct NewVehicleViewControllerFactory: VehicleViewControllerFactory { func makeBicycleViewController() -> UIViewController { return BicycleViewController(condition: .new) } func makeScooterViewController() -> UIViewController { return ScooterViewController(condition: .new) } } struct UsedVehicleViewControllerFactory: VehicleViewControllerFactory { func makeBicycleViewController() -> UIViewController { return BicycleViewController(condition: .used) } func makeScooterViewController() -> UIViewController { return ScooterViewController(condition: .used) } } 

في هذه الحالة ، كما يتضح من الكود ، تكون المصانع المحددة مسؤولة عن المركبات ذات الظروف المختلفة (الجديدة والمستعملة).

سيبدو إنشاء وحدة التحكم الصحيحة الآن كالتالي:

 let factory: VehicleViewControllerFactory = NewVehicleViewControllerFactory() let vc = factory.makeBicycleViewController() 

الطبقات تغليف المصنع


انتقل الآن لفترة وجيزة إلى حالات الاستخدام التي يقدمها كيريفسكي في كتابه.

تتعلق الحالة الأولى بتغليف فئات محددة . على سبيل المثال ، خذ نفس وحدات التحكم لعرض البيانات حول المركبات:

 final class BicycleViewController: UIViewController { } final class ScooterViewController: UIViewController { } 

افترض أننا نتعامل مع وحدة نمطية منفصلة ، على سبيل المثال ، مكتبة المكونات الإضافية. في هذه الحالة ، تظل الفئات المعلنة أعلاه internal (بشكل افتراضي) ، ويعمل المصنع كـ "واجهة برمجة التطبيقات" العامة للمكتبة ، والتي تُرجع في أساليبها الفئات الأساسية لوحدات التحكم ، مما يترك معرفة حول فئات فرعية محددة داخل المكتبة:

 public struct VehicleViewControllerFactory { func makeBicycleViewController() -> UIViewController { return BicycleViewController() } func makeScooterViewController() -> UIViewController { return ScooterViewController() } } 

نقل المعرفة حول إنشاء كائن داخل المصنع


تصف "الحالة" الثانية التهيئة المعقدة للكائن ، ويقترح Kerivsky ، كواحدة من الطرق لتبسيط التعليمات البرمجية وحماية مبادئ التغليف ، تقييد تقييد المعرفة حول عملية التهيئة خارج المصنع.

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

 enum Condition { case new case used } enum EngineType { case diesel case gas } struct Engine { let type: EngineType } enum TransmissionType { case automatic case manual } final class CarViewController: UIViewController { private let condition: Condition private let engine: Engine private let transmission: TransmissionType private let wheelDiameter: Int init(engine: Engine, transmission: TransmissionType, wheelDiameter: Int = 16, condition: Condition = .new) { self.engine = engine self.transmission = transmission self.wheelDiameter = wheelDiameter self.condition = condition super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("CarViewController: init(coder:) has not been implemented.") } } 

مثال على تهيئة وحدة التحكم المقابلة:

 let engineType = EngineType.diesel let engine = Engine(type: engineType) let transmission = TransmissionType.automatic let wheelDiameter = 18 let vc = CarViewController(engine: engine, transmission: transmission, wheelDiameter: wheelDiameter) 

يمكننا وضع المسؤولية عن كل هذه "الأشياء الصغيرة" على "أكتاف" مصنع متخصص:

 struct UsedCarViewControllerFactory { let engineType: EngineType let transmissionType: TransmissionType let wheelDiameter: Int func makeCarViewController() -> UIViewController { let engine = Engine(type: engineType) return CarViewController(engine: engine, transmission: transmissionType, wheelDiameter: wheelDiameter, condition: .used) } } 

وإنشاء وحدة التحكم بهذه الطريقة:

 let factory = UsedCarViewControllerFactory(engineType: .gas, transmissionType: .manual, wheelDiameter: 17) let vc = factory.makeCarViewController() 

طريقة المصنع


يحتوي القالب "ذو الجذر الواحد" أيضًا على معلومات حول أنواع محددة تم إنشاؤها ، ولكن ليس عن طريق إخفاء هذه المعرفة داخل فئة متخصصة ، ولكن عن طريق تعدد الأشكال. يقدم Kerivsky في كتابه أمثلة بلغة Java ويقترح استخدام فصول مجردة ، ولكن سكان عالم Swift لا يعرفون هذا المفهوم. لدينا أجواءنا الخاصة هنا ... والبروتوكولات.
يشير كتاب "Gangs of Four" إلى أن القالب يُعرف أيضًا باسم "المُنشئ الظاهري" ، وهذا لم يعد هباء. في "C ++" ، تكون الظاهرية هي وظيفة يتم إعادة تعريفها في الفئات المشتقة. لا تمنح اللغة المصمم الفرصة ليعلن أنه ظاهري ، ومن الممكن أن تكون محاولة لتقليد السلوك المرغوب الذي أدى إلى اختراع هذا النمط.

خلق كائن متعدد الأشكال


كمثال كلاسيكي على فائدة القالب ، نأخذ بعين الاعتبار الحالة عندما يكون في الأنواع المختلفة في التسلسل الهرمي تطبيق متطابق لطريقة واحدة باستثناء الكائن الذي يتم إنشاؤه واستخدامه في هذه الطريقة . كحل ، يُقترح إنشاء هذا الكائن بطريقة منفصلة وتطبيقه بشكل منفصل ، ورفع الطريقة العامة أعلى في التسلسل الهرمي. وبالتالي ، ستستخدم الأنواع المختلفة التطبيق العام للطريقة ، وسيتم إنشاء الكائن المطلوب لهذه الطريقة بشكل متعدد الأشكال.

على سبيل المثال ، دعنا نعود إلى وحدات التحكم الخاصة بنا لعرض السيارات:

 final class BicycleViewController: UIViewController { } final class ScooterViewController: UIViewController { } 

لنفترض أن كيانًا ما يستخدم لعرضها ، على سبيل المثال ، منسق ، والذي يمثل وحدات التحكم هذه بشكل مشروط من وحدة تحكم أخرى:

 protocol Coordinator { var presentingViewController: UIViewController? { get set } func start() } 

يتم دائمًا استخدام طريقة start() بنفس الطريقة ، إلا أنها تنشئ وحدات تحكم مختلفة:

 final class BicycleCoordinator: Coordinator { weak var presentingViewController: UIViewController? func start() { let vc = BicycleViewController() presentingViewController?.present(vc, animated: true) } } final class ScooterCoordinator: Coordinator { weak var presentingViewController: UIViewController? func start() { let vc = ScooterViewController() presentingViewController?.present(vc, animated: true) } } 

الحل المقترح هو جعل إنشاء الكائن المستخدم بطريقة منفصلة:

 protocol Coordinator { var presentingViewController: UIViewController? { get set } func start() func makeViewController() -> UIViewController } 

والطريقة الرئيسية هي توفير التنفيذ الأساسي:

 extension Coordinator { func start() { let vc = makeViewController() presentingViewController?.present(vc, animated: true) } } 

سوف تتخذ أنواع محددة في هذه الحالة النموذج:

 final class BicycleCoordinator: Coordinator { weak var presentingViewController: UIViewController? func makeViewController() -> UIViewController { return BicycleViewController() } } final class ScooterCoordinator: Coordinator { weak var presentingViewController: UIViewController? func makeViewController() -> UIViewController { return ScooterViewController() } } 

استنتاج


حاولت تغطية هذا الموضوع البسيط من خلال الجمع بين ثلاثة أساليب:

  • الإعلان الكلاسيكي عن وجود حفل الاستقبال ، مستوحى من كتاب "Gang of Four" ؛
  • الدافع للاستخدام ، مستوحى بشكل علني من كتاب كيريفسكي ؛
  • التطبيق المطبق كمثال على صناعة البرمجة القريبة مني.

في الوقت نفسه ، حاولت أن أكون أقرب ما يمكن من هيكل الكتب المدرسية للقوالب ، إلى أقصى حد ممكن ، دون تدمير مبادئ النهج الحديث للتنمية لنظام iOS واستخدام قدرات لغة Swift (بدلاً من C ++ و Java الأكثر شيوعًا).

كما اتضح ، من الصعب جدًا العثور على مواد مفصلة حول الموضوع تحتوي على أمثلة مطبقة. تحتوي معظم المقالات والأدلة الموجودة على مراجعات سطحية وأمثلة مختصرة فقط ، تم بالفعل اقتطاعها تمامًا مقارنة بإصدارات الكتب المدرسية للتطبيقات.

آمل أن أتمكن جزئيًا على الأقل من تحقيق أهدافي والقارئ - على الأقل جزئيًا كان مهتمًا أو على الأقل فضولي لتعلم أو تحديث معرفتي حول هذا الموضوع.

موادي الأخرى على أنماط التصميم:


وهذا رابط لـ "Twitter" ، حيث أنشر روابط لمقالاتي وأكثر من ذلك بقليل.

Source: https://habr.com/ru/post/ar451324/


All Articles