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

لنتحدث عما نتوقعه من الكود. حسنًا ، بالنسبة للمبتدئين ، يجب أن تعمل.
يبدو الأمر واضحًا ، بالطبع ، لكن كل واحد منا جرب أو أطلق بنجاح رمزًا لم يكن كذلك ، لذلك لا تضحك. النقطة الثانية - يجب أن يعمل الرمز بشكل صحيح مع المواقف غير الصحيحة. هذا هو التقاط الأخطاء. ولكن دعونا نعود إلى النقطة الأولى ونتحدث عنها قليلاً.
بشكل دوري ، أحصل على مهمة ليس لدي أي فكرة عن كيفية القيام بها. أي بشكل عام (أحاول أن أنظر حولي وأجرب شيئًا جديدًا باستمرار). وهنا انجذبت على الفور إلى كتابة بعض الملخصات ، نوع من البنية التحتية ، من أجل تأخير لحظة العمل الحقيقي. لذا ، هذا خطأ. يجب أن يعمل الرمز. أعلم أنني أكرر نفسي ، لكن هذه نقطة مهمة. إذا كنت لا تعرف كيفية حل المشكلة ، فلا تتسرع في إنشاء واجهات ووحدات وكل ذلك. هذه فكرة سيئة ، ستنتهي في نهاية وقتك ، ولن تذهب إلى أي مكان. تذكر أن كود العمل السيئ أفضل عدة مرات من الكود الجيد ، ولكنه لا يعمل.
هناك مثل قديم عن شركتي برمجيات صنعتا نفس المنتج. الأول فعل على أي حال ، لكن الأول دخل السوق ، والثاني فعل كل شيء بشكل مثالي وكان متأخراً. ونتيجة لذلك ، تمكنت الحملة الأولى من غزو السوق واشترت الشركة الثانية. إنها قليلاً عن أخرى ، لكن الفكرة الرئيسية لا تزال هي نفسها. أولاً نقوم بحل المشكلة ، ثم نجعل الشفرة جميلة.
بشكل عام ، قم أولاً بعمل نموذج أولي. فليكن عرجاء ، ملتوية وغير سعيدة ، ولكن عندما يُطلب منك ، يمكنك القول أن الحل موجود بالفعل ، يبقى دمجه. وأعد كتابته كما ينبغي. يمكنك محاولة التعبير عن هذا بمثل هذا المبدأ - إذا كنت تعرف كيفية القيام بالمهمة - قم بذلك بشكل جيد. إذا كنت لا تعرف حلها أولاً بطريقة أو بأخرى.
وهناك نقطة مهمة. أود منك أن تفهم. هذه ليست دعوة لكتابة كود تالف. يجب أن يكون الرمز جيدًا. هذه دعوة إلى First Thing First - أولاً يعمل الكود ، ثم يتم إعادة بناءه.
الآن دعونا نتحدث عن Shit Happens. لذا ، لدينا الكود ، حتى أنه يعمل. بدلا من ذلك ، "يعمل". دعونا نلقي نظرة على مثال بسيط:
public string Do(int x) { using (WebClient xx = new WebClient()) { return xx.DownloadString("https://some.super.url"); } }
هذا مثال رائع لرمز "العمل". لماذا؟ لأنه لا يأخذ في الاعتبار أنه عاجلاً أم آجلاً ، ستسقط نقطة النهاية لدينا. هذا المثال لا يأخذ في الاعتبار ما يسمى بالحالة الحافة - الحدود ، "الحالات السيئة". عندما تبدأ في كتابة التعليمات البرمجية ، فكر في ما قد يحدث من أخطاء. في الواقع ، أنا لا أتحدث فقط عن المكالمات البعيدة ، ولكن عن جميع الموارد الخارجة عن سيطرتك - إدخال المستخدم والملفات واتصالات الشبكة وحتى قاعدة البيانات. كل ما يمكن أن ينكسر سيكسر في أكثر الأوقات غير المناسبة والشيء الوحيد الذي يمكنك القيام به هو الاستعداد له قدر الإمكان.
لسوء الحظ ، ليست كل المشاكل واضحة للغاية. هناك عدد من مجالات المشاكل يكاد يكون مضمونا لتوليد الأخطاء. على سبيل المثال ، العمل مع الإعدادات المحلية ، مع المناطق الزمنية. الألم والصراخ "كل شيء يعمل على جهازي". هم فقط بحاجة إلى معرفتهم والعمل معهم بعناية.
بالمناسبة حول إدخال المستخدم. هناك مبدأ جيد جدًا ، ينص على أن أي إدخال للمستخدم يعتبر غير صحيح حتى يثبت خلاف ذلك. بمعنى آخر ، تحقق دائمًا مما أدخله المستخدم. ونعم ، على الخادم أيضًا.
المجموع:
- أولاً اجعل الشفرة تعمل ،
- ثم اجعله جيداً
- لا تنس حالات الحواف ومعالجة الأخطاء.
الآن لنتحدث عن دعم الكود
يعتبر الدعم مفهومًا معقدًا ، لكنني سأشمل ثلاثة مكونات هنا - يجب أن تكون الشفرة سهلة القراءة ، وسهلة التغيير ، ومتسقة.
من يكتب التعليقات بالروسية؟ لا أحد يكتب؟ عظيم بشكل عام ، إحدى المشاكل هي الكود غير الإنجليزي. لا تفعل هذا. كان لدي جزء من التعليمات البرمجية مع دروس باللغة النرويجية ، ولم أستطع نطق أسمائهم. كان حزينا. من الواضح أن دعم مثل هذا الرمز (لغير النرويجيين) لن يكون مهمة تافهة. لكن هذا نادر.
بشكل عام ، سهولة القراءة تتعلق بالتسمية والبنية. يجب أن تكون أسماء الكيانات - الفئات ، والأساليب ، والمتغيرات ، بسيطة وسهلة القراءة وذات معنى. خذ مثالنا السابق.
public string Do(int x) { using (WebClient xx = new WebClient()) { return xx.DownloadString("https://some.super.url"); } }
هل يمكنك فهم ما تفعله طريقة Do بالرغم من التنفيذ؟ بالكاد. وبالمثل مع أسماء المتغيرات. لفهم نوع كائن xx تحتاج إلى البحث عن إعلانه. يستغرق هذا وقتنا ، ويمنعنا من فهم ما يحدث ، بشكل عام ، في التعليمات البرمجية. لذلك ، يجب أن تعكس الأسماء جوهر الفعل أو المعنى. على سبيل المثال ، إذا قمت بإعادة تسمية أسلوب Do إلى GetUserName ، يصبح الرمز أكثر وضوحًا قليلاً وفي بعض الحالات لم يعد علينا النظر في تنفيذه. وبالمثل مع أسماء المتغيرات في النموذج x و xx. صحيح ، هناك استثناءات مقبولة بشكل عام في شكل e للأخطاء ، i ، k لعدادات الدورة ، n للأبعاد ، وبعضها الآخر.
مرة أخرى ، على سبيل المثال ، خذ الكود الذي كتبته قبل شهر وحاول قراءته بطلاقة. هل تفهم ما يحدث هناك؟ إذا كان الأمر كذلك ، أهنئكم. إذا لم يكن الأمر كذلك ، فأنت تواجه مشكلة في إمكانية قراءة الرمز.
بشكل عام ، هناك مثل هذا الاقتباس المثير للاهتمام:
"هناك شيئان صعبان فقط في علوم الكمبيوتر: إبطال ذاكرة التخزين المؤقت وتسمية الأشياء." © فيل كارلتون
هناك شيئان معقدان فقط في علوم الكمبيوتر: إبطال ذاكرة التخزين المؤقت والتسمية.
تذكرها عندما تعطي أسماء كياناتك.
المكون الثاني من التعليمات البرمجية القابلة للقراءة هو تعقيدها أو هيكلها. أنا أتحدث عن أولئك الذين يحبون كتابة ستة ifas متداخلة ، أو كتابة رد اتصال في رد اتصال داخل رد الاتصال. جافا سكريبت حتى مصطلح يسمى Callback Hell .

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

تعتمد كل وحدة على كل وحدة ، ومن المرجح أن تؤدي التغييرات في العقد في مكان واحد إلى ظهور الثعلب القطبي أو ، على الأقل ، إلى تصحيح طويل جدًا. من الناحية النظرية ، فإن التعامل مع هذا أمر بسيط للغاية - قلل من اعتمادك على التعليمات البرمجية الخاصة بك. كلما قل معرفتك بالشفرة عن أي تفاصيل تنفيذ ، كان ذلك أفضل بالنسبة لها. ولكن في الممارسة العملية ، يكون الأمر أكثر تعقيدًا ويؤدي إلى إعادة تعقيد التعليمات البرمجية.
في شكل نصائح ، أود أن أضعها على هذا النحو:
- إخفاء التعليمات البرمجية الخاصة بك إلى أقصى حد ممكن. تخيل أنه سيتعين عليك إزالته غدًا من المشروع يدويًا. كم عدد الأماكن التي يجب عليك إصلاحها وما مدى صعوبة ذلك؟ حاول تقليل هذا المبلغ.
- تجنب التبعيات الدائرية. افصل الشفرة إلى طبقات (المنطق والواجهة والوصول إلى البيانات) وتأكد من أن طبقات المستوى "الأدنى" لا تعتمد على طبقات المستوى "العلوي". على سبيل المثال ، لا يجب أن يعتمد الوصول إلى البيانات على واجهة المستخدم.
- قم بتجميع الوظائف في وحدات (مشاريع ، مجلدات) وإخفاء الفئات الموجودة بداخلها ، مع ترك الواجهة والواجهات فقط.
وارسم. فقط ارسم على ورقة كيفية معالجة بياناتك بواسطة التطبيق وما هي الفئات المستخدمة لهذا الغرض. سيساعدك ذلك على فهم الأماكن المعقدة قبل أن يصبح كل شيء غير قابل للإصلاح.
وأخيرا حول التوحيد. حاول دائمًا الالتزام بالأسلوب الموحد الذي يتبعه الفريق ، حتى إذا بدا لك أنه خطأ. ينطبق هذا على التنسيق والأساليب لحل المشكلة. لا تستخدم ~ ~ للتقريب ، حتى لو كان أسرع. لن يقدر الفريق ذلك. وعند البدء في كتابة رمز جديد ، انظر دائمًا إلى المشروع ، ربما تم تنفيذ شيء ما تحتاجه بالفعل ويمكنك إعادة استخدامه.
المجموع:
- التسمية الصحيحة
- هيكل جيد
- التوحيد.
يجب أن يكون الرمز منتجًا تمامًا
دعونا نشرب القليل من البرد. الشرط التالي الذي سنأخذه في الاعتبار هو أن الشفرة يجب أن تكون منتجة تمامًا.
ماذا أعني بكلمة "كفى"؟ ربما سمع الجميع أن التحسينات السابقة لأوانها شريرة ، فهي تقتل سهولة القراءة وتعقيد الشفرة. هذا صحيح. ولكن من الصحيح أيضًا أنه يجب أن تعرف أداتك ولا تكتب عليها حتى يقوم عميل بريد الويب بتحميل Core I7 بنسبة 60٪. يجب أن تعرف المشاكل النموذجية التي تؤدي إلى مشاكل في الأداء وتجنبها حتى في مرحلة كتابة التعليمات البرمجية.
دعنا نعود إلى مثالنا:
public string GetUserName(int userId) { using (WebClient http = new WebClient()) { return http.DownloadString("https://some.super.url"); } }
يحتوي هذا الرمز على مشكلة واحدة - تنزيل متزامن عبر الشبكة. هذه عملية I / O تجمد تدفقنا حتى يتم تنفيذها. في تطبيقات سطح المكتب ، سيؤدي هذا إلى واجهة متدلية ، وفي تطبيقات الخادم ، إلى حجز ذاكرة غير مفيد واستنفاد عدد الطلبات إلى الخادم. بمجرد معرفة مثل هذه المشاكل ، يمكنك بالفعل كتابة رمز محسن أكثر. وسيكون هذا كافياً في معظم الحالات.
لكن في بعض الأحيان ، لا. لذلك ، قبل كتابة التعليمات البرمجية ، تحتاج إلى معرفة المتطلبات المحددة لها من حيث الأداء.
الآن لنتحدث عن الاختبارات.
هذا ليس موضوعًا أقل سخافة من الموضوع السابق ، وربما أكثر من ذلك. كل شيء معقد مع الاختبارات. لنبدأ بالبيان - أعتقد أنه يجب تغطية الرمز بعدد معقول من الاختبارات.
لماذا نحتاج حتى إلى تغطية الكود واختباراته؟ في عالم مثالي ، ليست هناك حاجة إليها. في عالم مثالي ، تتم كتابة التعليمات البرمجية دون أخطاء ، ولا تتغير المتطلبات مطلقًا. لكننا نعيش في مكان بعيد عن العالم المثالي ، لذلك نحتاج إلى اختبارات للتأكد من أن الرمز يعمل بشكل صحيح (لا توجد أخطاء) وأن الرمز يعمل بشكل صحيح بعد تغيير شيء ما. هذه هي الفائدة التي تجلبها لنا الاختبارات. من ناحية أخرى ، حتى 100٪ (بسبب تفاصيل حساب المقاييس) التي تغطيها الاختبارات لا تضمن أنك غطت كل شيء على الإطلاق. علاوة على ذلك ، كل اختبار إضافي يبطئ التطوير لأنه بعد تغيير الوظيفة ، سيتعين عليك أيضًا تحديث الاختبارات. لذلك ، يجب أن يكون عدد الاختبارات معقولًا ، وتتمثل الصعوبة الرئيسية في إيجاد حل وسط بين كمية التعليمات البرمجية واستقرار النظام. العثور على هذا الوجه صعب للغاية ولا توجد وصفة عالمية لكيفية القيام بذلك. ولكن هناك بعض النصائح التي قد تساعدك في القيام بذلك.
- تغطية منطق تطبيق الأعمال. منطق الأعمال هو كل ما يتم إنشاء التطبيق من أجله ، ويجب أن يكون مستقرًا قدر الإمكان.
- تغطية الأشياء المعقدة والمحسوبة. الحسابات والتحويلات ودمج البيانات المعقدة. مكان يسهل فيه ارتكاب الخطأ.
- تغطية البق. الخطأ عبارة عن علامة تخبرنا أن الشفرة كانت ضعيفة هنا. وهذا مكان جيد لكتابة اختبار هنا.
- تغطية رمز إعادة استخدامها بشكل متكرر. من المحتمل جدًا أنه سيتم تحديثه بشكل متكرر ونحتاج إلى التأكد من أن إضافة شيء لن يكسر الآخر.
لا تغطي دون الحاجة
- المكتبات الأجنبية - ابحث عن المكتبات التي تغطيها اختباراتها بالفعل.
- البنية التحتية - DI ، التعيين التلقائي (إذا لم يكن هناك خرائط معقدة) وما إلى ذلك. هناك اختبارات e2e أو التكامل لهذا.
- أشياء تافهة - تعيين البيانات للحقول ، وإعادة توجيه المكالمات ، وما إلى ذلك. من المؤكد أنك ستجد أماكن أكثر فائدة لتغطيتها بالاختبارات.
حسنًا ، هذا هو الأساس.
لتلخيص. الكود الجيد هو
- كود العمل
- سهل القراءة
- سهل التغيير
- بسرعة كافية
- ومغطاة بالاختبارات بالمقدار الصحيح.
حظا سعيدا بهذه الطريقة الصعبة. وعلى الأرجح ، ستكره هذا. حسنا ، إذا لم يكن كذلك ، مرحبا.
