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

OOP باختصار
ربما هذا هو الجزء الأكثر صعوبة بالنسبة لي. ولكن لا يزال ، تحتاج إلى تأسيس الأساس ووصف ما هو جوهر OOP بإيجاز شديد لفهم لماذا عززت مبادئ التغليف ، والتعدد ، والميراث ، و SOLID. وسوف أفعل ذلك من خلال الحديث عن كيف يمكنك التفكير في شيء من هذا القبيل.
بدأ الأمر بـ Dijkstra ، الذي أثبت أنه يمكن التعبير عن أي خوارزمية بثلاث طرق لتحديد الأمر التالي: التنفيذ الخطي (بالترتيب) ، والتفرع حسب الشرط ، وتنفيذ تنفيذ الحلقة أثناء استيفاء الشرط. باستخدام هذه الاتصالات الثلاثة ، يمكنك بناء أي خوارزمية. علاوة على ذلك ، تمت التوصية بكتابة البرامج ، بحيث تقتصر على الترتيب الخطي للأوامر واحدًا تلو الآخر ، المتفرعة والحلقات. وقد سمي هذا "البرمجة الهيكلية الإجرائية " (شكرا للتوضيح sshikov ).
هنا أيضًا نلاحظ أنه يجب دمج تسلسل الأوامر في برامج فرعية ويمكن تنفيذ كل برنامج فرعي باستخدام أمر واحد.
بالإضافة إلى ترتيب الإجراءات ، من المهم بالنسبة لنا أن يتم تنفيذ الإجراءات. ويتم تنفيذها على البيانات التي أصبحت معتادة لتخزينها في المتغيرات. متغيرات تخزين البيانات. يتم تفسيرها على أساس نوعها. من الواضح أن طحن الأسنان ، ولكن التحلي بالصبر قليلا ، من فضلك.
من البداية ، تم تشكيل مجموعة عامة أو أكثر من أنواع البيانات البدائية. أعداد صحيحة ، أرقام حقيقية ، متغيرات منطقية ، صفائف ، سلاسل. خوارزميات + بنيات البيانات = البرامج ، كما تم توريث Nicklaus Wirth.
أيضًا ، منذ بداية الوقت ، كان هناك أشكال مختلفة مثل نوع البيانات كبرنامج فرعي. أو قطعة من الكود إذا كنت تريد. قد يقول البعض أن استخدام البرامج الثانوية كمتغيرات يعد من صلاحيات البرمجة الوظيفية. على الرغم من ذلك ، فإن القدرة على عمل قطعة من التعليمات البرمجية المتغيرة هي حتى في المجمّع. اسمح لهذا الاحتمال بالحد من "حسنًا ، فإليك رقم البايت الموجود في ذاكرة الوصول العشوائي (RAM) حيث يعيش هذا الروتين الفرعي ، ثم أمر CALL مع تكدس المكالمة في الأسنان وتدور كما يمكنك".
بالطبع ، كانت عدة أنواع من البيانات قليلة وبدأ الناس يفكرون في إضافة القدرة على إنشاء أنواع خاصة بهم إلى مختلف PLs. كان أحد الاختلافات في هذه الميزة ما يسمى التسجيلات. فيما يلي مثالان للتسجيل بلغة برمجة غير موجودة (يشار إليها فيما يلي بـ NEPL - لغة برمجة غير موجودة):
type Name: record consisting of FirstName: String, MiddleName: String, LastName: String. type Point: record consisting of X: Double, Y: Double.
أي أنه بدلاً من السحب على اثنين أو ثلاثة من المتغيرات ذات الصلة ، يمكنك تجميعها في بنية واحدة مع الحقول المسماة. ثم تقوم بتعريف متغير نوع الاسم والرجوع إلى حقل الاسم الأول ، على سبيل المثال.
ما قيمة هذا المتغير "المحسّن" في موضوعنا؟ من هنا لا يوجد سوى خطوة صغيرة واحدة إلى OOP. ليس فقط أنني أبرزت فقرة غامقة للإشارة إلى أنه يمكن أيضًا وضع أجزاء من التعليمات البرمجية في المتغيرات. انظر كيف تتحول المتغيرات إلى كائنات:
type Name: class consisting of FirstName: String, MiddleName: String, LastName: String, GetFullName: subprogram with no parameters returns String. type Point: class consisting of X: Double, Y: Double, ScalarMultiply: subprogram with (Double) parameters returns Point.
NB NEPL تتطور بنشاط واستبدلت بالفعل الكلمة الأساسية للتسجيل مع فئة.
وهذا يعني أنه يمكننا الوصول إلى حقل "GetFullName" واستدعائه. لا يحتوي المتغير على بيانات تصف حالته فحسب ، بل يتضمن أيضًا سلوكًا. وبالتالي ، يتحول المتغير إلى كائن لديه بعض المهارات والدولة. ونحن نعمل بالفعل ليس فقط مع المتغيرات ، ولكن مع أنظمة صغيرة يمكن إعطاء الأوامر.
في شبابي ، فتنت هذه الفكرة لي. مجرد التفكير ، يمكنك إنشاء أي نوع من البيانات . وأنت لا تعمل مع بعض الأرقام ، ولكن مع كائنات العالم الذي تقوم بإنشائه. لا عذاب مع صفائف مملة أو مجموعات معقدة من الأرقام. ونحن نعمل مباشرة مع كائنات أنواع لاعب ، العدو ، رصاصة ، بوس! نعم ، في شبابي كنت أرغب في إنشاء ألعاب فيديو.
في الواقع ، تبين أن كل شيء ليس بهذه البساطة. وبدون بعض الأفكار "المعززة" ، فإن OOP ستحول حياة المبرمج إلى جحيم. ولكن قبل الانتقال ، دعنا نعطي بعض المصطلحات الأخرى:
- تسمى أنواع البيانات "المعززة" بسلوكها في OOP بالفئات .
- تسمى متغيرات هذه الأنواع كائنات .
- وتسمى الإجراءات التي تحدد سلوك الكائنات الأساليب . كقاعدة عامة ، كل فئة لديها مجموعة من الطرق الخاصة بها ، وليس كل كائن. بحيث يتصرف كل كائن من فئة معينة مثل الكائنات الأخرى من نفس الفئة. سأكون سعيدًا بمعرفة التعليقات حول اللغات التي تختلف بها الأشياء.
الثالوث المقدس
لقد حدث ذلك تاريخيا حيث يسألون عن هذه الأشياء في المقابلات. مكتوب عنها في أي كتاب اللغة OOP. لماذا؟ لأنه إذا قمت بتصميم برنامج OOP دون أي اعتبار للتغليف وتعدد الأشكال ، فستحصل على "نعش ، نعش ، مقبرة ، بدون مرافق." الوراثة ليست ضرورية للغاية ، لكن هذا المفهوم يسمح لك بفهم OOP بشكل أفضل وهو أحد الأدوات الرئيسية في التصميم باستخدام OOP.
التغليف
حسنًا ، لنبدأ بالتعريف من ويكيبيديا: "تعبئة البيانات والوظائف في مكون واحد." يبدو التعريف واضحًا ، لكن في نفس الوقت معمم جدًا. لذلك ، دعنا نتحدث عن سبب ضرورة ذلك على الإطلاق ، وما الذي سيمنحه لنا ، وكيف بالضبط حزم البيانات والوظائف في مكون واحد.
لقد كتبت بالفعل مقالة تتناول التغليف. وهناك توبيخًا صائبًا بحقيقة أنني قمت بتقليل التغليف إلى إخفاء المعلومات ، وهذه أشياء مختلفة بعض الشيء. على وجه الخصوص ، أنتجت EngineerSpock صيغة أنيقة مثل الحماية الثابتة . أعترف بخطأي ، ثم سأشرح لماذا ارتكبت ذلك.
في غضون ذلك ، التعريف الخاص بي ، تعريف أولي لمبدأ التغليف ، أو ، إذا كنت تريد ، عملية التغليف ، التي لا تصف مبدأ التغليف فقط ، ولكن أيضًا ما يفترض تحقيقه به:
يجب تحويل أي كيان برمجي ذي حالة غير تافهة إلى نظام مغلق ، والذي لا يمكن نقله إلا من حالة صحيحة إلى أخرى.
حول الجزء الذي يكون فيه "أي كيان برمجي له حالة غير تافهة" ، دعنا نتحدث لاحقًا. في الوقت الحالي ، سنتحدث حصريًا عن الأشياء. وحول الجزء الثاني من التعريف الخاص بي. لماذا نحتاج هذا؟
كل شيء بسيط هنا: ما يمكن نقله من حالة صحيحة إلى أخرى لا يمكن كسره. هذا هو ، نحن بحاجة إلى التأكد من أنه لا يمكن كسر أي كائن. يبدو ، بعبارة ملطفة وطموحة. كيف تحقق هذا؟
في الصفر ، كل ما يتعلق بالكائن يجب أن يكمن في مكان واحد ، داخل نفس الحدود المعمارية ، دعنا نقول. في حالة تحوله إلى حد كبير ، فسأكرر التعريف من ويكيبيديا: "تعبئة البيانات والوظائف في مكون واحد".
أولا ، لفصل بوضوح واجهة وتنفيذه. أعتقد أن جميع زملائي على دراية بواجهة برمجة التطبيقات المختصرة. لذلك ، يجب أن يكون لكل كائن واجهة برمجة تطبيقات خاصة به ، أو PI ، إذا أردنا أن نكون دقيقين. هذا من أجل إنشائه ، والذي سيستخدمه الآخرون ، ما الذي سيحدثونه. ما ينبغي أن يكون مثل؟ حتى لا يفكر أحد في الدخول داخل جسم واستخدامه بشكل غير لائق. ولكن ليس أكثر من ذلك.
في كتاب ، للأسف ، لا أتذكر أي كتاب ، تم تفسير ذلك بمثال الميكروويف. هناك أزرار على ذلك. مقابض. إنها تسمح لك بتسخين الطعام. لا تحتاج إلى الاسترخاء في الميكروويف ، ولحام شيء هناك لتسخين حساء الأمس. لديك واجهة ، أزرار. ضع صفيحة واضغط على اثنين من الأزرار وانتظر دقيقة وكن سعيدًا.
مجرد التفكير في الأزرار التي يحتاج المستخدم إلى الضغط عليها ، وفصلها عن الحوائط الداخلية. وعلى أي حال لا تضيف أزرار إضافية! كان هذا هو الأول.
ثانياً ، احترام الحدود بين الواجهة والتنفيذ وجعل الآخرين يحترمونها. من حيث المبدأ ، هذه الفكرة بديهية وتحوم بين الحكمة الشعبية بأشكال عديدة. خذ على الأقل "إذا استفدت من شيء غير موثق ، ثم حدث شيء ما بالنسبة لك ، فأنت المسؤول". أعتقد ، مع "عدم تدوير الميكروويف حتى يعمل بالطريقة التي تريدها" ، كل شيء واضح. الآن حول كيفية جعل الآخرين يحترمون الحدود سيئة السمعة.
هذا هو المكان الذي يخفي فيه إخفاء المعلومات. نعم ، يمكنك دائمًا أن توافق ، تسأل ، تُعد اصطلاحات الكود ، وتشير إلى مراجعة الكود بأن هذا غير ممكن. لكن إمكانية التسلق إلى ما وراء هذه الحدود ستظل قائمة. هذا هو إخفاء المعلومات التي تصل إلى الإنقاذ.
لا يمكننا عبور الحدود سيئة السمعة إذا لم تتعرف المدونة لدينا على وجودها وما يكمن وراءها. وحتى إذا اكتشف ذلك ، فإن المترجم سوف يدعي أنه لا يوجد مثل هذا الحقل أو الطريقة ، وحتى إذا كان هناك ، فليس من الضروري أن أتطرق إليه ، فأنا أرفض التجميع ، وعمومًا من أنت ، لم نتصل بك ، استخدم جزء الواجهة.

هذا هو المكان الذي تلعب فيه كل أنواع أدوات الوصول المختلفة العامة والخاصة وغيرها من لغتك المفضلة. إن "إخفاء المعلومات" هو الطريقة الأكثر موثوقية للحفاظ على فوائد التغليف أسفل الصرف. على أي حال ، لا معنى لتجميع كل ما يتعلق بفصل واحد في مكان واحد ، إذا كان الكود يستخدم ما يريد وأين يريد. لكن مع إخفاء المعلومات ، لم يعد مثل هذا الموقف ينشأ من حيث المبدأ. وهذه الطريقة موثوق بها لدرجة أنه في عقول الآلاف والآلاف من المبرمجين (بمن فيهم أنا ، الموجود بالفعل) ، فإن الفرق بين التغليف وإخفاء المعلومات قد تم تسويته بطريقة ما.
ماذا لو كان YP المفضل لديك لا يسمح لك بإخفاء المعلومات؟ في هذا الموضوع ، يمكنك الاستمتاع بالتحدث عن التعليقات. أرى الطريقة التالية. في ازدياد:
- قم بتوثيق جزء الواجهة فقط واعتبر كل ما لم يتم توثيقه كتطبيق.
- افصل التنفيذ عن الواجهة عبر اصطلاح الكود (على سبيل المثال - في بيثون يوجد المتغير __all__ الذي يشير إلى ما سيتم استيراده بالضبط من الوحدة النمطية عندما تطلب استيراد كل شيء).
- اجعل قواعد التعليمات البرمجية هذه صارمة بدرجة كافية بحيث يمكن التحقق منها تلقائيًا ، وبعد ذلك سيتم مساواة أي انتهاك لها مع خطأ في التحويل البرمجي والبنية الساقطة.
مرة أخرى:
- حزمت كل ما يتعلق بفصل واحد في وحدة واحدة.
- بين الطبقات ، يتم رسم الحدود المعمارية الصارمة.
- في أي فئة ، يتم فصل جزء الواجهة عن تنفيذ جزء الواجهة هذا نفسه.
- يجب احترام الحدود بين الطبقات وإجبارها على احترام الآخرين!
سأنتهي بمثال على NEPL ، الذي لا يزال قيد التطوير بنشاط كبير ، وبعد عشر فقرات ، حصل على معدّلات الوصول:
type Microwave: class consisting of private fancyInnerChips: List of Chip, private foodWarmingThing: FoodWarmerController, private buttonsPanel: ButtonsPanel, public GetAccessToControlPanel: subprogram with no parameters returns ButtonsPanel, public OpenDoor: subprogram with no parameters returns nothing, public Put: subprogram with (Food) parameters return nothing, public CloseDoor: subprogram with no parameters returns nothing. type ButtonsPanel: class consisting of private buttons: List of ButtonState, public PressOn: subprogram with no parameters returns nothing, public PressOff: subprogram with no parameters returns nothing, public PressIncreaseTime: subprogram with no parameters returns nothing, public PressDecreaseTime: subprogram with no parameters returns nothing, public PressStart: subprogram with no parameters returns nothing, public PressStop: subprogram with no parameters returns nothing.
آمل أن يوضح الكود ما هو المثال. سأوضح نقطة واحدة فقط: يتحقق GetAccessToControlPanel مما إذا كان بإمكاننا حتى لمس الميكروويف. ماذا لو كانت مكسورة؟ ثم لا يمكنك النقر فوق أي شيء. يمكنك فقط الحصول على رسالة خطأ.
حسنًا ، حقيقة أن ButtonsPanel أصبح فصلًا منفصلاً ينقلنا بسؤال مهم: ما هو "المكون الفردي" من تعريف التغليف على ويكيبيديا؟ أين وكيف ينبغي أن تكمن الحدود بين الطبقات؟ بالتأكيد سوف نعود إلى هذه المشكلة لاحقًا.
المفسدمبدأ المسؤولية الفردية
استخدام خارج OOP
لقد تعلم الكثير من المبرمجين حول التغليف من Java / C ++ / C # تعليمي / بديلاً عن أول لغة OOP. لذلك ، التغليف في الوعي الشامل مرتبطة بطريقة أو بأخرى مع OOP. ولكن دعنا نعود إلى تعريفين من التغليف.
حزم البيانات والوظائف في مكون واحد.
يجب تحويل أي كيان برمجي ذي حالة غير تافهة إلى نظام مغلق ، والذي لا يمكن نقله إلا من حالة صحيحة إلى أخرى.
هل لاحظت؟ لا يوجد شيء على الإطلاق عن الطبقات والأشياء!
لذلك مثال. أنت ديسيبل . عملك هو أن تراقب نوع من قاعدة البيانات العلائقية. فليكن ، على سبيل المثال ، على MySQL. تستخدم قاعدة بياناتك الثمينة عدة برامج. ليس لديك سيطرة على بعضها. ما يجب القيام به
إنشاء مجموعة من الإجراءات المخزنة. نؤلفهم في دائرة واحدة ، والتي سوف نسميها واجهة. نقوم بإنشاء مستخدم واحد لبرامجنا دون أي حقوق. هذا هو الأمر CREATE USER. ثم ، باستخدام أمر GRANT ، يمنح المستخدمين فقط الحق في تنفيذ هذه الإجراءات المخزنة من مخطط الواجهة.
هذا كل شيء. لدينا قاعدة بيانات ، وهي كيان البرمجيات ذاته ذو حالة غير تافهة ، وهو أمر سهل بما فيه الكفاية للكسر. وحتى لا نفصلها ، فإننا ننشئ واجهة من الإجراءات المخزنة. وبعد وسائل MySQL نفسها ، نجعلها حتى تتمكن كيانات الجهات الخارجية من استخدام هذه الواجهة فقط.
لاحظ أن التغليف المشهور ، كما هو ، وكيف تم وصفه ، يستخدم إلى أقصى إمكاناته. ولكن هناك مثل هذه الفجوة بين التمثيل العلائقي للبيانات والكائنات التي يجب إغلاقها بإطارات عمل ORM الضخمة.
هذا هو السبب في أن الطبقات والكائنات لا تظهر في تعريف التغليف. الفكرة أوسع بكثير من OOP. وهو يجلب الكثير من الفوائد للتحدث فيه فقط في الكتب المدرسية على لغات OOP.
تعدد الأشكال
تعدد الأشكال له العديد من الأشكال والتعاريف. يكفي أن كوندراتيوس يكفيني عندما أفتح ويكيبيديا. هنا سأتحدث عن تعدد الأشكال ، كما صاغها Straustrup: واجهة واحدة - العديد من التطبيقات .
في هذا الشكل ، يمكن لفكرة تعدد الأشكال أن تعزز إلى حد كبير من موقف الكتّاب بالعين المجردة. بعد كل شيء ، إذا فصلنا الواجهة عن التنفيذ ، فلن يحتاج الشخص الذي يستخدم الواجهة إلى معرفة أن شيئًا ما قد تغير في التطبيق. أولئك الذين يستخدمون الواجهة (مثالي) لا يحتاجون حتى إلى معرفة أن التطبيق قد تغير على الإطلاق! وهذا يفتح إمكانيات لا نهاية لها للتوسع والتعديل. هل قرر سلفك أنه من الأفضل تسخين الطعام بواسطة رادار عسكري؟ إذا انفصلت هذه العبقرية المجنونة عن واجهة التنفيذ وأضفت عليها طابعًا رسميًا بشكل واضح ، فيمكن حينها تكييف الرادار العسكري مع الاحتياجات الأخرى ، ويمكن تحقيق واجهته لتسخين الطعام باستخدام ميكروويف.
يتطور NEPL بسرعة ، وتحت تأثير C # ، يكتسب (بعناية ، لا تتعثر على صيغة) نوع بيانات الواجهة.
type FoodWarmer: interface consisting of GetAccessToControlPanel: no parameters returns FoodWarmerControlPanel, OpenDoor: no parameters returns nothing, Put: have (Food) parameters returns nothing, CloseDoor: no parameters returns nothing. type FoodWarmerControlPanel: interface consisting of PressOn: no parameters returns nothing, PressOff: no parameters returns nothing, PressIncreaseTime: no parameters returns nothing, PressDecreaseTime: no parameters returns nothing, PressStart: no parameters returns nothing, PressStop: no parameters returns nothing. type EnemyFinder: interface consisting of FindEnemies: no parameters returns List of Enemy. type Radar: class implementing FoodWarmer, EnemyFinder and consisting of private secretMilitaryChips: List of Chip, private giantMicrowavesGenerator: FoodWarmerController, private strangeControlPanel: AlarmClock, public GetAccessToControlPanel: subprogram with no parameters returns FoodWarmerControlPanel, public OpenDoor: subprogram with no parameters returns nothing, public Put: subprogram with (Food) parameters return nothing, public CloseDoor: subprogram with no parameters returns nothing, public FindEnemies: subprogram with no parameters returns List of Enemy. type AlarmClock: class implementing FoodWarmerControlPanel and consisting of private mechanics: List of MechanicPart, public PressOn: subprogram with no parameters returns nothing, public PressOff: subprogram with no parameters returns nothing, public PressIncreaseTime: subprogram with no parameters returns nothing, public PressDecreaseTime: subprogram with no parameters returns nothing, public PressStart: subprogram with no parameters returns nothing, public PressStop: subprogram with no parameters returns nothing. type Microwave: class implementing FoodWarmer and consisting of private fancyInnerChips: List of Chip, private foodWarmingThing: FoodWarmerController, private buttonsPanel: ButtonsPanel, public GetAccessToControlPanel: subprogram with no parameters returns FoodWarmerControlPanel, public OpenDoor: subprogram with no parameters returns nothing, public Put: subprogram with (Food) parameters return nothing, public CloseDoor: subprogram with no parameters returns nothing. type ButtonsPanel: class implementing FoodWarmerControlPanel and consisting of private buttons: List of ButtonState, public PressOn: subprogram with no parameters returns nothing, public PressOff: subprogram with no parameters returns nothing, public PressIncreaseTime: subprogram with no parameters returns nothing, public PressDecreaseTime: subprogram with no parameters returns nothing, public PressStart: subprogram with no parameters returns nothing, public PressStop: subprogram with no parameters returns nothing.
لذلك ، إذا تم التصريح عن فئة بتنفيذ واجهة ، فيجب عليها أن تنفذ جميع الطرق من هذه الواجهة. خلاف ذلك ، فإن المترجم سوف يخبرنا "فاي". ولدينا واجهات اثنين: FoodWarmer و FoodWarmerControlPanel. انظر إليهم بعناية ، ثم دعونا نحلل التطبيقات.
كإرث من الماضي السوفيتي الصعب ، تلقينا الرادار ذو الاستخدام المزدوج ، والذي يمكن استخدامه لتسخين الطعام والعثور على العدو. وبدلاً من لوحة التحكم ، يتم استخدام المنبه ، لأنه تم تجاوز الخطة ، ويجب وضعها في مكان ما. ولكن ، لحسن الحظ ، قام MNS لم يكشف عن اسمه من معهد بحوث الكيمياء والأسمدة والسموم ، التي دفعوا بها ، بتنفيذ واجهات FoodWarmer للرادار و FoodWarmerControlPanel لساعة المنبه.
بعد جيل ، حدث لشخص ما أنه من الأفضل تسخين الطعام باستخدام ميكروويف ، ومن الأفضل التحكم في الميكروويف باستخدام الأزرار. والآن تم إنشاء فصول الميكروويف و ButtonsPanel. وأنها تنفذ نفس واجهات. FoodWarmer و FoodWarmerControl. ماذا يعطينا هذا؟
إذا استخدمنا في كل مكان في رمزنا متغيرًا مثل FoodWarmer لتسخين الطعام ، فيمكننا ببساطة استبدال التطبيق بأكثر حداثة ولن يلاحظ أحد أي شيء. وهذا يعني أن التعليمات البرمجية باستخدام الواجهة لا تهتم بتفاصيل التنفيذ. أو إلى حقيقة أنه قد تغير تماما. يمكننا حتى أن نجعل فئة FoodWarmerFactory التي تنتج تطبيقات FoodWarmer مختلفة اعتمادا على تكوين التطبيق الخاص بك.
انظر أيضًا إلى الحقول المغلقة في فئة الموجات الدقيقة والرادار. هناك لدينا المنبه ولوحة مع أزرار. ولكن خارج نعطي متغير من نوع FoodWarmerControlPanel.
في مكان ما على بيكابو ، كانت هناك قصة حول كيفية شرح مرشح معين لمبدأ تعدد الأشكال على النحو التالي:
هنا لدي قلم. أستطيع أن أكتب اسمي لها ، لكن يمكنني أن ألصقها في عينيك. هذا هو مبدأ تعدد الأشكال.
الصورة مضحكة ، والوضع فظيع ، وشرح مبدأ تعدد الأشكال عديم الجدوى.
مبدأ تعدد الأشكال ليس أن الطبقة القلم مع بعض الخوف يدرك واجهات القرطاسية والفولاذ البارد في نفس الوقت. مبدأ تعدد الأشكال هو أن كل شيء يمكن وخزه يمكن أن يعلق في العين. لأنه يمكن وخزها. وستكون النتيجة مختلفة ، ولكن من الأفضل أن نعطيه الحرمان البصري. وتتيح لك طريقة تعدد الأشكال أن تعكس هذه الحقيقة في النموذج الذي تبنيه لعالمك.
استخدام خارج OOP
هناك لغة مضحكة ومضحكة بكل معنى الكلمة لهذه الكلمات مثل Erlang. ولها ميزة مثل السلوك. راقب يديك:
ينقسم الكود هناك إلى وحدات. يمكنك استخدام اسم الوحدة كمتغير. بمعنى أنه يمكنك كتابة استدعاء دالة من وحدة نمطية مثل:
من أجل التأكد من أن الوحدة النمطية لها وظائف معينة ، هناك ميزة اللغة مثل السلوك. في وحدة نمطية تستخدم وحدات أخرى ، يمكنك تحديد متطلبات الوحدات النمطية المتغيرة باستخدام التصريح behaviour_info. ثم الوحدات النمطية التي ستستخدمها وحدة dad الخاصة بك ، والتي حددت behaviour_info ، باستخدام إعلان السلوك ، تخبر المترجم: "نحن نلتزم بتنفيذ هذا السلوك حتى تتمكن وحدة dad من استخدامنا."
على سبيل المثال ، تسمح لك الوحدة النمطية gen_server بإنشاء خادم متزامن أو غير متزامن في عملية منفصلة (لا توجد مؤشرات ترابط في Erlang ، فقط الآلاف من العمليات المتوازية الصغيرة) ، ينفذ الطلبات من عمليات أخرى. وفي gen_server يتم جمع كل المنطق المرتبط بالطلبات من العمليات الأخرى. ولكن تتم المعالجة المباشرة لهذه الطلبات من قبل أولئك الذين يقومون بتطبيق سلوك gen_server. وعلى الرغم من أن الوحدات النمطية الأخرى تقوم بتطبيقه بشكل صحيح (حتى إذا كانت هناك عناصر روتين فارغة) ، إلا أن gen_server لا يهتم بشكل عام بكيفية معالجة هذه الطلبات. علاوة على ذلك ، يمكن تغيير وحدة المعالجة على الطاير.
واجهة واحدة - العديد من التطبيقات. كما صرنا ستروستروب لنا. كما هو مكتوب في الكتب الذكية على OOP. والآن اقتباس من ويكيبيديا إلى الاستوديو:
Erlang — , .
« — » , , .
. .NET. . CLR . CLR Microsoft , , , ( ECMA-335).
.NET , Windows, Windows Phone, XBox ( XNA, , ), . Microsoft. , , . , Mono Project. .NET. .
, . Microsoft , .NET . . , , .NET Core. , .NET Core .NET Framework, , , . , .
, « — » - . , .
, . , , , . , .
, , . , , NEPL. . Name? , . EtiquetteInfo - .
import class EtiquetteInfo from Diplomacy. type PoliteName: class consisting of private FirstName: String, private MiddleName: String, private LastName: String, for descendants GetPoliteFirstName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteMiddleName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteLastName: subprogram with (EtiquetteInfo) parameters returns String, public GetFullName: subprogram with (EtiquetteInfo) parameters returns String. subprogram GetPoliteFirstName.PoliteName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return _EtiquetteInfo.PoliteFirstName(FirstName). subprogram GetPoliteMiddleName.PoliteName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return _EtiquetteInfo.PoliteMiddleName(MiddleName). subprogram GetPoliteLastName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return _EtiquetteInfo.PoliteLastName(LastName). subprogram GetFullName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return GetPoliteFirstName(_EtiquetteInfo) + GetPoliteMiddleName(_EtiquetteInfo) + GetPoliteLastName(_EtiquetteInfo).
, GetFullName - , ( , , ?). , , - . , , . , , , , . , . PoliteName . ExoticPoliteName — . , , .
- . ExoticPoliteName, PoliteName, . PoliteExoticName. , PoliteName.
import class EtiquetteInfo from Diplomacy. type PoliteExoticName: class extending PoliteName and consisting of private MoreMiddleNames: List of String, for descendants overridden GetPoliteMiddleName: subprogram with (EtiquetteInfo) parameters returns String, public overriden GetFullName: subprogram with (EtiquetteInfo) parameters returns String. subprogram GetPoliteMiddleName.PoliteExoticName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as String AggregatedMiddleName = String.Join(" ", MoreMiddleNames), return base.GetPoliteMiddleName(_EtiquetteInfo + AggregatedMiddleName). subprogram GetFullName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as String Prefix = "", String FirstName = GetFirstName(_EtiquetteInfo), if _EtiquetteInfo.ComplimentIsAppropriate(FirstName) then Prefix = "Oh, joy of my heart, dear ", return Prefix + base.GetFullName(_EtiquetteInfo).
: PoliteName . PoliteExoticName -.
, , . , GetPoliteFirstName GetPoliteLastName. . GetFullName, , .
, , PoliteName, PoliteExoticName, GetFullName. , PoliteName, , . , , base.GetFullName(etiquetteInfo). , , .
, " ". , . : , . . .
, . . , Boolean, , . , Object. . , , , , Object, .
, NEPL . PoliteName Object, PoliteExoticName PoliteName Object . , NEPL :
subprogram Foo.Bar with no parameters returning nothing implemented as PoliteExoticName _PoliteExoticName = GetSomePoliteExoticName(), PoliteName _PoliteName = _PoliteExoticName, Object _Object = _PoliteExoticName.
, , _Object.GetFullName, , . PoliteName PoliteExoticName - Object, - _Object, .
? , . . , ( Object) , - -.
, , , , . ? , . - , . . - , .
? , . . . , «» , . ?
. , NEPL for descendants.
type PoliteName: class consisting of private FirstName: String, private MiddleName: String, private LastName: String, for descendants GetPoliteFirstName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteMiddleName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteLastName: subprogram with (EtiquetteInfo) parameters returns String, public GetFullName: subprogram with (EtiquetteInfo) parameters returns String.
PoliteExoticName FirstName, «, , , , ». GetPoliteFirstName FirstName.

, , Square Shape, Shape Square . , . Shape , , . Square, Shape. لماذا؟ , Shape, .
. , ? -, . -, , , , . , , .
, . , . « »? , . . , .
. , . , , . . , . , , .
, , . . ( , ), , . , , . , , . .
, ) , ) , , , 999 1000 . , , .
()
, . - , . , , . - , .
SOLID
— , , . - . SOLID — , , … ? ? , , .
S — The Single Responsibility Principle
. .
.
. . .
, « » - . . « ?». , , ? .
, SRP :
.
, ? — , . ? . « ». , . ? .
, ? . , .txt-. , , , .txt-. - , . , , … .txt-. لماذا؟ . , , .txt-.
NB ( , ) «», « , ».

. , , , .txt-. . , . , . ولكن! -, , , . -, , , .

, , . , ) , ) .html-. , , .txt/.html.
, , , . , , .txt-. ما يمكن أن يحدث الخطأ؟
- . , . , , . , , , , , , . .
- , . 900 USD 900.00$? 20190826T130000 2019 ? , ?
- .txt-? .csv? , .txt. ? ? - ? - ? , ?
? — . , -.
, , , . , .
DRY — Don't Repeat Yourself
:
, . .
, , , - , . , , .
SRP . , , . , « ». , , , — .
. HTML . , «» . , HTML 90- . , . - , . , HTML- . CSS .
? -, CSS , «» . , . -, CSS- html- , - text-color . — . .
O — The Open/Closed Principle
, , , - . , «» «». / , , . « » . :
.
. , . , . , — .
, . . ? . , .
- , . , . , / .
- , . , . / /, . NB /. , : .
- , . , .
- , - , , . (), () «» .
- . , , ? , . , . , .
, . .
type SpellChecker: class consisting of public DoSpellCheck: subprogram with (String) parameters returns String. type CorporativeStyleChecker: class consisting of public DoCorporativeStyleCheck: subprogram with (String) parameters returns String. type TextProcessor: class consisting of private Text: String, private SpellChecker: SpellChecker, private CorporativeStyleChecker: CorporativeStyleChecker, public Process: subprogram with no parameters returns String. subprogram TextProcessor.Process with no parameters returning String implemented as String ProcessedText = Text, ProcessedText = SpellChecker.DoSpellCheck(ProcessedText), ProcessedText = CorporativeStyleChecker.DoCorporativeStyleCheck(ProcessedText), return ProcessedText.
,
type TextChecker: interface consisting of Check: have (String) parameters returns String. type SpellChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type CorporativeStyleChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type TextProcessor: class consisting of private Text: String, private SpellChecker: SpellChecker, private CorporativeStyleChecker: CorporativeStyleChecker, public Process: subprogram with no parameters returns String. subprogram TextProcessor.Process with no parameters returning String implemented as String ProcessedText = Text, List of SpellChecker Checkers = (SpellChecker, CorporativeStyleChecker), for each SpellChecker SpellChecker in Checkers do ProcessedText = SpellChecker.Check(ProcessedText) and nothing else, return ProcessedText.
O/CP . TextCheckersSupplier, .
type TextChecker: interface consisting of Check: have (String) parameters returns String. type SpellChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type CorporativeStyleChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type TextCheckersSupplier: class consisting of public GetCheckers: subprogram with no parameters returns List of TextChecker. type TextProcessor: class consisting of private Text: String, private CheckersSupplier: TextCheckersSupplier, public Process: subprogram with no parameters returns String. subprogram TextProcessor.Process with no parameters returning String implemented as String ProcessedText = Text, List of SpellChecker Checkers = CheckersSupplier.GetCheckers(), for each SpellChecker SpellChecker in Checkers do ProcessedText = SpellChecker.Check(ProcessedText) and nothing else, return ProcessedText.
? , , , . , TextProcessor. , TextCheckerSupplier , , . TextChecker' . , , , . , .
, , , , . .
L — The Liskov Substitute Principle
, :
, , , .
, , . ?
- NEPL:
type PoliteExoticName: class extending PoliteName and consisting of... subprogram Foo.Bar with no parameters returning nothing implemented as PoliteExoticName _PoliteExoticName = GetSomePoliteExoticName(), PoliteName _PoliteName = _PoliteExoticName, Object _Object = _PoliteExoticName.
, _PoliteName - . , , . , . PoliteName, . , , , PoliteName . , , , . .
, -, allex ( , ). , , :
-, .
, «Agile Principles, Patterns and Practices in C#». NEPL, .
Object _Object = GetObjectSomewhere(), PoliteExoticName IHopeItsActuallyName = _Object as PoliteExoticName,
, , . . , . - , . , . . ( , alias , ):
from UnboundedCollections import UnboundedSet as ThirdPartyUnboundedSet. from BoundedCollections import BoundedSet as ThirdPartBoundedSet. type Set: interface consisting of Add: have (Object) parameters returns nothing, Delete: have (Object) parameters returns nothing, IsMember: have (Object) parameters returns Boolean. type UnboundedSet: class implementing Set and consisting of private ThirdPartySet: ThirdPartyUnboundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. type BoundedSet: class implementing Set and consisting of private ThirdPartySet: ThirdPartyBoundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. subprogram BoundedSet.Add with (Object O) parameters returning nothing implemented as ThirdPartSet.Add(O).

. . . PersistentSet, - . , PersistentObject. - . Delete IsMember . Add...
from PersistentCollections import PersistentSet as ThirdPartyPersistentSet, PersistentObject. type PersistentSet: class implementing Set and consisting of private ThirdPartySet: ThirdPartyPersistentSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. subprogram PersistentSet.Add with (Object O) parameters returning nothing implemented as PersistentObject Po = O as PersistentObject, ThirdPartySet.Add(Po).

. PersistentSet Object, . , , Set , . ( ):
type MemberContainer: interface consisting of Delete: have (Object) parameters returns nothing, IsMember: have (Object) parameters returns Boolean. type Set: interface extending MemberContainer and consisting of Add: have (Object) parameters returns nothing. type PersistentSet: interface extending MemberContainer and consisting of Add: have (PersistingObject) parameters returns nothing.

C#.
, NEPLNEPL. List of String. , .
type List: class generalized with (T) parameters consisting of
Set PersistentSet.
from UnboundedCollections import UnboundedSet as ThirdPartyUnboundedSet. from BoundedCollections import BoundedSet as ThirdPartBoundedSet. from PersistentCollections import PersistentSet as ThirdPartyPersistentSet, PersistentObject. type Set: interface generalized with (T) parameters consisting of Add: have (T) parameters returns nothing, Delete: have (T) parameters returns nothing, IsMember: have (T) parameters returns Boolean. type UnboundedSet: class implementing Set of Object and consisting of private ThirdPartySet: ThirdPartyUnboundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. type BoundedSet: class implementing Set of Object and consisting of private ThirdPartySet: ThirdPartyBoundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. type PersistentSet: class implementing Set of PersistentObject and consisting of private ThirdPartySet: ThirdPartyPersistentSet, public Add: subprogram with (PersistentObject) parameters returning nothing, public Delete: subprogram with (PersistentObject) parameters returning nothing, public IsMember: subprogram with (PersistentObject) parameters returning Boolean.
()
. , , «» . , « — » . , . .
I — The Interface Segregation Principle
, . . .
, ? , - . , , SQL? , . , API . API , «interface», , . . ما يمكن أن يحدث الخطأ؟
, , - , DBA . , 100500 , . . , .
, , , «» . , « interface, ». DBA . GDPR, HIPAA , .
- . , . و هكذا.
? , . :
, ( ).
? interface «interface_billing», «interface_customer_data» . .
, , . pet-project. IActor. , . , IActor : ICollidable, IDisplayble, IUpdatable. ?

( , Camera), . , - . , . , , IDisplayble SpecEffect.
CollisionsController , - ICollidable. , , , SOLID . TileWall -, . CollisionsController . , , IActor , .

: , , .
D — The Dependency Inversion
-. , . , , . , , . , , , , - . , , - . , , - .
"هذا كل شيء! .» — - ImportantClass. , , . , ImportantClass VeryImportantClass, , , EvenMoreImportantClass, , . , , , . , . , .

ImportantClass VeryImportantClass EvenMoreImportantClass. ImportantClass . , , . , IVeryImportantClass IEvenMoreImportantClass, ImportantClass.
ImportantClass VeryImportantClass . ImportantClass « », IVeryImportantClass .

. , , .
. .
. .
, «» «» - . , , . . , , . , . , -.
. , .
- , . ( MegaSender), . , , SOAP API.
-. SenderAccess. , MegaSender SenderAccess . SenderAccess MegaSender, , MegaSender, MegaSender , Apple i.
MegaSender. LightSender. , SenderAccess c LightSender. , , . , .
SenderAccess, , MegaSender . , SenderAccess MegaSender. MegaSender « ». , MegaSender, , , . . , LightSender , , LightSender, MegaSender.
, SenderAccess , SenderAccess LightSender . .
, IActor ICollidable, IUpdatable, IDisplayble. , IActor . Actor Player, Enemy, Door, Wall . , .
Blueprint. . , , , , , et cetera. , , , .
, , C#, . , - List<String>. , List<T> List<String>. Actor Actor<TBlueprint>.
, , - . Actor<EnemyBlueprint> Actor<DoorBlueprint> , . , .
- . , , . , . , . , IActor, , ActorsFactory .
. : .
, , . - , . () :
. . . TCP/HTTP/SMPP/SOAP, . ? , TCP/HTTP/SMPP/SOAP- TCP/HTTP/SMPP/SOAP- , . , - . ? فكر في الأمر. , « » « 1000 ».
. ? - SOLID'? , . , - , .
- , .
- , .
. , -, , . . - , , , , -. , .
. - , :
KISS — . . . , , . , , .
, , Actor . , — , - . , .
YAGNI — . - , , « », , - . , . « » - . , , . , , .
, OC/P . 50 . , , -. 50 , . , - .
? , , « », . « » , .
, SOLID . , .
, . , -, , . , .
- «Code Complete» . , « » - .
- «Clean Code» . «Clean Architecture».
- «Agile Principles, Patterns and Practices in C#» . SOLID . , language-agnostic.
PS
, . , , . : ! , , language-agnostic .
, language-agnostic. NEPL, : , , , . , , .
, . , , . :
, , , . , , . , . : , .