OOP في الصور

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



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

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

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

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

الطبقات والكائنات


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

أبسط تفسير: الفئة عبارة عن رسم لمحول ، ومثيلات هذه الفئة محولات محددة ، على سبيل المثال ، Optimus Prime أو Oleg. وعلى الرغم من تجميعها وفقًا لرسم واحد ، إلا أنه يمكنهم المشي وتحويل وإطلاق النار بنفس الطريقة ، فإن كلاهما لهما حالة فريدة خاصة بهما. الحالة عبارة عن سلسلة من الخصائص المتغيرة. لذلك ، في كائنين مختلفين من نفس الفئة ، يمكننا أن نلاحظ اسمًا مختلفًا ، والعمر ، والموقع ، ومستوى الشحن ، وكمية الذخيرة ، وما إلى ذلك. يتم وصف وجود هذه الخصائص وأنواعها في الفصل.

وبالتالي ، فإن الفصل هو وصف للخصائص والسلوكيات التي سوف يمتلكها الكائن. والكائن هو مثيل مع حالته الخاصة من هذه الخصائص.

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

في المجموع ، لدينا الأساليب والخصائص التي هي سمات. كيفية العمل مع السمات؟ في معظم PLs ، المشغل المرجعي للسمة هو النقطة (باستثناء PHP و Perl). يبدو مثل هذا (الرمز الزائف):

//       class class Transformer(){ //   x int x //    (     0) function constructor(int x){ //   x // (  0    ) this.x = x } //   run function run(){ //      this this.x += 1 } } //    : //        0 optimus = new Transformer(0) optimus.run() //    print optimus.x //  1 optimus.run() //      print optimus.x //  2 

في الصور سأستخدم الترميز التالي:



لم أستخدم مخططات UML ، معتبرة أنها غير مرئية بدرجة كافية ، وإن كانت أكثر مرونة.


رقم الرسوم المتحركة 1

ماذا نرى من الكود؟

1. هذا هو متغير محلي خاص (داخل الأساليب) يسمح لكائن ما بالوصول إلى سماته الخاصة من طرقه. أود أن ألفت انتباهك إلى أنه فقط خاصتك ، أي عندما يتصل المحول بطريقته الخاصة ، أو يغير حالته الخاصة. إذا بدت المكالمة كما هي في الخارج: optimus.x ، ثم من الداخل ، إذا أراد Optimus الوصول إلى حقله x بنفسه ، فستبدو المكالمة في هذه الطريقة مثل this.x ، أي " I (Optimus) تشير إلى السمة x ". في معظم اللغات ، يُطلق على هذا المتغير هذا ، ولكن هناك استثناءات (مثل الذات)

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

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

4. يعمل المُنشئ وطرق التشغيل مع الحالة الداخلية ، لكن في جميع النواحي الأخرى لا تختلف عن الوظائف العادية . حتى بناء جملة يطابق الإعلان.

5. قد تمتلك الفصول طرقًا لا تحتاج إلى حالة ، ونتيجة لذلك ، تنشئ كائنًا. في هذه الحالة ، تكون الطريقة ثابتة .

SRP


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

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

رابطة


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

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

 class Gun(){ //    int ammo_count //    function constructor(){ //  this.reload() //    "" } function fire(){ //    "" this.ammo_count -= 1 //      } function reload(){ //   "" this.ammo_count = 10 //     } } class Transformer(){ //    Gun gun_left //   " "   Gun gun_right //   " "    /*            ,    */ function constructor(Gun gun_left, Gun gun_right){ this.gun_left = gun_left //      this.gun_right = gun_right //      } //    "",   ... function fire(){ //  ,    "" this.gun_left.fire() //    ,     "" this.gun_right.fire() } } gun1 = new Gun() //    gun2 = new Gun() //    optimus = new Transformer(gun1, gun2) //  ,     


رقم الرسوم المتحركة 2

this.gun_left.fire () و this.gun_right.fire () عبارة عن استدعاءات لكائنات تابعة تحدث أيضًا من خلال النقاط. في النقطة الأولى ، ننتقل إلى سمة أنفسنا (this.gun_right) ، والحصول على كائن البندقية ، وعند النقطة الثانية ، ننتقل إلى طريقة كائن البندقية (this.gun_right.fire ()).

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

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

2. التجميع - هو الحالة التي يتم فيها إصدار السلاح كسلاح في يده ، وبعد وفاة Optimus ، يمكن أن يلتقطها رفيقه أوليغ ، ثم يتم تسليمها بيده أو تسليمها إلى مرهن. أي أن دورة حياة كائن تابع لا تعتمد على دورة حياة الأصل ، ويمكن استخدامها من قبل كائنات أخرى.

تبشرنا الكنيسة الأرثوذكسية OOP بثلاثية أساسية - التغليف ، تعدد الأشكال والميراث ، والتي يقوم عليها النهج الموجه نحو الكائن بأكمله. دعونا فرزها بالترتيب.



ميراث


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

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

يعد كل من Optimus Prime و Megatron من المحولات ، لكن أحدهما Autobot والآخر Decepticon. لنفترض أن الاختلافات بين Autobots و Decepticons ستتألف فقط من حقيقة أن Autobots تتحول إلى سيارات ، و Decepticons - إلى طيران. جميع الخصائص والسلوكيات الأخرى لن تحدث أي فرق. في هذه الحالة ، يمكن تصميم نظام الوراثة على النحو التالي: سيتم وصف الميزات الشائعة (الجري ، إطلاق النار) في الفئة الأساسية للمحولات ، والاختلافات (التحويل) في فئتي الأطفال التابعين Autobot و Decepticon.

 class Transformer(){ //   function run(){ // ,    } function fire(){ // ,    } } class Autobot(Transformer){ //  ,   Transformer function transform(){ // ,      } } class Decepticon(Transformer){ //  ,   Transformer function transform(){ // ,      } } optimus = new Autobot() megatron = new Decepticon() 


رقم الرسوم المتحركة 3

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

الزائد


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

استخدام غير مناسب


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

في وصف العلاقات بين كيانين ، كيف يمكن للمرء أن يحدد متى يكون الميراث مناسبًا ، ومتى يكون التكوين مناسبًا؟ يمكنك استخدام ورقة الغش الشعبية: اسأل نفسك ، الكيان أ هو الكيان ب ؟ إذا كان الأمر كذلك ، فمن المرجح أن الميراث المناسب. إذا كان الكيان A جزءًا من الكيان B ، فإن اختيارنا هو التكوين.

فيما يتعلق بوضعنا ، سوف يبدو مثل هذا:

  1. هو محرر الاخبار المحولات؟ نعم ، ثم نختار الميراث.
  2. هل المسدس جزء من المحول؟ نعم ، هذا يعني التكوين.

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

الميراث ثابت


هناك اختلاف مهم آخر بين الميراث والتكوين وهو أن الميراث ثابت في الطبيعة ولا ينشئ العلاقات الطبقية إلا في مرحلة التفسير / التجميع. يتيح لك Composition ، كما رأينا في الأمثلة ، تغيير علاقة الكيانات أثناء الانتقال مباشرة في وقت التشغيل - وهذا أمر مهم للغاية في بعض الأحيان ، لذلك عليك أن تتذكر هذا عند اختيار العلاقات (ما لم تكن هناك بالطبع رغبة في استخدام metaprogramming ).

وراثة متعددة


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



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

فصول مجردة


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

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

تعدد الأشكال


تعدد الأشكال هو خاصية نظام تتيح لك الحصول على العديد من تطبيقات واجهة واحدة. لا يوجد شيء واضح. دعنا ننتقل إلى المحولات.

لنفترض أن لدينا ثلاثة محولات: Optimus و Megatron و Oleg. المحولات هي قتال ، لذلك لديهم طريقة الهجوم (). يخبر اللاعب ، من خلال الضغط على زر "قتال" على عصا التحكم ، اللعبة باستدعاء طريقة الهجوم () على المحول الذي يلعب اللاعب من أجله. ولكن نظرًا لأن المحولات مختلفة ، واللعبة مثيرة للاهتمام ، فكل واحد منهم سيهاجم بطريقة ما. دعنا نقول Optimus هو كائن من فئة Autobot ، وتم تجهيز Autobots مع المدافع برؤوس حربية البلوتونيوم (نعم ، محبي المحولات ليسوا غاضبين). Megatron هو Decepticon ، ويطلق النار من بندقية البلازما. أوليغ لاعب باس ، ويطلق عليه أسماء. وما هي الفائدة؟

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

التغليف


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

في العديد من اللغات ، تشفير البيانات هو جزء من التغليف. لهذا ، توجد معدّلات وصول (نصف تلك الموجودة بجميع لغات OOP تقريبًا):

  • publi - يمكن لأي شخص الوصول إلى السمة
  • خاصة - طرق فقط من هذه الفئة يمكن الوصول إلى السمة
  • محمي - مثلما هو خاص ، فقط ورثة الطبقة يمكنهم الوصول ، بما في ذلك

 class Transformer(){ public function constructor(){ } protected function setup(){ } private function dance(){ } } 

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

المساعدون


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

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

واجهات


تتمثل مهمة الواجهة في تقليل مستوى اعتماد الكيانات على بعضها البعض ، وإضافة المزيد من التجريد.

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

عادةً ، لا تحتوي اللغات التي لها واجهات على وراثة فئة متعددة ، ولكن هناك توارث متعددة للواجهة. يسمح هذا للفئة بسرد الواجهات التي تتعهد بتنفيذها.

تتكون الفئات التي تحتوي على واجهات من علاقة أطراف بأطراف: يمكن لفئة واحدة تنفيذ واجهات متعددة ، ويمكن تنفيذ كل واجهة ، في المقابل ، بواسطة العديد من الفئات.

واجهة لديه استخدام على الوجهين:

  1. على جانب واحد من الواجهة توجد فئات تنفذ هذه الواجهة.
  2. على الجانب الآخر ، المستهلكون الذين يستخدمون هذه الواجهة كوصف لنوع البيانات التي يعملون معها (المستهلكون).

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

تخيل أن إطار المحول مزود بثلاث فتحات: فتحة للأسلحة ، ومولد للطاقة ، ونوع من الماسح الضوئي. تحتوي هذه الفتحات على واجهات معينة: يمكن تثبيت المعدات المناسبة فقط في كل فتحة. يمكنك تثبيت قاذفة صواريخ أو مدفع ليزر في الفتحة المخصصة للأسلحة أو مفاعل نووي أو RTG (مولد كهربائي حراري للنظائر المشعة) في الفتحة الخاصة بمولد الطاقة ورادار أو غطاء في الفتحة الخاصة بالماسح الضوئي. خلاصة القول هي أن كل فتحة لها واجهة اتصال عالمية ، ويجب أن تتوافق أجهزة معينة مع هذه الواجهة. على سبيل المثال ، يتم استخدام عدة أنواع من الفتحات على اللوحات الأم: تتيح لك فتحة المعالج توصيل المعالجات المختلفة المناسبة لهذا المقبس ، وفتحة SATA تسمح لك بتوصيل أي محرك أقراص SSD أو HDD أو حتى CD / DVD.

أود أن ألفت انتباهكم إلى حقيقة أن نظام فتحات الناتجة عن المحولات هو مثال على استخدام التكوين. إذا كانت المعدات الموجودة في الفتحات قابلة للاستبدال أثناء حياة المحول ، فسيكون هذا تجميعًا بالفعل. , , , «» : IWeapon, IEnergyGenerator, IScanner.

 //  : interface IWeapon{ function fire() {} //    .   } interface IEnergyGenerator{ //    ,     : function generate_energy() {} //  function load_fuel() {} //  } interface IScanner{ function scan() {} } // ,  : class RocketLauncher() : IWeapon { function fire(){ //    } } class LaserGun() : IWeapon { function fire(){ //    } } class NuclearReactor() : IEnergyGenerator { function generate_energy(){ //      } function load_fuel(){ //     } } class RITEG() : IEnergyGenerator { function generate_energy(){ //     } function load_fuel(){ //   - } } class Radar() : IScanner { function scan(){ //    } } class Lidar() : IScanner { function scan(){ //     } } //  - : class Transformer() { // , : IWeapon slot_weapon //      . IEnergyGenerator slot_energy_generator //     , IScanner slot_scanner //     /*         ,      ,   : */ function install_weapon(IWeapon weapon){ this.slot_weapon = weapon } function install_energy_generator(IEnergyGenerator energy_generator){ this.slot_energy_generator = energy_generator } function install_scanner(IScanner scanner){ this.slot_scanner = scanner } } //   class TransformerFactory(){ function build_some_transformer() { transformer = new Transformer() laser_gun = new LaserGun() nuclear_reactor = new NuclearReactor() radar = new Radar() transformer.install_weapon(laser_gun) transformer.install_energy_generator(nuclear_reactor) transformer.install_scanner(radar) return transformer } } //  transformer_factory = new TransformerFactory() oleg = transformer_factory.build_some_transformer() 


№4

, , , .

- . , : () Transformer, - () ( Radar, RocketLauncher, NuclearReactor . .)

, . , , , , .


, , : - , , , , — .

, : - , , . , .

, , , . , ! , . , :



ISP

(Interface Segregation Principle / / SOLID) . , , .


. , , - (, , ). : , , . , , . , , .


:
— , .

, . ? . , — . , , . , , , , .

:

  1. , , , ( )
  2. , , , , . ( )



, , . - , - — . , . — . , . . — .

«-». , , .

. , . , , .

. ( , , ), . , .

. ( , ). , . , , - .

. , , . , . , , ! , . , .



, , , . , . , , , .




, , , . , , , , , , .

, , , . , , , . ! , .

— , , . , , , . , , .

— . , « », .

استنتاج


class -. (, , . .), , . - . , .

. , , , . « — » « ».

انا جاد , , . . .

, , , . .

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


All Articles