تسرد هذه المقالة بعض الأخطاء النموذجية التي أراها في كود Java الخاص بزملائي من حولي. من الواضح أن تحليل الكود الثابت (يستخدم فريقنا qulice) لن يتمكن من العثور على جميع المشكلات، ولهذا السبب أدرجتها هنا.
إذا كنت تعتقد أن هناك شيئًا مفقودًا، فيرجى إبلاغي بذلك وسيسعدني إضافته.
جميع هذه الأخطاء المذكورة أدناه تتعلق بشكل أساسي بالبرمجة الموجهة للكائنات، وخاصةً OOP الخاص بـ Java.
اسم الفئة
اقرأ هذه المقالة القصيرة "ما هي الأشياء". يجب أن تكون الفئة كيانًا مجردًا في الحياة الواقعية، وليس "المدققين" و"المتحكمين" و"المديرين". إذا كان اسم صفك ينتهي بـ "er" - فهذا تصميم سيء.
بالطبع، تعتبر فئات الأدوات أيضًا مضادة للأنماط، مثل StringUtils وFileUtils وIOUtils من Apache. كل ما سبق هو أمثلة على التصميم السيئ. مزيد من القراءة: بدائل لفئات الأدوات في OOP.
بالطبع، لا تستخدم البادئات أو اللواحق لتمييز الفئات عن الواجهات. على سبيل المثال، هذه الأسماء خاطئة: IRecord أو IfaceEmployee أو RecordInterface. بشكل عام، يجب أن يكون اسم الواجهة هو اسم الكيان الواقعي، ويجب أن يصف اسم الفئة تفاصيل التنفيذ الخاصة به. إذا لم يكن هناك شيء خاص بالتنفيذ، فيمكنك تسميته افتراضيًا أو بسيطًا أو شيئًا مشابهًا. على سبيل المثال:
انسخ رمز الكود كما يلي:
فئة SimpleUser تنفذ المستخدم {}؛
فئة DefaultRecord تنفذ السجل {}؛
فئة لاحقة تنفذ الاسم {}؛
تم التحقق من صحة الفئة تنفذ المحتوى {}؛
اسم الطريقة
يمكن للطرق إرجاع القيم أو الفراغ. إذا قامت إحدى الطرق بإرجاع قيمة، فيجب أن يصف اسمها ما تُرجعه، على سبيل المثال (لا تستخدم مطلقًا بادئة get):
انسخ رمز الكود كما يلي:
منطقية صالحة (اسم السلسلة)؛
محتوى السلسلة ()؛
int ageOf(ملف الملف);
إذا عادت فارغة، فيجب أن يوضح اسمها ما تفعله. على سبيل المثال:
انسخ رمز الكود كما يلي:
حفظ باطل (ملف ملف)؛
عملية باطلة(عمل العمل);
إلحاق باطلة (ملف ملف، سطر سلسلة)؛
هناك استثناء واحد فقط للقواعد المذكورة للتو - لا يتم احتساب طريقة اختبار JUnit. سيتم مناقشة هذا أدناه.
اسم طريقة الاختبار
في حالات اختبار JUnit، يجب أن يكون اسم الطريقة عبارة باللغة الإنجليزية بدون مسافات. وسيكون الأمر أكثر وضوحا مع مثال:
انسخ رمز الكود كما يلي:
/**
* يمكن لـ HttpRequest إرجاع محتواه بتنسيق Unicode.
* @throws Exception إذا فشل الاختبار
*/
إرجاع الفراغ العامItsContentInUnicode() يلقي استثناء {
}
يجب أن تبدأ الجملة الأولى في JavaDoc باسم الفصل الذي تريد اختباره، متبوعًا بـ can. لذلك، يجب أن تكون الجملة الأولى شيئًا مثل "يمكن لشخص ما أن يفعل شيئًا ما".
اسم الطريقة هو نفسه أيضًا، فقط بدون السمة. إذا قمت بإضافة موضوع في منتصف اسم الطريقة، أحصل على جملة كاملة، كما في المثال أعلاه: "يرجع HttpRequest محتواه بتنسيق Unicode".
يرجى ملاحظة أن اسم طريقة الاختبار لا يبدأ بـ can. فقط التعليقات في JavaDoc ستبدأ بـ can. بالإضافة إلى ذلك، يجب ألا تبدأ أسماء الطرق بفعل.
من الناحية العملية، من الأفضل الإعلان عن طريقة الاختبار لطرح الاستثناء.
اسم متغير
تجنب دمج أسماء المتغيرات، مثل timeOfDay، أو firstItem، أو httpRequest. ينطبق هذا على متغيرات الفئة والمتغيرات ضمن الأساليب. يجب أن تكون أسماء المتغيرات طويلة بما يكفي لتجنب الغموض في نطاقها المرئي، ولكن ليست طويلة جدًا إن أمكن. يجب أن يكون الاسم اسمًا بصيغة المفرد أو الجمع، أو اختصارًا مناسبًا. على سبيل المثال:
انسخ رمز الكود كما يلي:
قائمة <سلسلة> الأسماء؛
void sendThroughProxy(File file, Protocol proto);
محتوى الملف الخاص؛
طلب HttpRequest العام؛
في بعض الأحيان، إذا قام المُنشئ بحفظ معلمات الإدخال في كائن تمت تهيئته حديثًا، فقد تتعارض أسماء معلماته وسمات فئته. في هذه الحالة، اقتراحي هو إزالة حروف العلة واستخدام الاختصارات.
مثال:
انسخ رمز الكود كما يلي:
رسالة الطبقة العامة {
مستلم سلسلة خاصة؛
الرسالة العامة (سلسلة rcpt) {
this.recipient = rcpt;
}
}
في كثير من الأحيان، يمكنك معرفة الاسم الذي يجب أن يُعطى للمتغير من خلال النظر إلى اسم فئته. ما عليك سوى استخدام شكله الصغير، والذي يمكن الاعتماد عليه مثل هذا:
انسخ رمز الكود كما يلي:
ملف الملف؛
مستخدم مستخدم؛
فرع فرع؛
ومع ذلك، يجب ألا تفعل ذلك أبدًا مع الأنواع البدائية، مثل الرقم الصحيح أو السلسلة النصية.
إذا كان هناك متغيرات متعددة ذات طبيعة مختلفة، فكر في استخدام الصفات. على سبيل المثال:
انسخ رمز الكود كما يلي:
جهة اتصال السلسلة (سلسلة لليسار، سلسلة لليمين)؛
منشئ
بغض النظر عن الاستثناءات، يجب أن يكون هناك منشئ واحد فقط يستخدم لتخزين البيانات في متغيرات الكائن. يقوم المنشئون الآخرون باستدعاء هذا المنشئ بمعلمات مختلفة. على سبيل المثال:
انسخ رمز الكود كما يلي:
خادم الطبقة العامة {
عنوان السلسلة الخاص؛
الخادم العام (سلسلة أوري) {
this.address = uri;
}
الخادم العام (URI uri) {
this(uri.toString());
}
}
متغير لمرة واحدة
وينبغي تجنب المتغيرات التي يمكن التخلص منها بأي ثمن. ما أعنيه بـ "مرة واحدة" هنا هو متغير يستخدم مرة واحدة فقط. على سبيل المثال، هذا:
انسخ رمز الكود كما يلي:
اسم السلسلة = "data.txt"؛
إرجاع ملف جديد (الاسم)؛
يتم استخدام المتغيرات المذكورة أعلاه مرة واحدة فقط، لذلك يمكن إعادة هيكلة هذا الكود على النحو التالي:
انسخ رمز الكود كما يلي:
إرجاع ملف جديد("data.txt");
في بعض الأحيان، في حالات نادرة - بشكل أساسي من أجل تنسيق ذو مظهر أفضل - يمكن استخدام متغيرات يمكن التخلص منها. ومع ذلك، ينبغي تجنب ذلك قدر الإمكان.
غير طبيعي
وغني عن القول، لا ينبغي عليك أبدًا أن تبتلع أي استثناء بنفسك، ولكن يجب أن تتجاهله قدر الإمكان. يجب أن تقوم الأساليب الخاصة دائمًا بطرح الاستثناءات المحددة.
لا تستخدم الاستثناءات للتحكم في التدفق. على سبيل المثال، الكود التالي خاطئ:
انسخ رمز الكود كما يلي:
حجم كثافة العمليات؛
يحاول {
الحجم = this.fileSize();
} قبض على (IOException على سبيل المثال) {
الحجم = 0؛
}
فماذا يجب أن تفعل إذا كان IOException يطالبك بأن "القرص ممتلئ"؟ هل مازلت تعتقد أن حجم الملف هو 0 وتستمر في المعالجة؟
المسافة البادئة
فيما يتعلق بالمسافة البادئة، فإن القاعدة الأساسية هي أن قوس الفتح إما ينتهي عند نهاية السطر أو يكون مغلقًا على نفس السطر (والعكس صحيح بالنسبة لأقواس الإغلاق). على سبيل المثال، ما يلي غير صحيح لأن قوس الفتح الأول غير مغلق على نفس السطر وهناك أحرف أخرى بعده. يمثل القوس الثاني مشكلة أيضًا لأنه يسبقه أحرف ولكن القوس الافتتاحي المقابل ليس على نفس السطر:
انسخ رمز الكود كما يلي:
ملف الملف النهائي = ملف جديد (الدليل،
"ملف.txt")؛
المسافة البادئة الصحيحة يجب أن تكون هكذا:
انسخ رمز الكود كما يلي:
StringUtils.join(
المصفوفات. كقائمة (
"السطر الأول"،
"السطر الثاني"،
StringUtils.join(
Arrays.asList("أ"، "ب")
)
)،
"الفاصل"
);
القاعدة الثانية المهمة حول المسافة البادئة هي أنه يجب عليك محاولة كتابة أكبر عدد ممكن من الأحرف على السطر في نفس الوقت - الحد الأعلى هو 80 حرفًا. المثال أعلاه لا يفي بهذه النقطة، ويمكن أيضًا تصغيره:
انسخ رمز الكود كما يلي:
StringUtils.join(
المصفوفات. كقائمة (
"السطر الأول"، "السطر الثاني"،
StringUtils.join(Arrays.asList("a"، "b"))
)،
"الفاصل"
);
الثوابت الزائدة
يجب استخدام ثوابت الفصل عندما تريد مشاركة المعلومات ضمن أساليب الفصل الدراسي. يجب أن تكون المعلومات فريدة لفصلك. لا تستخدم الثوابت كبدائل للسلسلة أو القيم الحرفية الرقمية - فهذه ممارسة سيئة للغاية وستؤدي إلى تلويث التعليمات البرمجية الخاصة بك. الثوابت (مثل أي كائن في OOP) يجب أن يكون لها معناها الخاص في العالم الحقيقي. دعونا نرى ما تعنيه هذه الثوابت في الحياة الواقعية:
انسخ رمز الكود كما يلي:
مستند الفئة {
سلسلة نهائية ثابتة خاصة D_LETTER = "D"؛
ملحق السلسلة النهائية الثابتة الخاصة = ".doc";
}
خطأ شائع آخر هو استخدام الثوابت في اختبارات الوحدة لتجنب السلسلة الزائدة أو القيم الحرفية الرقمية في طرق الاختبار. لا تفعل هذا! يجب أن يكون لكل طريقة اختبار قيم إدخال فريدة خاصة بها.
استخدم نصًا أو قيمًا جديدة في كل طريقة اختبار جديدة. إنهم مستقلون عن بعضهم البعض. فلماذا لا يزالون يتشاركون نفس ثوابت الإدخال؟
اختبار اقتران البيانات
فيما يلي مثال على اقتران البيانات في طريقة الاختبار:
انسخ رمز الكود كما يلي:
مستخدم المستخدم = مستخدم جديد("جيف");
// ربما بعض التعليمات البرمجية الأخرى هنا
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));
في السطر الأخير، يقترن "Jeff" بنفس السلسلة الحرفية في السطر الأول. إذا أراد شخص ما، بعد بضعة أشهر، تغيير القيمة في السطر الثالث، فسيتعين عليه قضاء بعض الوقت في معرفة مكان استخدام "Jeff" أيضًا بنفس الطريقة.
لتجنب ذلك، من الأفضل إدخال متغير.