Programlamada Adlandırma Alışkanlıkları
Yaptığımız bir adlandırmanın anlaşılır bir tanımlayıcı isme sahip olması için edinmemiz gereken alışkanlıklar bulunuyor. Bir tarafta, kısmen her yazılımcı grubunun benimsediği ortak yöntemler var. Diğer taraftaysa, geliştirme yaparken kullandığınız dilin/ortamın mimarları tarafından tavsiye edilen, onların tecrübelerinden çıkan yöntemler vardır. Bu yazı ile birlikte bu konuyu detaylandırmaya çalışarak, anlatmayı deneyeceğim. Örneklerimin kaynakları, gerçek hayattaki uygulamaları ise .Net geliştirme platformuna ait olacak.
1. Identifier
Identifier bir isimdir. Bu isimlendirmeyi yazılımı kodlayan kişiler yapar. Birbirinden farklı amaçlar ile ulaşılması için verilen bu isme, tanımlayıcı(identifier) veya tanımlayıcı isim, ad(identifier name) denir.
Aşağıda iki farklı dilde yapılan ve aynı amaca hizmet eden tanımlamaları görebilirsiniz.
Go
type Person struct {
Name string
}
C#
public class Person
{
public string Name { get; set; }
}
Class/struct için seçilen tanımlayıcı ad Person ve ona ait bir özellik için seçilen tanımlayıcı ad ise Name ve burada yapılan işlemse bir class’ı/struct’ı ve ona ait özelliği tanımlamadır.
1.1. Identifierlar için Büyük ve Küçük Harf Kullanımı
Programlamada herhangi bir şeyi tanımlamanın anlaşılır olması için birden fazla kelimeye ihtiyaç varsa, bu kelimeleri birbirinden ayırmak için alt çizgi veya buna benzer ayırıcı bir sembol kullanmanıza gerek yoktur ve bu gibi sembolleri istisnai durumlar hariç elinizden geldiğince tanımlayıcı isimlerin içerisinde kullanmamanız iyi olur. Tanımlama yaparken tavsiye edilen iki kural vardır. Bunlar:
- PascalCasing
- camelCasing
PascalCasing düzeni: parametre tanımlamaları hariç, bütün public tanımlamalarda kullanılması tavsiye edilen düzendir. Bu düzenin kuralı, tanımlamadaki bütün kelimelerin ilk harfleri büyük olmalıdır, geriye kalanlarsa kısa. Eğer bilinen bir kısaltma kullanılacaksa ve bu kısaltma üç harften kısaysa, kısaltılmış terime ait harfler büyük harf olmalıdır.
Kısaltmaların bulunduğu durumlar için PascalCasing ile identifier örnekleri:
HtmlElements
IOStream
XmlNodes
Sıklıkla bilinmeyen bir kısaltmayı kullanmamalısınız. O terimin kısaltmasını kullanmak yerine, apaçık bir şekilde uzun halini kullanmalısınız.
Aşağıda görülen namespace, class ve interface tanımlamaları yapılırken PascalCase kullanılmış.
public namespace System { ... }public class StreamReader { ... }public interface IEnumerable { ... }
Int32 data type’ı ve ona ait public bir method tanımlanırken PascalCase kullanılmış.
public struct Int32 { public override string ToString(); }
String data type’ı ve ona ait public bir property tanımlanırken PascalCase kullanılmış.
public class String
{
public int Length { get; }
}
camelCasing düzeni: parametrelerde, private class/struct memberlar’da ve local olarak yapılan tanımların isimlendirmelerinde kullanılmalıdır. Bu düzenin kuralıysa, ilk kelimenin ilk harfi küçük olmalıdır, ondan sonraki bütün kelimeler büyük harf ile başlamalıdır, diğer harfler ise kısa. Kısaltmalar için istisnai bir durum yoktur.
Hangi durumlarda kesinlikle camelCasing kullanılması öneriliyor:
- Parametre isimlerinde
public void Add(string fullName, int studentNumber, int foo) {...}
- Private, internal member(metot, field ve diğerleri) isimlerinde
private void add() {...} // iyi olur
private void _add() {...} // eğer ki public olan başka bir methodu
destekliyorsa bu şekilde olması daha
doğruprivate string _fullName;
- Local variable isimlerinde
public void Do(int id, string anotherParameter)
{
bool localVariableName;
...
...
}
Kısaltma barındıran ve barındırmayan identifier isimlerine, camelCasing ile örnekler;
htmlElements
ioStream
xmlNodes
items
fullName
Kısaca
Namespace, type, bir class veya struct ve onların içindeki memberlar, bunların hepsinde dışarıya açık kullanıma sahip olan bileşenlerin tanımlayıcı ismi için PascalCase düzenini uygulanmalı.
Bir insanın ad ve soyadını birlikte tuttuğunuz bir property için Fullname veya Full_name kullanmak yerine FullName kullanın. Tek kelimelik bir örnek içinse, bir kişinin adını tuttuğunuz bir propertyi Name olarak adlandırın.
Private/internal/local tanımlamalar ve parametreler için camelCasing kullanın.
1.2. Identifierlar İçin Ön Ek Kullanımı
Geliştiriciler arasında tanımlamalar yaparken ön ek kullanımı ile ilgili bir tartışma var.
Geliştiricilerin bir kısmı tanım yaparken, identifierların önüne artık herhangi bir ek getirmemize gerek yok diyor. Bunu şuna dayanarak söylüyorlar; artık IDE’ler gelişti ve bir identifier’in üzerine geldiğimizde, ona ait bilgileri kolayca görebiliyoruz. Bu sebepten herhangi bir ön eke ihtiyacımız yok, diyorlar.
Geliştiricilerin diğer bir kısmı ise, her zaman bir IDE’ye ulaşacak ortama sahip olmayabilirim. Bir web sayfasında veya IDE’ye ulaşamayacağım ortamlarda yapacağım code-review esnasında, okuduğum identifier’ın nasıl tanımlandığını, tanımlandığı yeri kontrol etmeye ihtiyaç duymadan anlamak isterim, diyor.
Anladığım kadarıyla,
.Net geliştirme ortamına katkıda bulunanların söylediğiyse özet olarak şu: Biz framework’lerimizi geliştirirken identifier’lar için ön ek konusunda şu kuralları(aşağıda bahsettiğim) benimsedik, sizde .Net’de geliştirme yaparken bunlara uyabilirsiniz.
İki yazılımcı grubunun ve .Net geliştirme ortamında emeği olanların söyledikleri dışında Robert C. Martin’inin de bu konudaki cümlesini hatırlatmak isterim. Uncle Bob, Clean Code kitabında bu konuyla alakalı şunu söylüyor: “Classlarınız ve fonksiyonlarınız, ön eklere ihtiyaç duymayacak kadar küçük olmalı.”
.Net developerları ile bu konuda çatıştığı noktalar var. Bob amca, this keywordunun daha sık kullanılmasını istiyor, “m_” “s_” veya “t_” ön eklerinin hiç kullanılmamasını ve “_” ön ekinin daha az kullanılması gerektiğini düşünüyor.
Class/struct içerisinde public bir member’i destekleyen, internal veya private olan başka bir field tanımladığınız zaman “_”, “m_” ön eklerini kullanabilirsiniz. Aynı zamanda class/struct içindeki internal veya private olan static bir field için “s_” ön ekinin kullanılması tercih ediliyor. Son olarak, internal veya private olan thread-static tanımlamalı bir field için .Net platformu geliştiricileri “t_” ön ekini tavsiye ediyorlar.
“_” — “m_” örnekler
public class Person
{
private string _fullName; // başka bir member(property) için
field public string FullName // property
{
get { return _fullName; }
set { _fullName = value; }
}
}
Buradaki Person classının bir fieldı ve bir propertysı var. Tanımlanan field, class’ın içerisindeki bir propertynın örneği olarak kullanıldığını belirtmek için önüne “_” getirip, camelCase düzenini kullandık.
Fotoğrafta gösterilen yapıyı ayrıntılı incelemek için, https://referencesource.microsoft.com/#mscorlib/system/int32.cs,225942ed7b7a3252 adresini ziyaret edebilirsiniz.
Fotoğrafta gösterilen yapıyı ayrıntılı incelemek için, https://referencesource.microsoft.com/#mscorlib/system/array.cs,1b80aad630219ba5,references adresini ziyaret edebilirsiniz.
This keyword
“_” veya “m_” ön ekinin, class/struct içindeki kullanımlarına bir alternatif ararsanız this keyword’unu araştırabilirsiniz.
This keyword’unu elinizden geldiğince kullanmamanız tavsiye ediliyor. This keyword’unu, “m_”, “_” ön ekleri ile aynı şeyi ifade edecek identifier türetme şansımız kalmadığında kullanılması tavsiye ediliyor.
Bu tavsiye .Net geliştirme ortamının mimarları tarafından yapılan bir tavsiye.
public class Thing
{
private class ClassOne
{
private string _programName;
}private sealed class ClassTwo
{
private string programName; // this keywordu ile
ulaşman iyi olur
private string _programDetail;
}
}
“_programName” identifier’i yukarıdaki ClassOne için kullanılmış ve aşağıdaki classda da onunla aynı amaç için kullanılacak bir field daha var. ClassTwo’da “_” ön eki kullanılmadan bir tanım yapılmış. ClassTwo’da ki “programName” e ulaşılırken this kullanılması tercih edilecek.
Aynı class/struct içerisinde, üçüncü bir class’da daha “program name” ifadesini anlatacak üçüncü bir field’e ihtiyaç olsaydı, ona nasıl bir ad verebilirdik ve nasıl ulaşmalıydık?
“s_” — “t_” örnekler
public class SomeClass
{
[ThreadStatic]
private static byte[] t_buffer; private static int s_count;
}
Class/struct içerisinde internal/private bir member tanımlıyorsanız ve bu member herhangi bir public member’ı veya bulunduğu class’ı/struct’ı destekliyorsa, ona bir ön ek(“_”, “m_”) verebilirsiniz veya ona erişirken this kullanabilirsiniz. Ön ek tercih ediliyorsa, sadece class’ı/struct’ı destekleyen bir member için “m_”, class/struct içindeki bir member’i destekleyen başka bir member için “_” kullanılabilir. Bunun dışında, class/struct içinde internal/private bir field static bir yapıdaysa “s_”, thread-static bir yapıdaysa ise “t_” ön ekleri kullanılabilir.
“m_” örneği için Int32 Data type’ını inceleyebilirsiniz, “sadece classı/structı destekleyen bir member” ne demek daha iyi anlarsınız.
2. Namespace Adlandırması
Namespace yazılımınızı parçalara bölmenizi sağlayan ve bu parçaları yazılımın farklı kısımlarına dahil edebildiğimiz bileşenlerdir. Namespace adlandırmalarının da, en az identifierlar kadar ilk bakışta anlaşılır olması beklenir.
<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]
- Hangi firma tarafından sunulduğu?
- Teknolojinin veya ürünün adı nedir?
- Teknolojinin veya ürünün hangi özelliği kullanılıyor?
- Seçilen bir teknoloji/ürün özelliğinin sadece bir tane spesifik alt alanı(subnamespace) dahil edilmek istendiyse bu hangisi?
.Net platformu geliştiricilerinin namespace adlandırması için hazırladığı yönergelerin yukarıda görüldüğü gibi temelde dört farklı soruya cevap vermesi isteniyor. Aynı zamanda bunu yaparken, kurduğumuz mimarının özelliklerini ve onun alt alanlarını bölebildiğimiz kadar bölmemiz tavsiye ediliyor.
Namespace adlandırılmalarında yapılması gerekenler
- Geliştirdiğiniz uygulama programlama arabirimi veya kütüphane bir markaya, şirkete veya topluluğa aitliği olacaksa bir ön ek ekleyin.
Microsoft.Office
Microsoft.AspNetCore
Microsoft.Extensions - PascalCase kullanarak isimlendirin.
- Kısaltmalar hariç, ihtiyacınızın olduğu yerde kelimeleri çoğul olarak kullanmaktan çekinmeyin.
- Namespacelerinizin hiyerarşilerini, bağlantılı olduğu teknoloji çevresinde kurun.
Namespace adlandırılmalarında yapılmaması gerekenler
- Element, Node, Message gibi yaygın olarak kullanılan adları, tek başına kullanmayın. Neyin elementi, neyin nodeu veya messagei olduğunu, spesifik olarak belirtin. Örnek: XmlNode, HtmlElement, SoapMessage.
- Namespaceiniz için kullandığınız bir ismi, o namespace içindeki classları/structları adlandırırken kullanmayın.
Debug
adında bir namespace oluşturdunuz ve oDebug
isimli namespace’in içindeki bir class’aDebug
ismini vermemelisiniz.
namespace Debug
{
public class Debug // burada debug adını kullanmamalısınız
{
}
}
3. Class ve Struct Adlandırması
Class ve struct adlarında iki seçeneğiniz var, isim yada isim tamlaması kullanarak adlandırmalısınız.
Class ve struct adlandırmalarında yapılması tavsiye edilenler:
- İsim veya isim tamlaması kullanın.
- PascalCasing ile isimlendirin.
- Türettiğiniz ismin sonuna örnek aldığınız base classın ismini verebilirsiniz.
SerializableAttribute
ArgumentOutOfRangeException
Class ve struct adlandırmalarında yapılmaması tavsiye edilenler:
- Herhangi bir ön ek vermeyin.
- Fiil kullanmayın.
- Core namespacelerın altındakiler ile çakışacak bir ismi class/type veya structlarınıza vermeyin.
Core namespaceler, .Net içinSystem
veSystem
ın altında bulunan bütün namespaceler.
Örnek: Stream ile alakalı bir işleminiz için genişletme yazdınız, bunuStream
olarak adlandırırsanız,System.IO
altında zaten var olanStream
ile çakışır.
Yazılımınızı debug ederken bir hata vermez, çalışırken de bir sorun çıkarmaz. Aynı zamandaSystem.IO
nun altındakiStream
ile birlikte kullanmak isterseniz onu da kullanabilirsiniz fakat bu tür yaygın bir ismi vermek yanlıştır. Sizin yazdığınız class, streamin spesifik bir alanı ile ilgileniyorsa onu type adınızda belirtmeniz daha iyi olur.
4. Interface Adlandırması
Interfacelerde, isim, isim tamlaması, sıfat veya sıfat tamlaması kullanabilirsiniz ve “I” ön ekini getirmeniz gerekir.
İsim veya isim tamlaması olarak tanımlanan interfaceler bir hiyerarşinin davranışlarını temsil etmek zorundadır. ArrayList type’ının temel davranışlarını inheritance aldığı arabirim: IList. IList interface’ı de ICollection’dan inheritance alıyor. Bunların hepsinin bir araya gelişi, bir hiyerarşiyi oluşturuyor.
Sıfat veya sıfat tamlaması olarak tanımlanan interfaceler classların/structların kapasitelerini temsil etmek zorundadır. Kapasiteye bir örnek vermek gerekirse: string olarak tanımlanan bir variable içindeki verinin int bir değere dönüşmesi bir yetenektir ve IConvertible, bunu temsil eder. Aynı şekilde yukarıda bahsettiğim hiyerarşinin en son IEnumerable’dan inheritance alması, o hiyerarşinin altında bulunan yapıların bir kapasitesidir.
Interface adlandırmalarında yapılması gerekenler:
- “I” ile bir ön ek verin.
- İsim veya isim tamlaması kullanabilirsiniz.
IList <T>
ICollection - Sıfat veya sıfat tamlaması kullanabilirsiniz.
IConvertible
IFormatable
IEnumerable
5. Class/Struct/Type İçindeki Memberların Adlandırılması
Classları/structları/typeları oluşturan bileşenler; field, property veya metotlar gibi daha küçük bileşenlerdir. Bu daha küçük olan programlama bileşenlerine member denir.
5.1. Metot Adlandırmaları
Bir metoda verdiğiniz identifier yaptığı işi ilk okuyuşta anlatmalıdır. Ortada bir iş olduğu için seçtiğiniz kelime bir fiil veya fiil tamlaması olmalıdır. İlk okuyuşta anlatmalıdır kısmına geri dönecek olursak, bir fiil seçerken en yaygın kelimeyi veya fiil tamlamasını kullanmalısınız. Bilinmeyen, ikinci veya üçüncü anlamı metodun yaptığı işi anımsatacak kelimeleri seçmemelisiniz.
Metot adlandırmalarında yapılması gerekenler:
- PascalCasing kullanın.
- Fiil veya fiil tamlaması kullanın.
- Seçtiğiniz kelimenin bilinen en yaygın anlamı, metodun yaptığı işi anımsatmalıdır. Eğer ki ikinci veya üçüncü anlamı yapılan işe değiniyorsa başka bir kelime bulmalısınız.
public class String
{
public int CompareTo(...);
public string[] Split(...);
public string Trim();
}
5.2. Property Adlandırmaları
Propertyleri isim, isim tamlaması veya sıfat ile adlandırabilirsiniz. Propertyler farklı türlerde datalara atıfta bulunduğu için vereceğiniz isimlerin bunları yansıtması gereklidir. Mesela bir insanın boyu(Height), bir metnin uzunluğu(Length), bir torbanın içinde kaç adet top olduğu(Count).
Property adlandırmalarında yapılması gerekenler:
- PascalCasing kullanın.
- İsim, isim tamlaması veya sıfat kullanın.
- Boolean dönüş sağlayan verilerin ismini, olumlu bir ifade ile adlandırın.
Boolean veri tiplerine isterseniz, “Can”, “Is”, “Has” ile bir ön ek verebilirsiniz. Fakat bu ön ekleri kullanırken identifiera bir değer kattığından veya okunabilirliğini götürmediğinden emin olun.CanRead
, ifadesiReadable
dan daha okunaklı ve anlamlı bir identifier olur fakatIsCreated
için aynı şeyi söylemeyeyiz,Created
daha kolay okunabilir ve aynı anlamı aktarır. Aynı şekildeIsDeleted
ileDeleted
arasında da belirgin bir fark var. - Birden fazla veriyi içinde barındırabilecek propertyler için çoğul eki verin.
public class School
{
// iyi bir adlandırma
public ICollection<Student> Students { get; set;}// kötü bir adlandırma
public ICollection<Student> Student { get; set;}// kötü bir adlandırma
public ICollection<Student> StudentList { get; set; }// kötü bir adlandırma
public ICollection<Student> StudentGroup { get; set; }
}
Property adlandırmalarında yapılmaması gerekenler:
- Get, Set ile başlayan bir metodun devamında gelen kısım, bir propertynin ismi ile eşleşmemelidir.
TextWriter adında bir string propertynız olduğu zaman,
public string TextWriter { get{ ... } set{ ... } }
TextWriter isim tamlamasını, get veya set ile başlayan bir metodun içinde geçmemelidir.
public string GetTextWriter() { ... }
Bu öneriyi, property içi data encapsulation şansı tanınmayan dillerde geliştirme yapanlar dikkate almamalıdır.
5.3. Field Adlandırmaları
Aslında field adlandırmalarına, yukarıdaki başlıklar ile epeyce bir değindim. Tekrardan kısaca bahsedeceğim.
Bütün fieldlar tanımlanırken propertyler gibi isim, isim tamlaması veya sıfat olarak tanımlanmalıdır. Bahsettiğim gibi, private veya internal olan bir field’ı tanımlarken bazı ön ekler verebilirsiniz veya this keyword’unu kullanabilirsiniz. Private veya internal olarak tanımlanmış fieldlar, her zaman camelCasing kullanılarak tanımlanmalıdır. Public veya protected olarak tanımlanan hiç bir field’e ön ek vermemelisiniz ve bunları tanımlanırken PascalCasing kullanmalısınız.
Sonuç olarak, programlama bileşenlerinize verdiğiniz adlar sayesinde daha anlaşılır ve okunabilir bir yapı kurabilirsiniz. İyi, idare eder bir mimarı/yapı kurmanız zaman alır, hiç yoktansa benim için öyle oluyor. Bu süreçte, edinmeye çalıştığınız becerilerin temellerine inerek, aynı veya benzer konulara, farklı çözümlerle yaklaşan insanı veya insan gruplarını aynı anda kaynak edinip. Bu birbirinden farklı düşünen kişilere nötr kalarak, daha iyi sonuçlara ulaşabiliriz.