نود أن نلفت انتباهك إلى مقال لا يوافق مؤلفه على النهج الموجه للكائنات البحتة عند التعامل مع لغة C ++. نطلب منك تقييم ، إن أمكن ، ليس فقط حجة المؤلف ، ولكن أيضًا المنطق والأسلوب.
كان هناك الكثير من الكتابات مؤخرًا حول C ++ وأين تتجه اللغة وكم من ما يسمى "C ++ الحديثة" ليس خيارًا لمطوري الألعاب.
بينما أشارك وجهة النظر هذه تمامًا ، أميل إلى عرض تطور C ++ كنتيجة لترسيخ الأفكار الشائعة التي يسترشد بها معظم المطورين. سأحاول في هذه المقالة تنظيم بعض هذه الأفكار جنبًا إلى جنب مع أفكاري - وربما سأحصل على شيء ضئيل.
البرمجة الشيئية (OOP) كأداة
على الرغم من أن C ++ توصف بأنها لغة برمجة متعددة النماذج ، إلا أن معظم المبرمجين يستخدمون عمليًا C ++ فقط كلغة موجهة للكائنات (تُستخدم البرمجة العامة "لتكملة" OOP).
من المفترض أن تكون OOP أداة ، واحدة من العديد من النماذج التي يمكن للمبرمج استخدامها لحل المشكلات في التعليمات البرمجية. ومع ذلك ، في تجربتي ، يتم قبول OOP من قبل معظم المحترفين كمعيار ذهبي لتطوير البرامج. في الأساس ، يبدأ تطوير الحل بتحديد الأشياء التي نحتاجها. يبدأ حل مشكلة معينة بعد توزيع الكود بين الكائنات. مع الانتقال إلى هذا النوع من التفكير الموجه للكائنات ، يتحول OOP من أداة إلى صندوق أدوات كامل.
عن الانتروبيا كقوة سرية تغذي تطوير البرمجيات
أحب أن أفكر في حل OOP على أنه كوكبة: إنها مجموعة من الكائنات بينها خطوط مرسومة بشكل عشوائي. يمكن أيضًا اعتبار مثل هذا الحل بمثابة رسم بياني تكون فيه الكائنات عُقدًا ، والعلاقات بينها عبارة عن حواف ، لكن ظاهرة المجموعة / الكتلة ، التي تنقلها استعارة الكوكبة ، أقرب إليّ (مقارنةً بها ، الرسم البياني مجردة جدًا).
لكني لا أحب الطريقة التي تتكون بها مثل هذه "الأبراج من الأشياء". حسب فهمي ، كل كوكبة من هذا القبيل ليست أكثر من لقطة للصورة التي تشكلت في رأس المبرمج وتعكس شكل مساحة الحل في لحظة معينة. حتى مع الأخذ في الاعتبار جميع الوعود التي يتم تقديمها في التصميم الموجه للكائنات حول القابلية للتمدد ، وإعادة الاستخدام ، والتغليف ، وما إلى ذلك ... لا يمكن التنبؤ بالمستقبل ، لذلك في كل حالة يمكننا تقديم حل للمشكلة التي نواجهها بالضبط.
يجب أن نشجع على أننا "فقط" نحل المشكلة المعروضة علينا مباشرة ، ولكن من واقع خبرتي ، فإن المبرمج الذي يستخدم مبادئ التصميم بروح OOP يخلق حلاً ، بينما يقيد نفسه بافتراض أن المشكلة نفسها لن تتغير بشكل كبير و وبالتالي ، يمكن اعتبار الحل دائمًا. أعني أنه من الآن فصاعدًا ، يبدأ الناس في التفكير في الحل من حيث الكائنات التي تشكل الكوكبة المذكورة أعلاه ، وليس من حيث البيانات والخوارزميات ؛ المشكلة نفسها مجردة.
ومع ذلك ، فإن البرنامج يخضع للإنتروبيا ليس أقل من أي نظام آخر ، وبالتالي ، نعلم جميعًا أن الكود سيتغير. علاوة على ذلك ، بطريقة لا يمكن التنبؤ بها. لكن بالنسبة لي في هذه الحالة ، من الواضح تمامًا أن الكود سيتدهور في أي حال ، وينزلق إلى الفوضى والفوضى ، إذا لم تحاربها بوعي.
لقد رأيت هذا المظهر بعدة طرق مختلفة في حلول OOP:
- تظهر مستويات وسيطة جديدة في التسلسل الهرمي ، بينما لم يكن من المفترض في الأصل تقديمها.
- تتم إضافة وظائف افتراضية جديدة مع تطبيقات فارغة في معظم التسلسل الهرمي.
- يتطلب أحد الكائنات الموجودة في الكوكبة معالجة أكثر مما هو مخطط له ، مما يؤدي إلى انزلاق الروابط بين الكائنات الأخرى.
- , , , .
- .…
هذه كلها أمثلة على التمدد المنظم بشكل غير صحيح. علاوة على ذلك ، فإن النتيجة هي نفسها دائمًا ، ويمكن أن تأتي في غضون بضعة أشهر ، أو ربما في غضون بضع سنوات. بمساعدة إعادة البناء ، يحاولون القضاء على انتهاكات مبادئ تصميم OOP ، التي تم إجراؤها عند إضافة كائنات جديدة إلى الكوكبة ، وتمت إضافتها بسبب إعادة صياغة المشكلة نفسها. في بعض الأحيان تساعد إعادة الهيكلة. للحظات. الانتروبيا ثابت ، وليس لدى المبرمجين الوقت لإعادة تشكيل كل كوكبة OOP من أجل التغلب عليها ، لذلك أي مشروع يجد نفسه بانتظام في نفس الموقف ، واسمه فوضى.
في دورة حياة أي مشروع OOP ، تأتي ، عاجلاً أم آجلاً ، نقطة يستحيل بعدها الحفاظ عليها. عادة ، في مثل هذه اللحظة ، يجب أن تتخذ أحد الإجراءين التاليين:
- « »: - . , , , , , .
- : -, , , .
يرجى ملاحظة: الخيار مع الصندوق الأسود سيظل يتطلب إعادة الكتابة في حالة استمرار تطوير الميزات الجديدة و / أو استمرار الحاجة إلى القضاء على الأخطاء.
يعيدنا الموقف مع إعادة كتابة الحل إلى ظاهرة لقطة لمساحة الحل المتاحة في لحظة معينة. إذن ما الذي تغير بين تصميم OOP رقم 1 والوضع الحالي؟ في الأساس ، هذا كل شيء. لقد تغيرت المشكلة ، وبالتالي ، هناك حاجة إلى حل مختلف.
بينما كنا نكتب الحل ، باتباع مبادئ تصميم OOP ، قمنا بتلخيص المشكلة ، وبمجرد تغييرها ، انهار حلنا مثل منزل من الورق.
أعتقد أنه في هذه اللحظة بدأنا نتساءل عن الخطأ الذي حدث ، ونحاول السير في الاتجاه الآخر وتحديث الاستراتيجيات لحل المشكلة بناءً على نتائج ما بعد الوفاة (استخلاص المعلومات). ومع ذلك ، كلما صادفت مثل هذا السيناريو "وقت إعادة الكتابة" ، لا شيء يتغير: يتم استخدام مبادئ OOP مرة أخرى ، والتي بموجبها يتم تنفيذ لقطة جديدة ، بما يتوافق مع الحالة الحالية لمساحة المشكلة. تتكرر الدورة بأكملها.
سهولة إزالة الكود كمبدأ تصميم
في أي نظام مبني على مبدأ OOP ، تحظى الكائنات الموجودة في "الكوكبة" بالاهتمام الرئيسي. لكنني أعتقد أن العلاقات بين الأشياء لا تقل أهمية ، إن لم تكن أكثر ، عن الأشياء نفسها.
أفضل الحلول البسيطة حيث يتكون الرسم البياني للتبعية الخاص بالشفرة من الحد الأدنى لعدد العقد والحواف. كلما كان الحل أبسط ، كان من الأسهل ليس تغييره فحسب ، بل إزالته أيضًا. لقد وجدت أيضًا أنه كلما كان من الأسهل إزالة الكود ، زادت سرعة إعادة تركيز الحل وتكييفه مع ظروف المشكلة المتغيرة. في الوقت نفسه ، تصبح الشفرة أكثر مقاومة للإنتروبيا ، نظرًا لأن الأمر يتطلب جهدًا أقل للحفاظ عليها بالترتيب ومنعها من الانزلاق إلى الفوضى.
حول الأداء بالتعريف
ولكن أحد الاعتبارات الرئيسية لتجنب تصميم OOP هو الأداء. كلما احتجت إلى تشغيل المزيد من التعليمات البرمجية ، كان الأداء أسوأ.
من المستحيل أيضًا عدم ملاحظة أن ميزات OOP ، بحكم تعريفها ، لا تتألق مع الأداء. لقد قمت بتطبيق تسلسل هرمي OOP بسيط بواجهة وفئتين مشتقتين تتجاوزان استدعاء دالة ظاهرية خالصة في Compiler Explorer .
الرمز في هذا المثال يطبع إما "Hello، World!" أو لا ، اعتمادًا على عدد الوسائط التي تم تمريرها إلى البرنامج. بدلاً من البرمجة المباشرة لكل شيء وصفته للتو ، سيتم استخدام أحد أنماط تصميم OOP القياسية ، الميراث ، لحل هذه المشكلة في الكود.
في هذه الحالة ، أكثر ما يلفت الانتباه هو مقدار إنشاء برامج التحويل البرمجي للكود ، حتى بعد التحسين. ثم، وتبحث عن كثب، يمكنك أن ترى كيف مكلفة، وفي الوقت نفسه لا طائل منه هذه الصيانة: عندما يتم تمرير عدد غير صفرية من الحجج للبرنامج، رمز لا يزال يخصص الذاكرة (دعوة
new)، يحمل عناوين vtableكل الكائنات، يحمل عنوان وظيفة Work()ل ImplBويقفز إليها، بحيث بعد ذلك على الفور العودة ، حيث لا يوجد شيء يمكن القيام به هناك. أخيرًا ، يتم استدعاؤه deleteلتحرير الذاكرة المخصصة.
لم تكن أي من هذه العمليات ضرورية على الإطلاق ، لكن المعالج قام بها جميعًا بشكل صحيح.
وبالتالي ، إذا كان أحد الأهداف الأساسية لمنتجك هو تحقيق أداء عالٍ (غريب إذا كان الأمر غير ذلك) ، فعندئذٍ في الكود يجب أن تتجنب العمليات المكلفة غير الضرورية ، مفضلاً العمليات البسيطة التي يسهل الحكم عليها ، واستخدام التركيبات التي تساعد في تحقيق هذا الهدف.
خذ الوحدة على سبيل المثال . كجزء من ممارساتهم الحديثة ، فإن الأداء هو الصواب باستخدام C # ، وهي لغة موجهة للكائنات ، لأن هذه اللغة مستخدمة بالفعل في المحرك نفسه. ومع ذلك ، فقد استقروا على مجموعة فرعية من C # ، علاوة على ذلك ، على مجموعة غير مرتبطة ارتباطًا صارمًا بـ OOP ، وعلى أساسها قاموا بإنشاء بنيات شحذ للأداء العالي.
نظرًا لأن مهمة المبرمج هي حل المشكلات باستخدام الكمبيوتر ، فلا يمكن تصور أن أعمالنا تكرس القليل جدًا من الاهتمام لكتابة التعليمات البرمجية التي تجعل المعالج يقوم بالعمل الذي يجيده المعالج بشكل خاص.
حول محاربة الصور النمطية
في مقال أنجيلو بيسكي " التعقيد المفرط هو أصل كل الشرور " ، حقق المؤلف الهدف (انظر القسم الأخير: الأشخاص) من خلال الاعتراف بأن معظم مشكلات البرامج هي في الواقع عوامل بشرية.
يحتاج الأشخاص في الفريق إلى التفاعل وتطوير فهم مشترك لما هو الهدف العام وما هو السبيل لتحقيقه. إذا كان هناك خلاف في الفريق ، على سبيل المثال ، حول الطريق إلى الهدف ، فمن الضروري لتحقيق مزيد من التقدم في الآراء. عادة لا يكون هذا صعبًا إذا كانت الاختلافات في الرأي صغيرة ، ولكن يكون من الصعب تحملها إذا كانت الخيارات مختلفة بشكل أساسي ، قل "OOP or not OOP".
تغيير رأيك ليس بالأمر السهل. التشكيك في وجهة نظرك ، وإدراك مدى خطئك وتعديل مسارك أمر صعب ومؤلم. لكن من الأصعب بكثير تغيير رأي شخص آخر!
لقد أجريت الكثير من المحادثات مع أشخاص مختلفين حول OOP ومشاكلها المتأصلة ، وعلى الرغم من أنني أعتقد أنني كنت دائمًا قادرًا على شرح سبب تفكيري بهذه الطريقة وليس بطريقة أخرى ، لا أعتقد أنني تمكنت من إبعاد أي شخص عن OOP.
صحيح ، على مدار سنوات العمل ، حددت ثلاث حجج رئيسية لنفسي ، بسبب عدم استعداد الناس لمنح الجانب الآخر فرصة:
- « ». « ». « » . , , ( , - ). « …».
- « , , , ». «» , , . , « ».
- "الجميع يعرف OOP ، من المريح جدًا التحدث مع الناس بلغة مشتركة ولديهم معرفة عامة." هذا خطأ منطقي يسمى "حجة الناس" ، أي أنه إذا استخدم جميع المبرمجين تقريبًا مبادئ OOP ، فلن تكون هذه الفكرة غير مناسبة.
إنني أدرك تمامًا أن الكشف عن الأخطاء المنطقية في الجدل لا يكفي لفضحها. ومع ذلك ، أعتقد أنه عند رؤية العيوب في أحكامك ، يمكنك الوصول إلى عمق الحقيقة والعثور على السبب العميق لرفضك فكرة غير عادية.