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

الفصل 1. كود النظيفة
ما هو أنظف كود نسخة مارتن في بضع كلمات؟ هذا رمز بدون ازدواج ، مع وجود حد أدنى لعدد الكيانات ، سهل القراءة ، بسيط. كشعار ، يمكن للمرء أن يختار: "الوضوح هو قبل كل شيء!".
الفصل 2. أسماء ذات مغزى
يجب أن تنقل الأسماء نوايا المبرمج
يجب أن يشير اسم المتغير أو الوظيفة أو الفئة إلى سبب وجود هذا المتغير وماذا يفعل وكيف يتم استخدامه. إذا كان الاسم يتطلب تعليقات إضافية ، فإنه لا ينقل نوايا المبرمج. من الأفضل أن تكتب بالضبط ما يتم قياسه وفي أي وحدة.
مثال على اسم متغير جيد: daysSinceCreation؛
الغرض: إزالة عدم الوضوح.
تجنب التضليل
لا تستخدم كلمات ذات معاني خفية غير المقصود. احذر من الاختلافات الدقيقة في الأسماء. على سبيل المثال ، XYZControllerForEfficientHandlingOfStrings و XYZControllerForEfficientStorageOfStrings.
تم العثور على أمثلة مخيفة حقًا من الأسماء المضللة عند استخدام الحرف الصغير "L" والحرف الكبير "O" في أسماء المتغيرات ، وخاصة في المجموعات. بطبيعة الحال ، تنشأ مشاكل بسبب حقيقة أن هذه الحروف لا تختلف تقريبًا عن الثوابت "1" و "0" ، على التوالي.
استخدام الاختلافات ذات مغزى
إذا كانت الأسماء مختلفة ، فينبغي أن تشير إلى مفاهيم مختلفة.
"سلسلة الأرقام" من النموذج (a1 ، a2 ، ... aN) هي عكس التسمية الواعية. لا يحملون معلومات أو يقدمون فكرة عن نوايا المؤلف.
كلمات غير معلوماتية زائدة عن الحاجة. يجب ألا يظهر متغير الكلمة في أسماء المتغيرات. يجب ألا يظهر جدول الكلمات مطلقًا في أسماء الجداول. لماذا NameString أفضل من الاسم؟ هل يمكن أن يكون الاسم رقمًا حقيقيًا؟
استخدم أسماء الهجاء: generTimestamp أفضل بكثير من genymdhms.
اختر أسماء قابلة للبحث
لا يمكن استخدام أسماء الحروف الفردية إلا للمتغيرات المحلية بطرق قصيرة.
تجنب مخططات ترميز الاسم
وكقاعدة عامة ، تكون الأسماء المشفرة ضعيفة بشكل سيئ ومن السهل عمل خطأ مطبعي فيها.
واجهات والتطبيقات
أنا (مؤلف الكتاب) أفضل ترك أسماء الواجهات بدون بادئات. البادئة I ، الشائعة جدًا في الكود القديم ، في أفضل الأحوال يصرف الانتباه ، وفي أسوأ الأحوال - تنقل معلومات غير ضرورية. لن أخبر المستخدمين بأنهم يتعاملون مع واجهة.
أسماء الصف
يجب أن تكون أسماء الفئات والكائنات أسماء ومجموعاتها: العملاء و WikiPage و Account و AddressParser. تجنب استخدام كلمات مثل Manager أو Processor أو Data أو Info في أسماء الفئات. لا ينبغي أن يكون اسم الصنف فعلًا.
أسماء الطرق
أسماء الطريقة هي أفعال أو عبارات شفهية: postPayment ، deletePage ، save ، إلخ. تتشكل طرق القراءة / الكتابة وتتنبأ من القيمة والحصول على البادئة وتعيينها وفقًا لمعيار javabean.
الامتناع عن التورية
تتمثل مهمة المؤلف في توضيح رمزه قدر الإمكان. يجب أن ينظر إلى الكود في لمحة ، دون الحاجة إلى دراسة متأنية. ركز على نموذج الأدب الشعبي ، حيث يجب على المؤلف نفسه التعبير بحرية عن أفكاره.
إضافة سياق هادف.
يمكن إضافة السياق باستخدام البادئات: addrFirstName ، addrLastName ، addrState ، إلخ. على الأقل ، سيتفهم قارئ التعليمات البرمجية أن المتغيرات جزء من بنية أكبر. بالطبع ، سيكون من الأصح إنشاء فصل يسمى العنوان ، حتى يعرف المترجم أن المتغيرات جزء من شيء آخر.
متغيرات ذات سياق غير واضح:
private void printGuessStatistics(char candidate, int count) { String number; String verb; String pluralModifier; if (count == 0) { number = "no"; verb = "are"; pluralModifier = "s"; } else if (count == 1) { number = ~_~quot quot~_~; verb = "is"; pluralModifier = ""; } else { number = Integer.toString(count); verb = "are"; pluralModifier = "s"; } String guessMessage = String.format( "There %s %s %s%s", verb, number, candidate, pluralModifier ); print(guessMessage); }
الوظيفة طويلة بعض الشيء ، ويتم استخدام المتغيرات طوال الوقت. لتقسيم الوظيفة إلى أجزاء صغيرة أصغر ، يجب إنشاء فئة GuessStatisticsMessage وجعل ثلاثة متغيرات حقول هذه الفئة. وبهذه الطريقة ، سنوفر سياقًا واضحًا للمتغيرات الثلاثة - والآن أصبح من الواضح تمامًا أن هذه المتغيرات جزء من GuessStatisticsMessage.
المتغيرات مع السياق:
public class GuessStatisticsMessage { private String number; private String verb; private String pluralModifier; public String make(char candidate, int count) { createPluralDependentMessageParts(count); return String.format( "There %s %s %s%s", verb, number, candidate, pluralModifier ); } private void createPluralDependentMessageParts(int count) { if (count == 0) { thereAreNoLetters(); } else if (count == 1) { thereIsOneLetter(); } else { thereAreManyLetters(count); } } private void thereAreManyLetters(int count) { number = Integer.toString(count); verb = "are"; pluralModifier = "s"; } private void thereIsOneLetter() { number = ~_~quot quot~_~; verb = "is"; pluralModifier = ""; } private void thereAreNoLetters() { number = "no"; verb = "are"; pluralModifier = "s"; } }
لا تضيف السياق الزائد
عادةً ما تكون الأسماء المختصرة أفضل من الأسماء الطويلة ، إلا إذا كان معناها واضحًا لقارئ الشفرة. لا تقم بتضمين سياق أكثر من اللازم في الاسم.
الفصل 3. وظائف
الاكتناز!
القاعدة الأولى: يجب أن تكون الوظائف مضغوطة.
القاعدة الثانية: يجب أن تكون الوظائف أكثر إحكاما.
لقد علمتني تجربتي العملية (على حساب العديد من التجارب والخطأ) أن الوظائف يجب أن تكون صغيرة جدًا. من المرغوب فيه ألا يتجاوز طول الوظيفة 20 سطرًا.
حكم عملية واحدة
يجب أن تؤدي الوظيفة عملية واحدة فقط. يجب عليها أداء جيدا. وقالت إنها لا ينبغي أن تفعل أي شيء آخر. إذا كانت إحدى الوظائف تنفذ فقط تلك الإجراءات التي تقع في نفس المستوى تحت الاسم المعلن للوظيفة ، فإن هذه الوظيفة تنفذ عملية واحدة.
أقسام الوظائف
لا يمكن تقسيم الوظيفة التي تؤدي عملية واحدة فقط إلى أقسام.
مستوى واحد من التجريد لكل وظيفة
للتأكد من أن الوظيفة تنفذ "عملية واحدة فقط" ، من الضروري التحقق من أن جميع أوامر الوظيفة في نفس المستوى من التجريد.
إن مزج مستويات التجريد داخل الوظيفة يؤدي دائمًا إلى حدوث تشويش.
قراءة التعليمات البرمجية من الأعلى إلى الأسفل: قاعدة تقليله
يجب قراءة الرمز كقصة - من الأعلى إلى الأسفل.
يجب أن تتبع كل وظيفة وظائف المستوى التالي من التجريد. يسمح لك هذا بقراءة الكود ، مع خفض مستويات التجريد بالتسلسل أثناء قراءة قائمة الوظائف. أنا أسمي هذا النهج "القاعدة خفض".
تبديل الأوامر
كتابة أمر التبديل المضغوط أمر صعب للغاية. حتى أمر التبديل ذو الشرطين فقط يشغل مساحة أكبر من كتلة واحدة أو وظيفة واحدة يجب أن تشغلها في رأيي. من الصعب أيضًا إنشاء أمر تبديل يقوم بشيء واحد - بطبيعته ، أوامر التبديل تؤدي دائمًا عمليات N. لسوء الحظ ، أوامر التبديل ليست ممكنة دائمًا ، ولكن على الأقل يمكننا التأكد من أن هذه الأوامر مخفية في فئة منخفضة المستوى ولا تتكرر في التعليمات البرمجية. وبالطبع ، يمكن أن يساعدنا تعدد الأشكال في هذا.
يوضح المثال عملية واحدة فقط ، اعتمادًا على نوع الموظف.
public Money calculatePay(Employee e) throws InvalidEmployeeType { switch (e.type) { case COMMISSIONED: return calculateCommissionedPay(e); case HOURLY: return calculateHourlyPay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeeType(e.type); } }
هذه الميزة لديها العديد من العيوب. أولاً ، إنه أمر رائع ، ومع إضافة أنواع جديدة من العمال سوف تنمو. ثانياً ، من الواضح أنها تؤدي أكثر من عملية واحدة. ثالثًا ، ينتهك مبدأ المسؤولية الفردية ، لأنه يحتوي على عدة أسباب محتملة للتغيير.
رابعًا ، ينتهك مبدأ Open Closed Principle ، لأنه يجب تغيير رمز الوظيفة في كل مرة تتم فيها إضافة أنواع جديدة.
ولكن ربما يكون العيب الأكثر خطورة هو أن البرنامج قد يحتوي على عدد غير محدود من الوظائف الأخرى ذات بنية مماثلة ، على سبيل المثال:
isPayday (الموظف e ، تاريخ التسجيل)
أو
deliverPay (الموظف e ، دفع المال)
و هكذا.
جميع هذه الوظائف سيكون لها نفس هيكل معيب. يكمن حل هذه المشكلة في دفن أمر التبديل في أساس المصنع التجريدي وعدم إظهاره لأي شخص. يستخدم المصنع أمر التبديل لإنشاء المثيلات المقابلة لأحفاد الموظف ، ويدعو إلى وظائف حساب باي ، isPayDay ، deliverPay ، وما إلى ذلك ، تمرير نقل متعدد الأشكال من خلال واجهة الموظف.
public abstract class Employee { public abstract boolean isPayday(); public abstract Money calculatePay(); public abstract void deliverPay(Money pay); } ----------------- public interface EmployeeFactory { public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType; } ----------------- public class EmployeeFactoryImpl implements EmployeeFactory { public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType { switch (r.type) { case COMMISSIONED: return new CommissionedEmployee(r) ; case HOURLY: return new HourlyEmployee(r); case SALARIED: return new SalariedEmploye(r); default: throw new InvalidEmployeeType(r.type); } } }
قاعدتي العامة المتعلقة بأوامر التبديل هي أن هذه الأوامر صالحة إذا حدثت مرة واحدة في البرنامج ، وتستخدم لإنشاء كائنات متعددة الأشكال ، والاختباء وراء علاقات الوراثة لتبقى غير مرئية لبقية النظام. بالطبع ، لا توجد قواعد بدون استثناءات ، وفي بعض الحالات يكون من الضروري انتهاك شرط واحد أو أكثر من هذه القاعدة.
استخدم أسماء ذات معنى
يتمثل نصف الجهد المبذول لتطبيق هذا المبدأ في اختيار الأسماء الجيدة للوظائف المدمجة التي تؤدي عملية واحدة. كلما كانت الوظيفة أصغر وأكثر تخصصًا ، أصبح من السهل اختيار اسم لها معنى.
لا تخف من استخدام الأسماء الطويلة ، فالاسم الطويل ذو المغزى أفضل من الاسم القصير الغامض. اختر نظامًا يسهل قراءة الكلمات في اسم الوظيفة ، ثم قم بعمل اسم من هذه الكلمات التي تصف الغرض من الوظيفة.
وسيطات الدالة
في الحالة المثالية ، يكون عدد وسيطات الدالة صفرًا. فيما يلي وظائف مع وسيطة واحدة (أحادي) وبوسيطتين (ثنائي). يجب تجنب الدالات التي تحتوي على ثلاث وسائط (ثلاثية) كلما كان ذلك ممكنًا.
تخلط وسيطات الإخراج الموقف حتى أسرع من الإدخال. وكقاعدة عامة ، لا يتوقع أحد أن تقوم دالة بإرجاع المعلومات في الوسائط. إذا لم تتمكن من الإدارة بدون وسيطات ، فحاول على الأقل حصر نفسك في وسيطة إدخال واحدة.
التحويلات التي تستخدم وسيطة مخرجات بدلاً من قيمة الإرجاع تربك القارئ. إذا حولت الدالة وسيطة الإدخال ، فستكون النتيجة
يجب أن يتم تمريرها في قيمة الإرجاع.
أعلام الحجج
الحجج حجة قبيحة. يمثل تمرير المعنى المنطقي لوظيفة ما عادةً رهيبة حقًا. يعمل على تعقيد توقيع الأسلوب فورًا ، حيث يعلن بصوت عالٍ أن الوظيفة تؤدي أكثر من عملية واحدة. إذا كانت العلامة صحيحة ، يتم تنفيذ عملية واحدة ، وإذا كانت خاطئة ، يتم إجراء عملية أخرى.
وظائف ثنائية
من الصعب فهم الوظيفة ذات الوسيطتين أكثر من الوظيفة الأحادية. بالطبع ، في بعض الحالات ، يكون نموذج الحجة اثنين مناسبًا. على سبيل المثال ، استدعاء Point p = new Point (0،0) ؛ معقول تماما. ومع ذلك ، هناك وسيطة في حالتنا هي مكونات مرتبة بنفس القيمة.
الكائنات كحجج
إذا كان يجب أن تتلقى إحدى الدالات أكثر من وسيطين أو ثلاثة ، فمن المحتمل بدرجة كبيرة أن يتم تجميع بعض هذه الوسيطات في فصل دراسي منفصل. النظر في الإعلانين التاليين:
Circle makeCircle(double x, double y, double radius); Circle makeCircle(Point center, double radius);
إذا تم نقل المتغيرات معًا ككل (مثل المتغيرات x و y في هذا المثال) ، فعلى الأرجح ، فإنها تشكل معًا مفهومًا يستحق اسمه الخاص.
الأفعال والكلمات الرئيسية
إن اختيار اسم جيد للدالة يمكن أن يفسر إلى حد كبير معنى الوظيفة ، وكذلك ترتيب ومعنى الوسيطات الخاصة بها. في الوظائف الأحادية ، يجب أن تشكل الوظيفة نفسها ووسيطتها زوجًا فعليًا / غير طبيعي. على سبيل المثال ، تبدو مكالمة النموذج (الاسم) مفيدة للغاية.
يفهم القارئ أنه بغض النظر عن "الاسم" ، فإنه "مكتوب" في مكان ما. والأفضل من ذلك هو سجل WriteField (الاسم) ، والذي يشير إلى أن "الاسم" مكتوب على "حقل" بعض البنية.
الإدخال الأخير هو مثال على استخدام الكلمات الأساسية في اسم دالة. في هذا النموذج ، يتم ترميز أسماء الوسائط في اسم الوظيفة. على سبيل المثال ، يمكن كتابة assertEquals كـ assertExpectedEqualsActual (متوقع ، فعلي). هذا يحل إلى حد كبير مشكلة تذكر ترتيب الحجج.
فصل الأوامر والطلبات
يجب أن تفعل الوظيفة شيئًا ما أو تجيب على بعض الأسئلة ، ولكن ليس في وقت واحد. إما أن تغير الوظيفة حالة الكائن ، أو تقوم بإرجاع معلومات حول هذا الكائن. الجمع بين عمليتين في كثير من الأحيان يخلق الارتباك.
عزل محاولة / كتل الصيد
كتل محاولة / الصيد تبدو قبيحة جدا. إنهم يخلطون بين بنية الكود ويخلطون معالجة الأخطاء مع المعالجة العادية. لهذا السبب ، يوصى بفصل أجسام كتل try و catch في وظائف منفصلة.
خطأ في التعامل مع عملية واحدة
وظائف يجب إجراء عملية واحدة. خطأ معالجة عملية واحدة. هذا يعني أن الوظيفة التي تعالج الأخطاء يجب ألا تفعل أي شيء آخر. ويترتب على ذلك أنه في حالة وجود الكلمة الأساسية "try" في الوظيفة ، فيجب أن تكون الكلمة الأولى في الوظيفة ، وبعد حظر catch / وأخيراً ، لن يكون هناك شيء آخر.
بهذا يختتم الفصل 3.