Category: c#
kod kalitesi üzerine
September 20th, 2008Bir yazılımın kalitesini ölçerken hafıza kullanımı, performans, stabil kalma, hata adedi, yazılımcı "din"i inançlarına uygunluk vs gibi ölçekler kullanırsınız peki karşınızda duran kod dosyası için kaliteyi nasıl ölçersiniz?
Bakalım;
- Satır adedi?
- Hız?
- Okunabilirlik?
- Notlar (Comments)?
- Hata kontrolleri ?
Siz bu listeye istediğiniz kadar aklınıza gelenleri ekleyebilirsiniz ve hepsi pasta'nın bir dilim'ini paylaşır. Bazılarından ödün verip diğerini yükseltebilirsiniz ancak hangi dilimlerin daha çok yer kaplayacağı asıl sorundur.
Satır Adedi
Bazı yazılımcılar (-ki buna kendimi tutamayıp bende dahil oluyorum) ürettikleri yazılımın satır adedi ile övünürler. Aslında satır adedinin yüksek olması her zaman (ama her zaman) kötüdür. Satır adedinin artması yazılımın karmaşıklaşması, bakımının zorlaşması, hatalı/kötü/bilmeden bir "reuse" yada varsa object oriented kullanımının işaretidir.
Aynı işi yapan 2 yazılım için satır adedi daha düşük olan'ın yönetimi her zaman için daha kolaydır. Herhangi bir hata olması durumunda neden'i bulmanız her zaman daha kısa sürecektir. Bu yüzden mümkün olduğu kadar satır adedini az tutmak gerekli.
Satır adedi daha çok projenin boyutu ile ilgili bir ölçektir. Fazlası her zaman kötüdür.
Gerekmediği sürece yeni bir satır eklememek ilerideki saçlarınızın yerinde ve koyu renkte kalmasına yardımcı olur.
Hız
Hızlı çalışan bir yazılım her zaman için tercih edilir ancak bu konuda 2 kez düşünmek gerekli.
Okunabilirliği yüksek ama yavaş çalışan bir kodu, bakınca kimsenin hiç bir şey anlamadığı ama hızlı çalışan bir kod'a çoğu zaman tercih etmeniz gerekir.
Neden?
Eğer tek başına bir yazılım geliştiriyorsanız bu çoğu zaman sizin için bir sorun yaratmaz ama bir ekip ile çalışıyorsanız ekip'deki herkesin sizin becerilerinize sahip olması gibi bir imkanınız olmayacaktır.
Yazılımın içinde bu hızlı çalışan ama dokunulamayan kısımları bıraktığınız her parça dönüp dolaşıp size gelecektir.
Yıllar önce yazdığım (-ki şu an ne iş yaptığını bilsem de nasıl yaptığı konusunda en ufak bir fikrim olmayan) bir kod örneği vereyim;
Code:
void putpixel16(int x,int y,unsigned char * segment) | |
{ | |
register unsigned char * vidmem; | |
register unsigned short int packed; | |
| |
vidmem = (unsigned char * ) (segment + ( y * SCREEN_WIDTH + x ) * SCREEN_BYTEPERPIXEL); | |
| |
_asm { mov eax,vidmem } | |
_asm { mov edi,eax } | |
_asm { mov al,ColR } | |
_asm { and al,0f8h } | |
_asm { mov bl,ColG } | |
_asm { and bl,11100000b } | |
_asm { shr bl,5 } | |
_asm { or al,bl } | |
_asm { stosb } | |
_asm { mov bl,ColG } | |
_asm { and bl,1ch } | |
_asm { shl bl,3 } | |
_asm { mov al,ColB } | |
_asm { shr al,3 } | |
_asm { or al,bl } | |
_asm { stosb } | |
} |
ve aynı kodun daha anlaşılabilir hali;
Code:
void putpixel16(dword x,dword y,unsigned char * segment) | |
{ | |
register unsigned short int *vidmem; | |
register unsigned short int packed; | |
| |
vidmem = (unsigned short int * ) (segment + ( y * SCREEN_WIDTH + x ) * SCREEN_BYTEPERPIXEL ); | |
| |
packed = (ColR << 11) | (ColG << 6) | ColB; | |
| |
* vidmem = packed; | |
} |
Bu örnekle putpixel'i (ölümcül doğası itibariyle) c ile yazın demiyorum, sadece örnektir
Eğer ölümcül değil ise her zaman okunabilir olanı tercih etmek, sabahları işe gittiğinizde yüzlerce sorun yerine internetten haber sitelerini gezmenize yardımcı olacaktır.
Okunabilirlik
Bir kod parçasınının okunabilirliği üzerine yüzlerce şey anlatabilirsiniz ancak en iyisi örnek;
Code:
for (int i = 1; i < num; ++i) { meetsCriteria[i] = true; } | |
for (int i = 2; i < num / 2; ++i) { | |
int j = i + i; | |
while (j <= num) { meetsCriteria[j] = false; j += i; } | |
} | |
for (int i = 0; i < num; ++i) { | |
if (meetsCriteria[i]) { Console.WriteLine(i + " meets criteria"); } | |
} |
Bu örnekde tek bakışta işlemlerin nasıl ilerlerdiğini anlamak zor. İşleyişi çözmek için kod üzerinden fazladan 3-5 sn daha geçirmeniz gerekli, bunun yerine aşağıdaki gibi bir düzen daha iyi olacaktır. Okurken gözlerin kamaştığı bir düzen yerine;
Code:
for (int i = 1; i < num; ++i) | |
{ | |
meetsCriteria[i] = true; | |
} | |
| |
for (int i = 2; i < num / 2; ++i) | |
{ | |
int j = i + i; | |
| |
while (j <= num) | |
{ | |
meetsCriteria[j] = false; | |
| |
j += i; | |
} | |
} | |
| |
for (int i = 0; i < num; ++i) | |
{ | |
if (meetsCriteria[i]) | |
{ | |
Console.WriteLine(i + " meets criteria"); | |
} | |
} |
tercih etmeniz her zaman için başkaları ve bir zaman sonra sizin için sağlıklı olandır.
Buna göre her "statement" olarak geçecek bloğu "{" , "}" arasına alabilirsiniz.
Mantıksal olarak ayrı iş yapılan yerlerde boş bir satır bırakabilirsiniz.
Akışı göstermek için indent verebilirsiniz. Ident'ler için her editör/programcı farklı boşluk seviyesi (2,4,8..) kullandığı için editörü Tab karakteri yerine boşluk koymak üzere ayarlarsanız başka birisi baktığında da aynı düzeni görür.
Notlar
Kod içinde yazılan notlar için kötü örneklerde bazen hiç not olmaz yada yazan kişi her satırın ne olduğuna dair kendince açıklamalar yapar. Bu konuda iyileştirme yapmak istiyorsanız her zaman için yazdığınız kodun ne yaptığı yerinde neden bu şekilde olduğuna dair notlar düşün.
Bu mantıkla baktığınızda her satırda açıklama yapmak yada not yazma zahmetini en aza indirmiş olursunuz.
Hata kontrolleri
Bu konu yeni öğrenen yazılımcılar için belkide en çok hata yapılan konu. Bu yüzden biraz daha detay vermeye çalışıyorum. .Net için konuşuyorsak "hata" olarak konuşulan başlık "exception" olarak geçer. .Net dünyasında kontrolü yapılmamış her exception için Windows'un mavi ekranları olarak algılanabilir ve Windows tabanlı uygulamalar için;

yada ASP.Net için

İyi yazılmış bir .Net uygulaması için asla ama asla bu tip ekranları görmemeniz gereklidir. Bunun anlamı hata kontrolleri yeterince yapılan bir uygulamada exception oluşması gereklidir. Exception'ın Türkçe anlamıda zaten "kural dışı durum" olarak geçer.
.Net dünyasında exceptionlar tepeden baktığınız durumda 2 kategoriye ayrılır. Çalıştırma (Execution) hataları yada bug'dan kaynaklanan hatalar.
Örneğin NullReference, InvalidOperation, ArgumentNull gibi hatalar uygulama genellikle bir bug olduğunu gösterir. Bu tür hataları azaltmak için yapabileceğiniz en iyi çözüm parametre alan methodlarda gerekli kontrolleri yapmaktır. Örneğin;
Code:
void Save(Order order) | |
{ | |
Database.SaveObject(order.Customer); | |
Database.SaveObject(order.Order); | |
} |
Gibi bir method içerisinde parametre olarak gönderilen Order tipi objenin null olup olmadığı kontrolü yapılmıyor. Uygulama içerisindeki bir hata yüzünden null parametre gönderildiğinde size bu NullReference exception olarak gözükecektir. Bunun yerine;
Code:
void Save(Order order) | |
{ | |
if (order == null) | |
throw new ArgumentNullException("order"); | |
| |
Database.SaveObject(order.Customer); | |
Database.SaveObject(order.Order); | |
} |
şeklindeki bir kontrol olası hataları daha erken bulmanızı sağlar.
Execution hatalarına örnek olarakda FileNotFound, SerializationException gibi hatalar olabilir. Bu tür hatalarda genellikle yeterince kontrol yapılmadan işlem yapıldığında ortaya çıkar. Örneğin dosyanın varlığını kontrol etmeden dosyayı açmak gibi. Yapılması gereken dosyanın var olup olmadığını kontrol ederek kullanıcının anlayacağı bir mesaj vermek en doğrusudur.
Execution hatalarında elbetteki olası tüm kontrolleri yapmanız mümkün değil ancak sık olabilecek hataları önceden kontrol etmeniz yeterlidir. Bunun dışındaki hatalar için standart exception dialoglarını kendinize özgü hata ekranları ile değiştirmeniz her zaman için iyi bir fikirdir. Böylece handle etmediğiniz bir hata olduğunda uygulamanın size mail atabilmesi, hata log'larına kayıt gibi fantastik imkanlarınız olabilir.
Standart exception dialoglarını değiştirmek için AppDomain.CurrentDomain.UnhandledException event'ini kullanabilirsiniz. Handle edilmeyen herhangi bir exception oluştuğunda bu event tetiklenir. Örnek olarak buradaki adresde bazı bilgiler bulabilirsiniz.
Serialization nedir, nerede kullanılır ?
February 13th, 2008Biraz uzun bir yazı oldu ancak halen yeterli olduğunu düşünmüyorum ancak konuyu bilmeyenler için hızlı bir giriş ve önemli konuları anlatabilirim umarım.
Serialization tanımladığınız class'ları memory, disk, network gibi katmanlardan geçirmek için kullandığınız teknolojidir. .Net içinde Remoting, WebServices gibi geniş alanlarda siz farkında olmadan sessiz sedasız çalışırlar.
Tanımladığınız class serialize edilirken kullanacağınız formata göre farklı şekillerde formatterlar kullanılır. Format olarak .Net içerisinde Xml ve Binary olarak temel 2 format için formatter tanımlanmıştır.
Serialization'da temel olarak yöntem bir obje üzerinden diğer tüm obje ilişkileri çözülerek bir hedef kaynağa tüm özellikler yazılır. Karşı tarafta da aynı işlemin tersi yapılarak obje yeniden hayata geri döndürülür.
Serialization sırasında objeniz üzerindeki Method, Property gibi özellikler kaydedilmez, sadece ve sadece private olarak tanımlanmış class üzerindeki tüm yer kaplayan bilgiler (string, datetime, başka bir class) kaydedilir. Bu işlem system.reflection ile class üzerinde tüm değişkenler taranarak yapılır. Bu yüzden class'ı yeniden hayata geçirek olan tarafda o class'ın biliniyor olması gereklidir, yani client ve server tarafında aynı class'ın tanımlanmış olması (dll yada farklı yöntemlerle) gerekli.
Bu arada System.Reflection kullanıldığı ve genel bir sistem için tasarlandığından standart class yaratıp kullanmaya göre biraz daha yavaş çalışır ama korkulacak seviyede bir yavaşlıktan bahsetmiyorum.
Binary Formatter
Binary formatter çoğunlukla hız gereken yerlerde (Remoting) gibi kullanılılır. Mesela Client/Server bir uygulama yazmak istediğinizde classlarınızın çok hızlı şekilde client ve server arasında dolaşmasını istiyorsanız bu format sizin için uygun olacaktır.
Aynı şekilde bir class'ı diske kaydetmek istediğinizde .Net size itiraz etmez ancak pratikte bunun bazı yan etkileri olacaktır. Bu detaylara ilerleyen kısımda geliriz.
Xml Formatter
Bu formatda class xml formatına dönüştürülerek yapılır. Üretilen Xml formatı açık olduğu için herhangi bir başka uygulama yada platform tarafından okunabilir. Böylece web servislerinin tüm kapıları açılır.
Örnek olması için başka bir blog'dan aldığım örnek;
Code:
public class Person | |
{ | |
[XmlElement("FirstName")] | |
public string Name { get; set; } | |
public List<Pet> Pets { get; set; } | |
} | |
| |
public class Pet | |
{ | |
[XmlAttribute("Name")] | |
public string Name { get; set; } | |
[XmlAttribute("Breed")] | |
public string Type { get; set; } | |
} |
class'larını serialize etmek isteğinizde;
XML:
<PetClub xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> | |
<Members> | |
<Person> | |
<FirstName>Jon</FirstName> | |
<Pets> | |
<Pet Name="Chester" Breed="Savannah Cat" /> | |
<Pet Name="Abby" Breed="Domestic Miniature Panther" /> | |
</Pets> | |
</Person> | |
<Person> | |
<FirstName>Dan</FirstName> | |
<Pets> | |
<Pet Name="Lucy" Breed="Semi-sweet Chocolate Lab" /> | |
</Pets> | |
</Person> | |
</Members> | |
</PetClub> |
bir xml dosyası oluşacaktır. Bunu yapmak için;
Code:
public static string ConvertToXml(object item) | |
{ | |
XmlSerializer xmlser = new XmlSerializer(item.GetType()); | |
using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) | |
{ | |
xmlser.Serialize(ms, item); | |
UTF8Encoding textconverter = new UTF8Encoding(); | |
return textconverter.GetString(ms.ToArray()); | |
} | |
} |
metodunu kullanabilirsiniz.
Dikkat
Serialization dikkatli kullandığınız sürece her zaman için işinize yarayacak bir araçtır. Ancak yanlış kullanımlarda başınıza ciddi sorunlar açabilir.
Diske kayıt işlemleri için ASLA binary formatter kullanmayın. Aksi halde class'larınız üzerinde değişiklik yaptığınızda yada versiyon'u numarasını değiştirdiğinizde eski verileri okuyamazsınız. (Bunu atlatmanın yolları var ancak pratikte pek işinize yaramaz)
Client/Server konuşmaları için ASLA xml formatter kullanmayın. Aksi halde ciddi performans sorunları yaşayabilirsiniz. Web servisleri içinde xml formatter kullanılsa da bu servisler zaten yavaş çalışacağı bilinerek tasarlandığından sorun olmaz ancak client server tipi uygulamalarda kabul edilen sınırı aşan bir yavaşlık yaşarsınız.
Yeterli zamanınız varsa xml için kendi mekanizmanızı oluşturun. Bu xml verisini okumak ve class'ları başlatmak için çok daha hızlıdır.
Code:
public interface IXmlSerializable | |
{ | |
void XmlParse(XmlNode node); | |
void XmlCompile(XmlWriter writer); | |
} |
şeklinde bir interface tanımlayarak kendi serialize metodlarınızı tanımlayabilirsiniz. Bu sayede reflection olmadan hızlı yollarla ve versiyon değişikliklerinden etkilenmeden yola devam edebilirsiniz.
Equals ve ==
July 3rd, 2007![]()
Kullandığınız objenin tipi ne olursa olsun .NET'de iki objenin eşit olup olmadığını karşılaştırmanın iki yolu vardır.
if ( a == b )
yada
if ( a.Equals(b) )
Görünüşte aynı işi yapıyor gibi olsalarda gerçekte oldukça farklıdırlar.
VS2005 Araçları
April 14th, 2007
Daima plugin delisi birisi olduğum için, her VS2005 (Visual Studio 2005) kurduğumda mutlaka yanında kurduğum araçlar vardır, IDE'nin kendisi güzel olsada bu tool'lar olmadan kalkansız savaşa girmiş hissederim hep.
VS2005 Team Edition
* Professional edition'dan farklı olarak içerisinde Code Analysis (yani FxCop) ve Profiling bulunur. Sadece Code Analysis için herşeye değer.
TortoiseSVN
* SVN kullanıyorsanız kullanabileceğiniz tek ve en güzel svn client'dır. Ücretsizdir.
VisualSVN
* IDE içerisinden TortoiseSVN komutlarını kullanmak için geliştirilmiş ama benim kullanım amacım sadece dosyaları rename ettiğimde svn'i haber vermesi. Ücretlidir.
NCover
* Coverage için kullandığım şahane. Ücretsizdir.
testdriven.net
* IDE içerisinden unit test rutinlerini çalıştırmak için kullanırım. NCover ile arkadaş olduğu için daha bir güzel. Ücretsizdir.
Bu araçları yanınıza aldıktan sonra "life is beatiful" şarkısını söylemeye başlayabilirsiniz.
HashTable nedir ?
September 23rd, 2006Klasik programlamanın temel taşlarından biri array tipidir. Basitçe;
int[] pi = new int[] { 3, 1, 4, 1, 5, 9 , 2, 6, 5, 4 };
şeklinde tanımlanan bir arrayde elemanlara index (elemanın numarası) değerleri ile ulaşılır. Yani;
pi[0] değeri 3'ü, p[5] değeri de 9'u taşır. Bizde gayette hızlı olan yolla index vererek array içindeki değerleri kullanabiliriz. Bunun bilgisayar mühendisliğindeki geyiği erişim O(1) dır.