شرح الكود النظيف - مقدمة عملية للترميز النظيف للمبتدئين

"أي أحمق يمكنه الكتابة رمز يستطيع الكمبيوتر فهمه. يكتب المبرمجون الجيدون كودًا يمكن للبشر فهمه ". - مارتن فاولر

تعد كتابة كود واضح ومفهوم وقابل للصيانة مهارة ضرورية لكل مطور لإتقانها.

في هذا المنشور ، سنلقي نظرة على أهم المبادئ لتحسين جودة الكود وسأقدم لك أمثلة على التعليمات البرمجية لكل منها.

معظم الأمثلة مأخوذة من الكود النظيف لروبرت جيه مارتن . إنها برمجة كلاسيكية وأقترح عليك قراءة النص بالكامل عندما يكون لديك وقت.

كيفية تسمية المتغيرات (وأشياء أخرى)

"هناك شيئان صعبان فقط في علوم الكمبيوتر: إبطال ذاكرة التخزين المؤقت وتسمية الأشياء." - فيل كارلتون

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

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

كيف تصنع أسماء ذات مغزى

لا تستخدم التعليقات لشرح سبب استخدام متغير. إذا كان الاسم يتطلب تعليقًا ، فيجب أن تأخذ وقتك في إعادة تسمية هذا المتغير بدلاً من كتابة تعليق.

"يجب أن يخبرك الاسم عن سبب وجوده ، وماذا يفعل ، وكيف يتم استخدامه. إذا كان الاسم يتطلب تعليقًا ، فإن الاسم لا يكشف عن نيته." - كود نظيف

سيئة:

var d; // elapsed time in days

لقد رأيت هذا النوع من الكود مرات عديدة. من المفاهيم الخاطئة الشائعة أنه يجب عليك إخفاء الفوضى بالتعليقات. لا تستخدم أحرفًا مثل x أو y أو a أو b كأسماء متغيرات ما لم يكن هناك سبب وجيه (متغيرات الحلقة استثناء لهذا).

حسن:

var elapsedTimeInDays; var daysSinceCreation; var daysSinceModification;

هذه الأسماء أفضل بكثير. يخبرونك بما يتم قياسه ووحدة هذا القياس.

تجنب التضليل

كن حذرًا بشأن الكلمات التي تعني شيئًا محددًا. لا تشر إلى مجموعة من الحسابات كقائمة حسابات إلا إذا كان نوعها عبارة عن قائمة بالفعل. الكلمة لها معنى محدد وقد تؤدي إلى استنتاجات خاطئة.

حتى لو كان النوع عبارة عن قائمة ، فإن الحسابات هي اسم أبسط وأفضل.

سيئة:

var accountList = [];

حسن:

var accounts = []

تجنب الكلمات المزعجة

الكلمات المزعجة هي الكلمات التي لا تقدم أي معلومات إضافية حول المتغير. فهي زائدة عن الحاجة ويجب إزالتها.

بعض كلمات الضجيج الشائعة هي:

  • (البادئة)
  • معلومات
  • البيانات
  • متغير
  • موضوع
  • مدير

إذا تم تسمية فصلك الدراسي باسم UserInfo ، يمكنك فقط إزالة المعلومات وجعلها مستخدمًا. يعد استخدام BookData بدلاً من Book كاسم فئة أمرًا لا يحتاج إلى تفكير ، حيث يقوم الفصل بتخزين البيانات على أي حال.

يمكنك أيضًا قراءة منشور مدونة Jeff Atwood حول تسمية SomethingManager هنا.

استخدم الأسماء التي يمكن نطقها

إذا كنت لا تستطيع نطق اسم ، فلا يمكنك مناقشته دون أن تبدو سخيفًا.

سيئة:

const yyyymmdstr = moment().format("YYYY/MM/DD"); 

حسن:

const currentDate = moment().format("YYYY/MM/DD");

استخدم أسماء قابلة للبحث

تجنب استخدام الأرقام السحرية في التعليمات البرمجية الخاصة بك. اختر الثوابت المسماة القابلة للبحث. لا تستخدم أسماء ذات حرف واحد للثوابت لأنها يمكن أن تظهر في العديد من الأماكن وبالتالي لا يمكن البحث عنها بسهولة.

سيئة:

if (student.classes.length < 7) { // Do something }

حسن:

if (student.classes.length < MAX_CLASSES_PER_STUDENT) { // Do something }

هذا أفضل بكثير لأنه يمكن استخدام MAX_CLASSES_PER_STUDENT في العديد من الأماكن في التعليمات البرمجية. إذا احتجنا إلى تغييره إلى 6 في المستقبل ، فيمكننا فقط تغيير الثابت.

المثال السيئ يخلق علامات استفهام في ذهن القارئ ، مثل ما أهمية الرقم 7؟

يجب عليك أيضًا الاستفادة من اصطلاحات التسمية والإعلان الثابتة في لغتك مثل الخاص الثابت النهائي في Java أو const في JavaScript.

كن متسقا

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

كيف تكتب الدالات

اجعلها صغيرة

يجب أن تكون الوظائف صغيرة وصغيرة جدًا. ونادرًا ما يكون طولها 20 سطرًا. كلما طالت الوظيفة ، زاد احتمال قيامها بأشياء متعددة ولها آثار جانبية.

تأكد من أنهم يفعلون شيئًا واحدًا فقط

يجب أن تفعل الوظائف شيئًا واحدًا. يجب أن يفعلوا ذلك بشكل جيد. يجب أن يفعلوا ذلك فقط. - كود نظيف

Your functions should do only one thing. If you follow this rule, it is guaranteed that they will be small. The only thing that function does should be stated in its name.

Sometimes it is hard to look at the function and see if it is doing multiple things or not. One good way to check is to try to extract another function with a different name. If you can find it, that means it should be a different function.

This is probably the most important concept in this article, and it will take some time to get used to. But once you get the hang of it, your code will look much more mature, and it will be more easily refactorable, understandable, and testable for sure.

Encapsulate Conditionals in Functions

Refactoring the condition and putting it into a named function is a good way to make your conditionals more readable.

Here is a piece of code from a school project of mine. This code is responsible for inserting a chip on the board of the Connect4 game.

The isValidInsertion method takes care of checking the validity of the column number and allows us the focus on the logic for inserting the chip instead.

public void insertChipAt(int column) throws Exception { if (isValidInsertion(column)) { insertChip(column); boardConfiguration += column; currentPlayer = currentPlayer == Chip.RED ? Chip.YELLOW : Chip.RED; } else  if (!columnExistsAt(column)) throw new IllegalArgumentException(); else if (isColumnFull(column - 1)  }

Here is the code for isValidInsertion, if you are interested.

 private boolean isValidInsertion(int column) { boolean columnIsAvailable = column = 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS; boolean gameIsOver = getWinner() != Chip.NONE; return columnIsAvailable && !gameIsOver; } 

Without the method, if condition would look like this:

if (column = 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS && getWinner() != Chip.NONE)

Gross, right? I agree.

Fewer Arguments

Functions should have two or fewer arguments, the fewer the better. Avoid three or more arguments where possible.

Arguments make it harder to read and understand the function. They are even harder from a testing point of view, since they create the need to write test cases for every combination of arguments.

Do not use Flag Arguments

A flag argument is a boolean argument that is passed to a function. Two different actions are taken depending on the value of this argument.

For example, say there is a function that is responsible for booking tickets to a concert and there are 2 types of users: Premium and Regular. You can have code like this:

 public Booking book (Customer aCustomer, boolean isPremium) { if(isPremium) // logic for premium book else // logic for regular booking }

Flag arguments naturally contradict the principle of single responsibility. When you see them, you should consider dividing the function into two.

Do Not Have Side Effects

Side effects are unintended consequences of your code. They may be changing the passed parameters, in case of passing by reference, or maybe changing a global variable.

The key point is, they promised to do another thing and you need to read the code carefully to notice the side-effect. They can result in some nasty bugs.

Here is an example from the book:

public class UserValidator { private Cryptographer cryptographer; public boolean checkPassword(String userName, String password) { User user = UserGateway.findByName(userName); if (user != User.NULL) { String codedPhrase = user.getPhraseEncodedByPassword(); String phrase = cryptographer.decrypt(codedPhrase, password); if ("Valid Password".equals(phrase)) { Session.initialize(); return true; } } return false; } }

Can you see the side-effect of this function?

It is checking the password, but when the password is valid, it is also initializing the session which is a side-effect.

You can change the name of the function to something like checkPasswordAndInitializeSession to make this effect explicit. But when you do that, you should notice that your function is actually doing two things and you should not initialize the session here.

Don't Repeat Yourself

Code repetition may be the root of all evil in software. Duplicate code means you need to change things in multiple places when there is a change in logic and it is very error prone.

Use your IDE's refactoring features and extract a method whenever you come across a repeated code segment.

Bonus

Do not leave code in comments

Please, do not. This one is serious because others who see the code will be afraid to delete it because they do not know if it is there for a reason. That commented out code will stay there for a long time. Then when variable names or method names change, it gets irrelevant but still nobody deletes it.

Just delete it. Even if it was important, there is version control for that. You can always find it.

Know your language's conventions

You should know your language's conventions in terms of spacing, comments, and naming things. There are style guides available for many languages.

For example, you should use camelCase in Java but snake_case in Python. You put opening braces on a new line in C# but you put them on the same line in Java and JavaScript.

These things change from language to language and there is no universal standard.

إليك بعض الروابط المفيدة لك:

  • دليل أسلوب بايثون
  • دليل أسلوب جافا سكريبت من Google
  • دليل أنماط جافا من Google

استنتاج

الترميز النظيف ليس مهارة يمكن اكتسابها بين عشية وضحاها. إنها عادة يجب تطويرها من خلال وضع هذه المبادئ في الاعتبار وتطبيقها كلما كتبت رمزًا.

شكرًا لك على الوقت الذي قضيته في القراءة وآمل أن تكون مفيدة.

إذا كنت مهتمًا بقراءة المزيد من المقالات مثل هذه ، فيمكنك الاشتراك في مدونتي.