27 Ağustos 2018 Pazartesi

Kodlama Standardı

Giriş
Kodlama Standardı (Coding Standard veya Coding Guideline) geliştirirken bazı kararlar alınıyor. Bu kararlarla ilgili notlarım aşağıda.

Kodlama Standardı Başlıkları
Maddeleri belli başlıklar altında toplamak okunurluğu ve anlaşılabilirliği artırıyor. Kullanılan dile ve ortama göre başlık değişebilir. C++ için aşağıdaki başlıklar kullanılabilir.

General Rules
Recursive and Re-Entrant Functions
Scope of Variables
Memory Management
Range and Type Checking
Exceptions
Constants and Macros
Overriding Operators
If Clause and Loop Conditions
Pointers
Class Declarations
Templates
Variables and Methods
Function Input and Outputs
Conventions
Style
Safety Rules

Kod Gözden Geçirmesi
Kod Gözden Geçirmesi başlıklı yazıya göz atabilirsiniz.

Kodlama Standardı Yoksa
Uncle Bob'a göre kodlama standardı olmalı ancak yazılı hale getirilmemeli. Bu konuda şöyle söylüyor.
  1. Don't write them down if you can avoid it. Rather, let the code be the way the standards are captured.
Eskiden şöyle düşünüyordum
-----------
Kodlama standardı yoksa bence geliştirme faaliyeti başlamamalı. Bazı projelerde firmanın kodlama standardı yetersiz kalabiliyor. Örneğin hiç gömülü yazılım yapmamış hep web uygulaması geliştirmiş bir firmada gömülü yazılım için kodlama standardı bulunmayabilir. Bu durumda geliştirmeye başlamadan önce mutlaka kodlama standardını yazmak gerekir diye düşünüyorum.
-----------

Kodlama Standardı ve Indentation  ve Süslü Parantez Kullanımı
Bazı kodlama standartlarında Indentation "Conventions" bölümünde açıkça belirtiliyor. Sıkça gördüğüm kullanım şekilleri K&R (Kernighan ve Ritchie) ve 1TBS (One True Brace Style). Ben 1TBS taraftarıyım.

K&R Tarzı:
Tek satırlarda süslü parantez kullanılmaz.
int i;
for (i = 0; i < 10; i++)
  printf("Hi.");
Tek satırlarda süslü parantez kullanılmaz. Şöyle yapılır.
if ( a > 10 )
  std::cout << "a is above 10" << std::endl;
1TBS Tarzı:
Tek satırlarda süslü parantez kullanılır.
int i;
for (i = 0; i < 10; i++) {
  printf("Hi");
}
Commentler konusunda tarafsızım şöyle olabilir.
if (condition) {  // comment
    do something();
}
Şöyle de olabilir.
// comment
if (condition) {  
    do something();
}
Visual Studio Tarzı
Şu hale getiyor. Comment istenilen yere konulabilir.
if (condition) // comment
{  
  do something();
}

Kodlama Standardı ve Naming Convention
Her programcının hayali kodun tek bir elden çıkmış gibi görünmesidir. Bunun için bir çok kodlama standardı naming convention belirler.

Değişkenler PascalCase, camelCase, alllowercase, snake_case olabilir

Sınıflar için de benzer bir naming convention belirlenir.

Naming Convention sayesinde bir sınıfın ne iş yaptığını anlamak kolay olabilir.

Convention bir örüntüyü çağrıştırmalıdır. Model (domain sınıfları UserModel, AlbumModel) ,  Controller, Builder, Factory gibi kelimelerler biten sınıflar iyidir.

Util, Helper, Manager kelimeleri ile biten sınıflar bazen ne olduğu belirsiz  şeylere dönüşebiliyor., ancak yine de tamamen reddetmemek lazım yani bazen kabul edilebilir. Clean Code - Meaningful Names (Uncle Bob) yazısına bakabilirsiniz.

Naming convention'ın en eksik kaldığı nokta, 3. parti yazılımların, dile ait kütüphanelerin bizim standardımıza uymamasıdır. Özellikle C++'ta bu durum çok bariz göze çarpar. Eskiden kimi standard Hungarian Notation kullanırdı. Bjarne Straustrup'un standardında da kendi sınıflarımızın büyük harf ile başlaması yazıyor.
an initial capital letter for types (e.g., Square and Graph)
Böylece kendi sınıflarımızı C++'taki diğer sınıflardan ayırabilirmişiz.
The C++ language and standard library don't use capital letters
Bence en güzeli Java ve C#'taki gibi naming convention'ın dilin bir parçası olması.

Kodlama Standardı ve Continue
Döngü içinde continue olsun
foreach (someObject in someObjectList)
{
  if(someObject == null) continue;
  someOtherObject = someObject.SomeProperty;
}
Bazı kodlama standartları continue kullanımını yasaklıyor. Bu durumda kodun şu hale getirilmesi gerekir.
foreach (someObject in someObjectList) 
{
  if(someObject != null) 
  {
    someOtherObject = someObject.SomeProperty;
  }
}
Kodlama Standardı ve Include Guard
C ve C++ projeleridne bu niyeyse birçok projede problem oluyor. Herkes farklı bir tarz kullanıyor. Ben şu kullanımı seviyorum.
#ifndef MY_FILE_H
#define MY_FILE_H
...
...
#endif
Kodlama Standardı ve Parametre Sayısı
Clean Code - Number of Arguments yazısına bakabilirsiniz. 

Bence 4'ten fazla parametre fazla sayılmalı. Şu tür kodlar parametre sayısı 4'ten az olsa bile okuması zor kodlar.
var a = F(G1(H1(b1), H2(b2)), G2(c1));
Kodlama Standardı ve Global Değişkenler
Açıklaması şöyle. Global değişkenler parametre sayısının fazla olmasından bile kötü.
Using global variables is a worse practice than more parameters irrespective of the qualities you described. My reasoning is that more parameters may make a method more difficult to understand, but global variables can cause many problems for the code including poor testability, concurrency bugs, and tight coupling. No matter how many parameters a function has, it won't inherently have the same problems as global variables.
Kodlama Standardı ve Magic Numbers veya Sabitler
Açıklaması şöyle. Yani birimler ve bu birimlerin neyi temsil ettiği belirtilmeli
The issue is not only with the lack of units, but the fact that it is not clear what three of those units represent. Do you only have three minutes to complete a task? Then the constant might be better named as MAXIMUM_TASK_DURATION. Is three gallons the capacity of some container? Then we could use the name CONTAINER_CAPACITY.
Şöyle olabilir
int MAXIMUM_TASK_DURATION_MINUTES = 3;
Kodlama Standardı ve Return Value on All Control Paths
Örnek
Elimizde şöyle bir kod olsun. Bu kod int ile çağrılırsa masum görünüyor ancak double il çağrılırsa ve t parametresi NaN ise hatalı.
template <typename T>
int h1(const T& t){
  if (t < 1){
    return 0;
  } else if (t >= 1){
    return 1;
  }
}
Şöyle yapmak daha iyi
if (t < 1) {
    return 0;
}
return 1;
Kodlama Standardı ve Single Function Exit Point
Single Function Exit Point Tek Değer Dönme Noktası olarak çevriliyor. Bazı kodlama standartlarında bir metod içinde tek return olması isteniyor. Bu kural ağır bir kural.

Validation Check yazısında bu kurala uymayan örnekler var ve bence kodu çok dah anlaşılır hale getiriyor.

Bu kadar ağır kurala uymak istemeyenler "Return Value on All Control Paths" kuralını uygulasalar da yeterli olabilir.

Örnek
Basit bir for düngüsü olsun
for (int x : array)
{
  if (x == value)
    return true;
}
return false;  
Bu kodu şu hale getirebiliriz.
int i = 0;
while (i < array.length && array[i] != value)
    i++;
return i < array.length;
Kodlama Standardında Fonksiyon Uzunluğu Olmalı mı ?
Bence olmamalı. Zaten rakam üzerinde uzlaşma da yok . Code Complete (Steve McConnell) kitabında 100-200 satıra kadar uygundur deniliyor. Clean Code kitabında ise en fazla 20 satır olmalı denilmiş. Yapılan işe ve alana göre değişken bir rakam. Unzluk ile ilgili bir açıklama şöyle
Consider the practice of "splitting code from a long method into several private methods".

Robert C. Martin says that this style allows for limiting the contents of each method to one level of abstraction - as a simplified example, a public method would probably only consist of calls to private methods like verifyInput(...), loadDataFromHardDisk(...), transformDataToJson(...) and finally sendJsonToClient(...), and these methods would have the implementation details.

  • Some people like this because readers can get a quick overview of the high-level steps and can choose which details they want to read about.
  • Some people dislike it because when you want to know all the details, you have to jump around in the class to follow allong the execution flow (this is what JacquesB likely refers to when he writes about adding complexity).

The lesson is: all of them are right, because they are entitled to have an opinion.
Inline Expression
Code Complete (Steve McConnell) kitabındaki açıklaması şöyle
Move an expression inline Replace an intermediate variable that was assigned the result of an expression with the expression itself.
Yani geçici değişkenler kullanmayın deniliyor. İkinci kod parçasını savunuyor. Ben katılmıyorum.
void foo()
{
  int number_of_elements = (function_1() + function_2()) / function_3(); //expression

  write(number_of_elements);
}

//Bu tercih edilmeli deniliyor
void foo()
{
  write((function_1() + function_2()) / function_3());
}

Bazı Maddelerle İlgili Notlarım
Aşağıdaki maddeler kodda olmasa iyi olan şeyler. Ancak kodlama standardına da eklemeye gerek yok. Sadece best practice gibi düşünülebilir.


1. Çok Fazla İç İçe If Kullanılması
Çok fazla iç içe if kullanılması iyi değildir.
if (somecondition) {
  if (somcecondition2) {
     if(somecondition3) {
       if(somecindition4) {
         ....
           if (somecondition10) {
               ...
               ...
               ...
               //20 lines of code after
               return "hello world";
            }
         }
      }
      ...
      ...
      ...
      return "hello monday";
   }
}
...
...
...
return "hello earth";

2. Kötü Çoklu Return Kullanan Örnekler
Yukarıdaki örneklerden de görüldüğü gibi çoklu return kullanılması her zaman kötü değildir. Ancak bazen gerçekten de anlaşılması güç kod parçalarına sebep olabiliyor. Örnek okuması ve anlaması zor bir kodu temsil ediyor. Bu yüzden aşağıdaki tek değer dönme noktası tavsiye ediliyor.
int function() {
     if (bidi) { print("return 1"); return 1; }
     for (int i = 0; i < n; i++) {
       if (vidi) { print("return 2"); return 2;}
     }
     print("return 3");
     return 3;
  }
3. Verilen Parametrelerin Doğruluğunu Kontrol Etmek
Validation Check yazısına bakınız.

4. Crash Early
Validation Check yazısına bakınız.
5. Sonsuz Döngüler
Sonsuz döngüler için aşağıdakilerden herhangi biri kullanılabilir.
for(;;) {}
while(1) {} / while(true) {}
do {} while(1) / do {} while(true)
Ben while (true) {...} şeklinde olanları tercih ediyorum.

Hiç yorum yok:

Yorum Gönder