Java Kodunuzun Nesne-Merkezli Olmadığının 10 İşareti – IV: Hiç Alan Nesnesinin Olmaması Ya da Olsa Bile Yetersiz ve İnce Olmaları
Geliştirdiğimiz Java kodunun nesne-merkezli olmadığının 10 işaretinin neler olduğunu bu yazıda listelemiş ve bu yazıda ve bu yazıda da ilk iki maddeyi ele almıştık. Şimdi de üçüncü maddeyi ele alalım:
Kodunuzda hiç alan nesnesinin olmaması ya da varsa bile yetersiz ve ince olmaları. (No or very thin domain objects in the code.)
Eğer kodunuzda iş alanınızı modelleyen Müşteri, Hesap, Ürün, Poliçe, Kredi, Olay, Belge, Araç, Başvuru Dosyası ve benzeri nesneleriniz yoksa, bir iş modeliniz de yok demektir. Daha doğrusu, gerçekte var olan işinizin nesnelerle ifade edilmiş bir modeli yok demektir. Nesne-merkezli programlamanın en temel üstünlüğü ya da nesnelerin en fazla görünür olduğu yer aslında dilimizde işin nesne modeli (domain model ya da business domain model) olarak da adlandırdığımız, işin kendi kavramlarının ve aralarındaki ilişkilerin nesne merkezli olarak ifade edildiği durumdur. Böyle bir model, en temel iş terimlerini, özelliklerini ve sorumluluklarını ifade ettiği için son derece değerlidir. Bu modelin öncelikle bir sözlük olarak ifade etmek analiz sürecinde olmazsa olmaz çalışmalardandır. Sonrasında bu sözlüğü, use caselerden yola çıkarak, dilden bağımsız olarak UML class diyagramları ve/veya XML scheması olarak ifade etmek, uygulama geliştiren bir kurumun en temel bilgi teknolojisi değerlerinden olacaktır. Böyle bir çalışma ile kurum, teknik dilindeki tüm kavramları, ozellikleri ve davranışları boyutunda yakalamış olacaktır. Hatta eğilim, farklı ülkelerde hatta tüm dünyada, bazı kuruluşlar öncülüğünde, iş kollarının jargonlarının bu şekilde, platform ya da dilden bağımsız olarak yakalanıp ifade edilmesidir. Bunun da amacı, hızlı ve rahat entegrasyondur tabi olarak. Kurum böyle bir çalışmayla böyle bir modele sahip olduğunda, örneğin bu kurumun yazılımları gerek veri tabanında gerek ise uygulama katmanında, her zaman aynı iş terimlerini aynı şekilde ifade edecektir.
İşin nesne modeli kurgulanmadığında uygulamalar, işin nesne modeli ile ilgili en iyi ihtimalle, aynı nesneleri farklı şekilde ifade ederek ilerleyecektir. Bu durumda da örneğin Müşteri nesnesi, her uygulamada farklı yapılarda olacak ve uygulamalar arası bir entegrasyon söz konusu olduğunda ciddi miktarda “dönüştürme” yükü ortaya çıkacaktır. Özellikle başarılı SOA projelerinin önündeki en temel engellerden birisi, iş kavramlarını farklı şekillerde ifade eden veri tabanı ve uygulamaların birbirleriyle konuşabilmeleri için gerekli “dönüştürme” ya da “yeknesaklaştırma” çalışmalarının, özellikle büyük kurumlar için devasa boyutlara ulaşmasıdır.
Bir kurumda üç aşağı – beş yukarı aynı anlama gelen iş kavramlarının farklı uygulmalarda içerik olarak farklı şekillerde ifade edilmesi, yani bir uygulamanın Müşteri ya da Poliçe dediği kavramı, başka bir uygulamanın çok farklı nesnelerle ve yapılarla ifade etmesi, kurumsal mimari (enterprise architecture) açısından sıkıntılı bir durumdur. Ama hiç olmazsa bu organizasyonel bir problemdir. Bu durumun daha kötüsü şu iki halden birisi olur muhtemelen: İlkinde nesne merkezli bir iş modeli vardır ama bu modeldeki nesneler, sadece veri taşıyıp, diğer nesnelerle get/set metotlarıyla haberleşen dolayısıyla bir önceki maddede açıkladığım gibi, yüksek bir içerik bağımlılığına sahip olan nesnelerdir. Bu durumla sıklıkla karşılaşıyorum ve sebep olarak da nesne merkezli bir iş ve ihtiyaç analizi yapılmaması yanında, nesne kavramını yeterince içselleştirememiş, nesne-merkezli programlama konusunda tecrübe sahibi olmayan programcıları gorüyorum. Bu tür programcılar için nesne, yine bu serideki bir önceki yazımda açıkladığım gibi, bir kaç private alanı bir araya getirip, set/get metotlarını hızlıca oluşturmaya, nesne diyen, ama aslında nesne yerine veriyi modelleyen bir anlayışa sahip programcı arkadaşlarımızdır.
Bu türden nesnelere Martin Fowler “anemic domain model” ya da “kansız iş modeli” diyor. Yani bir model var ama model, iş alanındaki verileri temsil ederken malesef o modelin bir parçası olan davranışları, sorumlulukları temsil etmiyor. Yani model nesne ile kurgulanmış ama ifade ettiği şey, veri, davranışlar değil. Halbuki nesneler veri taşıyandan çok bir davranışa ya da sorumluluğa sahip olan elemanlardır. Verinin davranışlarıyla birlikte bir yerde ifade edilmesi, nesnenin ya da daha temelde sarmalama/encapsulation kavramın tanımıdır. Aksi taktirde tam da bir önceki maddede örneğini verdiğim Date nesneleriniz olur ve tarih ile ilgili davranışlar bir ya da daha çok kereler, “copy-paste reuse” tekniğini iyi bilen ya da birbirinden tamamen habersiz, her şeyi sıfırdan kendisi yapmaya çalışan yazılımcılar tarafından çoğaltılır. Bu da karman-çorman olmuş, anlaşılması zor, değişmesi daha zor olan yazılımlar demektir.
Gerek eğitimlerde gerek ise danışmanlık verdiğim yerlerde çok sıklıkla karşılaşıyorum bu türden durumlara. Account nesnesi var ama withdraw(), deposit() ya da calculateInterest() gibi metotlar Account nesnesi üzerinde değil. Bu gibi metotlar service ya da controller tipinde nesnelerde birikmiş durumda. Bu durumlarda son derece yetersiz, ince ya da zayıf iş nesneleri olurken, devasa boyutlarda, onlarca metota sahip servis nesnelerimiz oluyor. Daha da kötüsü, bu durumda bu türden iş nesneleri ile servis nesneleri arasında içerik bağımlılığı oluşuyor ki karmaşıklık çok ciddi bir engel haline geliyor. Bir nesneye değişiklik yapmak servis nesneleri ve onlar üzerinden diğer nesnelere kadar değişiklik yapmaya sebep oluyor. (Yeri değil ama bu ayırımı, yani veriyi nesneler üzerinde, davranışı ise servisler üzerinde tutmayı, katmanlı mimarinin gereği olarak uyguladığını söyleyenlere kafalarındaki katmanlı mimari ve nesne kavramını gözden geçirmelerini tavsiye ederim. Bu konuda benim şu ve devamı olan şu yazılarıma bakabilirsiniz.)
/** * This class represents a banck account anemically. It carries data but no responsibility regarding that data. * @author akin * */ public class Account { private String iban; private double balance; public Account(String iban, double balance) { super(); this.iban = iban; this.balance = balance; } public String getIban() { return iban; } public void setIban(String iban) { this.iban = iban; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } @Override public String toString() { return "Account [iban=" + iban + ", balance=" + balance + "]"; } }
Yukarıdaki gibi bir kansız nesnemiz olduğunda bakın para çekme işlemini nasıl yapıyoruz:
/** * This class implements business logic on Account object. * @author akin * */ public class AccountManager { ... public void withdraw(Account account, double amount) throws InsufficentBalanceException{ double balance = account.getBalance(); if(balance >= amount) balance -= amount; else throw new InsufficientBalanceException(account); } ... }
Yukarıdaki AccountManager nesnesi ile Account arasında sadece bilgi akışı olduğuna ve bu durumun aslında en istenmeyen türden bağımlılık yarattığına dikkatinizi çekmek istiyorum. Biz tasarım sırasında temelde iş nesneleri arasındaki ilişkileri nasıl halledelim diye kafa patlatırken bir de böyle bir ilişki yumağı, sistemi berbat etmektedir.
İkinci durum ise, benim eskiden daha sık karşılaştığım, hiç bir nesne modelinin olmadığı, dolayısıyla sadece Java’daki ilkel tipler ile String tipindeki alanların List ve benzeri torbalarla taşındığı yaklaşımdır.
Bu durumda uygulama, ilgili iş kavramının, çoğunlukla veri tabanındaki anahtar alana karşılık gelen bilgisine sahiptir ve diğer bilgileri, bu anahtar bilgi sayesinde ya veri tabanından sorguluyordur ya da uygulamanın diğer parçalarından alıyordur. Ama nihai olarak alınan ve verilen şey nesne değildir, gerçekte nesne olabilecek bilgilerin bölük börçük ifade edilmesidir. Bu durumda aslında nesnelerde ifade edilmesi gereken nesneler arası ilişkiler (associations) de, genel olarak sadece veri tabanında tablolar arasında ifade edilir. Yani kod, tek başına anlaşılabilir bir yapı olmaktan çıkar. Dolayısıyla örneğin aşağıdaki gibi metotlar çok sık görülür:
... public ArrayList getVehicle(int id, String [] properties, boolean b){ List vehicle = new ArrayLIst(); vehicle.add(id); vehilce.add(properties[0]); ... return vehicle; } ...
Bu metodun arayüzü, gerçekte Vehicle nesnesinin parçası olan farklı bilgileri, array vb. bölük börçük bilgiler olarak almakta ve yine bölük börçük pek çok bilgiyi bir ArrayList’e koyup geri döndürmektedir. Dolayısıyla iş alanının nesnelerle modellenmesi söz konusu değildir. Bu kodun bir programcı tarafından, ciddi miktarda bilgi desteği olmadan anlaşılması imkansızdır. Örneğin, “vehicle” ismindeki List’in hangi odasında ne var, bunların özellikleri nelerdir vb. bilgiler muhtemelen başka dokümanlarda tutulmalıdır ki bu kod programcılara anlam ifede etsin.
Her uygulama bir iş için yapılır ve ister çok teknik olsun ister olmasın ister çok karmaşık olsun isterse de sadece bir kaç süreç ve kural içerecek kadar basit olsun muhakkak bir iş süreci ve mantığı barındırır. Dolayısıyla bu süreçler ve ilgili mantığın, nesneler ve aralarındaki ilişkilerle ifade edilmesi gereklidir. Aksi taktirde hangi dili kullandığınızın ya da kaç katmanlı yapı kurguladığınızın çok fazla önemi olmayacaktır. Çok sıklıkla EJB, Spring, JSF gibi teknolojileri kullanacak öngörüde bulunan yazılımcıların bu konularda çuvalladığını görüyorum. Aslolan iş süreçlerinizi doğru düzgün ve değişıme el verecek şekilde modelleyip kodlamak. Teknoloji, hangi frameworku ya da hangi dili kullandığınız ancak bu sorunu çözdükten sonra problem olarak ele alınmalıdır.
Bol ve eğlenceli nesneli projeler diliyorum…
Toplam görüntülenme sayısı: 1558