Java Kodunuzun Nesne-Merkezli Olmadığının 10 İşareti – II: Aşırı Statik Metot Kullanımı
Geliştirdiğimiz Java kodunun nesne-merkezli olmadığının 10 işaretinin neler olduğunu bu yazıda listelemiştik. Şimdi de bu listedeki ilk maddeyi ele alalım:
Sınıflarda aşırı miktarda statik metot kullanımı. (Excessive use of static methods in classes.)
Kodunuzdaki statik metotların sayısı, kodunuzun ne kadar nesne-merkezli olduğunun göstergesidir. Hiç statik metota sahip olmayan bir yazılım düşünmek pek de mümkün değildir ama statik metotların kullanımının istisnai durumlardan çıkarak genel bir hal kazanması, yaklaşımınızın nesne-merkezli olmaktan çıktığının kanıtıdır.
Nesne-merkezli programlamada esas olan, nesne kullanmaktır; statik metotlar ise nesne oluşturmadan, sınıf üzerinden programlama yapmanın yoludur ve bu yüzden de nesne-merkezli programlamanın varlık sebebine karşı bir durum oluşturmaktadırlar. Dolayısıyla aslolan, nesne metotlarının (instance methods) kullanılması, statik metotların ise tamamen istisnai sebeplere bağlı olarak yazılmasıdır. Ne kadar çok statik metot kullanırsanız nesne-merkezli programlamadan o kadar çok uzaklaşıp prosedürel programlamaya o kadar yaklaşmış olursunuz.
Statik metot kullanıp nesne metotu kullanmamak, yazılımı geliştirirken nesneye ihtiyaç duymamak demektir. Bunun birkaç sebebi olabilir:
İlk sebep, nesne-merkezli bir yaklaşıma sahip olmayıp, bundan dolayı nesne üretmemektir. Bu durumda nesne-merkezli bir dil kullanılsa bile yaklaşım nesne-merkezli değildir, daha çok prosedüreldir.
Konu ister iş alanı nesneleri, isterse de algoritmik yapıdaki davranışları içeren servis nesneleri olsun, temelde sadece statik metotlar tercih etmek nesne oluşturmadan nesne-merkezli programlama yapmak anlamına gelmektedir ki bu durum eğer çok özel bir sebepten kaynaklanmıyorsa nesne kavramını ve nesne-merkezli programlamayı bilmemek demektir. Programlamada yeni olan ya da tamamen prosedürel dünyaya alışkın programcıların Java (ya da nesne-merkezli herhangi bir başka dil kullanarak) kodu yazmaya başladıklarında sıklıkla karşılaştıkları bir durumdur. Problemli olan şey, statik kullanımının bulaşıcı ve yayılma eğiliminde olduğu gerçeğidir. Bir sınıf oluşturup üzerine statik metotlar ve alanlar koymaya başladığınızda kısa sürede kendinizi sadece statik metot yazarken bulacaksınız.
İkinci sebebi ki bu çok daha meşru bir hali ifade eder, çünkü bu durumda metot hakikatten de nesnenin kendisine özel olan durumunu değil, nesnelerin ortak değerlerde barınan durumuna bağımlıdır. Bu durumda nesnelerin ortak durumu, statik değişkenlerle ifade edilir ve bu ortak duruma erişen metotların da statik olmaları normaldir. Bu anlamda statik metot kullanımı, olmadığında sıkıntılı ve verimsiz olacak halin sağlıklı bir şekilde çözülmesinde ibarettir. Örneğin Singleton tasarım şablonunda bir tane yaratılmış olan nesnenin “static” olması ve o nesneye ancak yine “static” olan bir metottan ulaşılması bir zorunluluktur:
public class Singleton { private static Singleton singleton = new Singleton(); private static int count; private String name; private Singleton() { count++; name = "Singleton" + count; } public static Singleton getInstance() { return singleton; } public void printName() { System.out.println(name); } }
Nesnesiz programlama yapmanın uçüncü sebebi ise nesne oluşturmanın anlamsız olduğu durumlardır. Bu gibi hallerde durum da vardır ve bu durum üzerinde çalışan metotlar da vardır ama durum ve metotların üzerinde tanımlandığı yapının nesne olması konusunda çok da açıklık yoktur. Böyle hallerde sınıfın kendisinin nesne olarak davranmasını bekleriz. Örneğin “Tanrı” diye bir sınıfımız olsa bu sınıftan kaç tane nesne üretmeyi düşünürdünüz? Eğer çok tanrılı bir dine inanmıyorsak, olsa olsa bir tane Tanrı nesnesi üretmek yeterli olurdu. Bu durumda hiç nesne oluşturmadan Tanrı sınıfını nesne gibi kullanmak dolayısıyla da bu sınıfın üzerindeki her türlü durumu ve davranışı statik yapmak son derece anlamlı olur.
Dolayısıyla bazen ya kavram o kadar soyuttur ki gerçekte bir nesne ile ifade etmezsiniz ve sınıfı nesne olarak kullanırsınız ya da kavramınızın verdiği hizmetler hiç bir şekilde o sınıftan üretilecek nesnelerin farklı olacak durunmlarına bağlı değildir. Java APIsindeki java.lang.Math sınıfı buna çok güzel bir örnektir. Math sınıfının nesnesini oluşturmanın anlamı yoktur, çünkü onun sınıfını yukarıdaki Tanrı sınıfında olduğu gibi olsa olsa ancak bir tane nesneye sahip olabilir (eğer tek bir evrenimiz varsa ve evrenin her yerinde aynı Matematik geçerliyse 🙂 )bu yüzden sınıfını nesne olarak kullanabiliriz. Ayrıca Math sınıfının metotlarının davranışı bu sınıftan oluşturulacak nesnelerin durumuna bağlı değildir. Dolayısıyla Math sınıfından nesne oluşturmadan üzerindeki statik metotlar sayesinde ondan hizmet alabiliriz.
Math sınıfına benzer tabiatta olan, yani nesne modelinin bir parçası olmayan, araçsal (utility) metotlar da genelde statik olurlar çünkü ya nesnelerin durumlarına göre değişen davranışa sahip değildirler, tüm durum bilgisi zaten statik olan sınıfın durum bilgisinden ibarettir ya da gerekli bilgiyi zaten çağrılırlarken dışarıdan argüman olarak alırlar. Bu gibi metotların bulunduğu sınıflardan nesne oluşturmak, çoğunlukla iş alanını modelemeye bir katkıda bulunmadığı gibi, kod kalabalığı yapıp, belleği de şişirirler. Örneğin loglama vb. işleri yapan bu tip metotlar da genel olarak statik metotlar olarak ifade edilirler. Dolayısıyla statik metotların bu gibi durumlarda özenle ama doğru bir şekilde kullanılması şarttır.
Bazen, gereksiz nesne oluşturmama gibi masum bir amacın, bol statik metot kullanımına yol açtığı görülmektedir. Gereksiz nesne oluşturma, belleğin gereksiz kullanımına dolayısıyla da, bellek kaçağı (memory leak), performans ve ölçeklenirlik (scalability) problemlerine yol açar. Ama bu durumun tedavisi “hiç nesne oluşturmamak” değildir. Yani “nesneler belleği şişiriyor dolayısıyla nesne oluşturmayıp statik metotlarla çalışıyoruz” demek daha büyük sorunlara yol açacaktır. Eğer hakikatten bellek kullanımı bu kadar önemli ve kritik ise, bunun mimari bir ihtiyaç olarak ortaya konması ve bu seviyede çözümünün aranması daha doğru bir yaklaşım olur. Böyle bir çalışma, nesne merkezli bir dil kullanmak yerine bellek konusunda daha cimri olan prosedürel bir dil kullanmak gibi bir karar ile de sonlanabilir. Ya da Java kullanarak ve bellek konusunda daha tedbirli davranarak da bu sorunu çözebilirsiniz. Nesne oluşturmaktan kaçınmanın ana sebebi nesnenin “gereksiz” değil olsa olsa “anlamsız” olmasıdır. Yukarıda bahsedilen Tanrı ya da java.lang.Math sınıfı gibi, modellenen işin bir parçası olmayan, dolayısıyla işle ilgili nesneden nesneye değişen bir duruma sahip olmayan, araçsal sınıflar ve genelde onların statik olan metotları olmasaydı (ve dolayısıyla da biz bu sınıfların metotlarını ancak nesneleri üzerinden kullanabilir olsaydık,) bellek probleminden çok daha önce kod yapımızla ilgili problemler yaşardık. Çünkü bu gibi sınıfların nesnelerini yaratmak çoğu zaman çok fazla bilgi gerektirir ve bu durumu aşmak için nesne üretici (factory) yapılar gibi başka tasarım desenlerine ihtiyaç duyardık.
Uzun lafın kısası, statik metot kullanımını şu durumlarla sınırlı kılmak, bu durumlar dışında daima nesne oluşturup, nesne metotlarını kullanmak gereklidir:
- Nesne yaratmanın anlamsız olduğu durumlar. Bu durumlarda sınıfı, nesne olarak kullanabilirsiniz. Nesne yaratılsa bile sadece bir nesneye ihtiyaç varsa ki bu durum Singleton tasarım şablonuna karşılık gelir, sınıfın kendisini o tek nesne yerine, sanki nesneymiş gibi kullanılabilir. Böyle durumlarda hala nesne kullanımını tercih etmek burada açıkladığımız sebeplerden dolayı daha uygun olabilir.
- Metodunuz, üzerinde çağrılacağı nesnenin hiç bir nesne değişkenini (instance variable) kullanmıyorsa, yani nesnenin durumuna ulaşmıyorsa, statik metot kullanabilirsiniz. Çünkü statik metotlar statik olmayan değişkenlere ulaşamazlar.
- Metodun çağrılması için bir nesneye ihtiyacınız yoksa statik metot kullanabilirsiniz. Özellikle aynı nesnenin pek çok farklı bağlama geçilip, hepsinde kullanılması gibi durumlarda, farklı bağlama nesne geçmek yerine orada doğrudan sınıf üzerindeki statik metotları çağırmak son derece kolaylıktır. Özellikle araçsal davranışlara sahip (utility) sınıfları bu şekilde yazılımın her bölgesinde kullanmak anlamlıdır.
- Birden fazla nesne metodu tarafından ortak kullanılacak, nesnenin değişkenlerine ulaşmayan ve muhtemelen dışarıya da açılmayacak kodlar statik metotlarda tutulabilir.
Statik kullanırken iki kere düşünün 🙂
Toplam görüntülenme sayısı: 2033
Ahmet
11 Mart 2015 @ 13:37
Static metotların kullanım amacını çok güzel açıklamışsınız emeğinize sağlık. Bir Java developer değilim fakat kullandığım dilde static metotlarla ilgili yaptığım bi kaç hatayı yazınız ile fark ettim. Static metotların alışkanlık yaptığı aşikar.
Akin
11 Mart 2015 @ 17:20
Sevindim Ahmet bey. Tesekkur ederim.