3 Haziran 2008 Salı

Yazılım Tasarım Prensipleri



Yazılım mimarisi nedir?

Bu sorunun yanıtı ‘çok katmanlı’ olarak verilebilir. En üst seviyede, yazılım uygulamalarının genel yapısı ve şeklini tanımlayan mimari paternler vardır. Bir seviye altında yazılım uygulamalarının amacı ile uyumlu mimari yapı yer alır. Bir sonraki seviyede ise modüller ve aralarındaki bağlantıları tanımlayan yapı vardır. Bu seviye tasarım paternleri paketler elemanları ve sınıfların kullanıldığı seviye olup bu yazının konusunu oluşturmaktadır.

Mimari Yapı ve Bağımlılık HalleriYazılımda ters giden şey nedir? Birçok yazılım uygulamalarının tasarım, tasarımcının zihninde oluşan çok net görüntülerle başlar. Bu aşamada tasarım çok net, çekici ve iyi düşünülmüş durumdadır. Tasarım öylesine basit ve güzel bir hal almıştır ki, tasarımcı ve uygulayıcılar sistemin çalışmaya başlaması için sabırsızlanmaktadırlar. Bu uygulamaların bazıları başlangıçtaki bu basit ve güzel yapıları ilk sürüme kadar korurlar.

Fakat, daha sonra bir şeyler olur. Yazılımdan hafif kokular gelmeye başlar. Başlangıçta durum çok kötü değildir. Tasarımın sağına soluna içerden değişme yapılır. Baştaki güzellik hala kendini korur. Ancak, zamanla bozulma devam ederek tasarımın her yerine yayılır. Program geliştiricilerin üzerinde değşiklik yapmakta zorlandıkları bir hale gelmeye başlar. Ve en sonunda en ufak bir değişikliğin yapılması bile öyle zor hale gelir ki, yöneticiler projenin yeniden tasarlanması için sesini yükseltmeye başlarlar. Bu yeniden tasarım çok nadiren başarılı olur. Tasarımcılar tüm iyi niyetleri ile çalışmaya başlarlar ancak kendilerini hareket eden bir hedefe ateş eder durumda bulurlar. Eski sistem gelişmeye ve değişmeye devam ederken yeni tasarımın en kısa sürede bitirilmesi gerekmektedir. Yeni tasarım daha ilk sürümü tamamlanmadan bozulmaya başlamıştır. Genellikle planlanan çok daha sonraki bir tarihte uygulamaya geçerken yeni sürüm, daha ilk günden sorunlar yumağı halindedir. Tasarımcılar yeni bir tasarımı düşünmeye başlamışlardır bile!

Bozulan Tasarımın BelirtileriTasarımın bozulmaya başladığını gösteren dört temel belirti vardır. Bu belirtiler birbirleri ile bağlantılı olan katılık, kırılganlık, atıl duruma düşme ve yüzergezerliktir.

Katılık (Rigidity)
Katılık yazılımın en basit durumlarda bile üzerinde değişme yapılmasının çok zor olmasıdır. Her değişiklik bağımlı modüllerde zincirleme başka değişikliklere neden olur. Bir modül üzerinde çok basit, iki gün süreli diye başlayan mühendislerin uygulamanın her bir noktasında çalışmasını gerektiren ve haftalar boyu süren bir maratona dönüşür. Yazılım bu şekilde davrandığında, yöneticiler programcıların kritik olmayan değişmeler yapmasından kuşku duyarlar. Bu isteksizlik, programcıların güvenilir bir şekilde değişikliği ne zaman bitireceklerini bilmemelerinden kaynaklanır. Eğer yöneticiler, programcıları sıkı takip etmezler ise, programcılar uzun bir süre ortadan kaybolurlar. Yazılım tasarımı müşterilerin kayıt yaptırdıkları fakat hiç ayrılamadıkları otele döüşür. Yöneticilerin korkuları akut hale geldiğinde, yazılım üzerinde değişme yapılmasını hiç istemezler ve resmi katılık ortamı kendiliğinden oluşur. Böylece tasarım sorunu olarak başlayan süreç yönetim politikasına dönüşür.

Kırılganlık (Fragility)
Katıcılık ile ilgili yakından ilişkili olan diğer bir belirti kırılganlıktır. Kırılganlık yazılımın, bir noktasında bir değişme yapıldığında başka birçok yerinden bozulmasıdır. Genellikle bozulmalar değişme yapılan yer ile kavramsal olarak hiç ilgisi olmayan yerlerde oluşur. Bu hatalar yöneticilerin yüreklerini hoplatır. Değişmeyi istedikleri her durumda yazılımın hiç beklenmedik bir şekilde bozulmasından korku duyarlar. Kırılganlığın arttığı durumlarda bozulma olasılığı zamanla artar. Bu türlü yazılımların bakım tutumunun yapılması olanaksızdır. Her değişiklik durumu daha da kötüleştirir, değişiklik çözüldüğünden daha fazla yeni sorunlara yol açar. Bu gibi yazılımlar yöneticiler ve müşterilerin, geliştiricilerin yazılım üzerindeki kontrollerini kaybettiklerini düşünmelerine yol açar. Güvensizlik artar ve kredibiliteleri düşer.

Atıl Duruma Düşme (Immobility).
Atıl durum hali, yazılımın projelerden veya aynı projenin değişik kısımlarının yeniden kullanılamamasıdır. Bu durum, bir programcının başka bir programcının yazdığı modüle benzer bir modüle ihtiyaç duyduğunda ortaya çıkar. Ancak söz konusu modül genellikle birçok başka modüle bağımlı durumdadır. Uzun çalışmalar sonrasında programcı yazılımın bu kısmını istenmeyen diğer bölümlerden ayırmasının çok zor ve riskli olduğunun farkına varır. Bu nedenle de yeniden kullanım yerine modül yeni baştan yazılır.

Yüzergezerlik (Viscosity) Akışkanlık
Akışkanlık iki şekilde oluşur: Tasarımın akışkanlığı ve ortamın akışkanlığı. Bir değişme ile karşılaşıldığında, programcı genellikle çözüm için birden fazla olası yöntem ile karşı karşıya kalır. Bu yöntemlerden bazıları tasarımın genel yapısını korurken, bazıları korumaz. Tasarımın genel yapısını koruyan yolun uygulanmasının zor olduğu durumlardır. Bu yanlış olanı yapmanın kolay, doğru olanı yapmanın ise zor olduğu durumdur. Ortam akışkanlığı geliştirme ortamının yavaş ve verimsiz olduğu durumlarda ortaya çıkar. Örneğin, eğer derleme zamanı çok uzun ise, programcılar tasarım açısından uygun olmadığı halde, uzun derlemelere gerek duymayacak şekilde değişikliği yaparlar. Eğer kod kontrol sistemi birkaç dosyanın kayıt edilmesi için bile uzun zaman gerektiriyorsa programcılar bu işlemi en az gerektirecek şekilde programı değiştirmeye çalışırlar. Bu esnada tasarım gözetilmez. Bu dört belirti kötü mimari yapının ipuçlarını verir. Bu belirtilere sahip her uygulama her yönüyle bozulmaya açık bir tasarımın varlığını gösterir. Bu bozulmaların oluşmasının nedeni nedir?

Değişen İhtiyaçlarTasarımı kötüleştiren en önemli nedenlerin başında diğer ihtiyaçlar gelir. İhtiyaçlar başlangıç yapılan tasarımda hiç öngörülmediği bir şekilde değişir. Bu değişiklikler genellikle çok çabuk yapılmak zorundadır ve orjinal tasarımın genel yapısını bilmeyen programcılar tarafından yapılıyor olabilir. Tasarım yapılan değişme çalışmaktadır ancak orjinal tasrıma uygun bir değişme değildir. Yavaş yavaş değişiklikler yapıldıkça bu uygun olmayan gidiş etkisini artırarak akut hale gelir. Ancak, ihtiyaçların değişmesini tasarımın kötüleşmesinin bile nedeni olarak suçlayamayız. Yazılım mühendisleri olarak bizler, ihtiyaçların sürekli değişikliğini biliyoruz. Gerçekte, herkes biliyorki projenin en hassas kısmı ihtiyaçlar dökümanıdır. Eğer tasarımımız peşpeşe gelen değişmeler nedeniyle bozuluyorsa, hata tasarımdadır. Tasarımlarımızın bu gibi değişmelere karşı dayanıklı olması için yeni yöntemler geliştirmeliyiz.

Bağımlılığın Yönetimi ( Dependency Management)
Tasarımın bozulmasına ne tür değişmeler neden olur? Bağımsızlıklar için beklenmeyen, yeni değişikliklerdir bunlar. Yukarıda sözü edilen dört belirtinin her biri, direk veya dolaylı bir şekilde, yazılım modüllerinin uygun olmayan bağımlılığı nedeniyle oluşur. Bozulan şey, bağımlılık mimarisidir ve onunla birliktede yazılım bakımının yapılabilme yeteneğidir. Bağımlılık mimarisinin bozulmasını önlemenin yolu, uygulamadaki modüller arasında oluşan bağımlılığın kontrol edilmesinden geçer. Bu kontrol bağımlılığı önleyen ateş duvarları (firewal) ile kurulur. Bu ateş duvarlarının ötesine bağımlılık ilişkileri geçemez. Nesneye yönelik tasarım bu gibi ateş duvarlarını örmek için gerekli prensip ve teknikleri bize sağlar. Bu yazıda, uygulamanın bağımlılık mimarisinin kontrol edilmesini sağlayan prensipleri ardından teknikleri veya tasarım paternlerini inceleyeceğiz.



Nesneye Dayalı Sınıf Tasarımının Prensipleri :

Açık kapalı prensibi ( AKP) (Open closed Principle)
Bir modül genişlemeye açık fakat değişime kapalı olmalıdır. Nesneye dayalı tasarım prensiplerinin en önemli olanıdır. Orjinal tasarım Bertnan Meyer’in (005c2) çalışmalarından yapılmıştır. Bu tanıma göre: Modüllerimizi öyle hazırlamalıyızki, bu modüller üzerinde değişme yapılmasına gerek kalmadan genişletilebilsin. Diğer bir deyişle, modüllerin kaynak kodunu değiştirmeye gerek kalmaksızın modüllerin yaptıkları işi değiştirebilmeliyiz. Bu tanım çelişkili görülebilir, fakat AKP’yi başarmanın teknikleri vardır. Bu tekniklerin tamamı soyutlama (abstraction) üzerine kuruludur. Gerçekte soyutlama AKP’nin gerçekleşmesi için anahtar görevini yapar. Bu teknikler aşağıda olduğu gibidir.

Dinamik Çokşekillilik (Dynamic polymorphism)
Yazılıma her yeni modem eklendiğinde Giriş fonksiyonunun değişmesi gerekir. Daha kötüsü, her gerekli modem tip sayımına bağımlı olduğu için, hem yeni modem ilavesinde tüm modemlerin derlenmesi gerekir. Bu şekilde tasarlanan programcılar birçok irili ufaklı switch ifadelerinden oluşur. Modeme ilgili birşey yapılmak istendiğinde if/else ifadeler zinciri içinden uygun fonksiyon seçilir. Yeni bir modem eklendiğinde veya modem çalışma şekli değiştiğinde modemle ilgili seçilen işlemlerin yapıldığı kod parçaları taranarak modemle ilgili kısımlar değiştirilmelidir. AKP’ye örnek olarak Şekil 1’i düşünelim. Burada Giriş fonksiyonu sadece modem arabirime bağlıdır. İlave modemler Giriş fonksiyonunda değişikliğe neden olmaz. Böylece, yeni modemlerin ilavesi ile, herhangi bir değişmeye gerek kalmadan genişleyebilen bir modül oluşturulur.

AKP’nin Mimari Hedefleri.Yukarıdaki tekniği kullanarak, üzerinde değişme yapılmadan genişletilebilen modüller yaratabiliriz. Bu küçük bir öngörü ile, var olan kodu değiştirmeden, yeni kodlar ekleyerek programımıza yeni özellikler ekleyebileceğimiz anlamına gelir. Bu gerçekleştirmesi zor bir hedef gibi görünmesine karşın, imkansız değildir. AKP tam anlamıyla gerçekleştirimesede, kısmi olarak uygulandığında bile uygulamanın yapısında çok önemli gelişmeler sağlar. Çalışmakta olan program kodları etkilemeden yapılacak değişiklik ile her zaman tercih edilir. Eğer çalışan kodu değiştirmeniz gerekmiyorsa, kodu bozma olasılığınızda çok düşüktür.

Liskov Değiştirme Prensibi ( LDP)
(Liskov substitution Principle) Alt sınıflara ait oldukları temel sınıflar yerine kullanılabilmelidir. Bu prensip ilk olarak Barbara Liskov’un veri soyutlama ve tip teorisi üzerine yaptığı çalışmadan kaynaklanmıştır. ( LSP97). Yine Betrond Meyer’in Kontrat ile Tasarım (Design by Cuntrat) kavramıda bu prensibin temelini oluşturur. Yukarıda tanımlanan kavram Şekil-3’de gösterilmiştir. Türetilen sınıflara ait oldukları temel sınıflar ile yer değiştirebilmelidir. Yani, temel sınıfı kullanan istemci, eğer bu temel sınıftan türetilen bir sınıf kendisine gönderildiğinde çalışmaya devam edebilmelidir. Diğer bir deyişle, istemci isimli bir fonksiyon Temel tipinde bir argümanı kullanıyorsa, bu fonksiyona Türetilen sınıfın nesneleri de gönderilebilmelidir. Bu çok aşikar gibi görülebilir, fakat gözönüne alınması gereken önemli ayrıntılar vardır. Bunun en tipik örneğini meşhur daire/elips açmazı ile gösterilebilir.

Daire/Elips Açmazı. Lise matematik derslerinde hepimiz dairenin elipsin bir başka şekli olduğunu öğrendik. Tüm daireler merkezdeki aynı noktada olan elipstir. Bu bizim kavramsal modelimizi oluştururken bazı zorluklar içerir. Şekil-5’teki elips tanımına dikkatlice baktığımızda bu zorluğun ne olduğunu görebiliriz. Dikkat edilirse Elipsin üç veri elemanı vardır. İlk ikisi merkez, sonuncusu ise ana eksen’dir. Eğer Daire Elips’den türetiliyorsa, bu veri tiplerinide miras alması gerekir. Oysaki daire sadece merkez ve çap olmak üzere iki veri elemanına sahiptir. Eğer gereksiz yere bellekte ayrılacak yeri ihmal edersek, override eden Setmerkez rutininde her iki merkez değerinin aynı olmasını sağlayarak, Daire’nin uygun şekilde davranmasını sağlayabiliriz. Liste-4’e bakınız. Böylece her iki merkez dairenin merkezi gibi davranır ve ana eksen dairenin çapı olur.

İşleri karıştıran istemcidir. Yarattığımız model kendi içinde kenarlı bir yapıya sahiptir. Daire nesnesi daireye ait tüm kurallara uyar. Kuralları ihlal etmesine neden olacak işlem yapmanız mümkün değildir. Aynı durum Elips içinde geçerlidir. Daire fazladan bir veri elemanına sahip olduğu halde bu iki sınıf uyumlu bir model oluşturur. Ancak, Daire ve Elips evrende kendi başlarına yer almazlar. Evrende diğer başka elemanlar vardır ve umuma açık (public) arayüzlerini birbirlerine açarlar. Bu arayüzler aralarındaki kontratın varlığını gösterir. Bu kontrat açıkça ifade edilmesede arayüzün varlığı ile kendiliğinden tanımlanmış olur. Örneğin, Elips’in kullanıcıları aşağıdaki kod parçasının çalışmasını beklerler. Bu örnekte fonksiyon Elips için çalışır. Bu fonksiyonda, ve aralarındaki eksenin merkezlerinde doğru olarak ayarlanacağı beklenir. Bu fonksiyona bir Elips nesnesi gönderirsek her şey yolunda gider. Ancak, bu fonksiyona bir Daire nesnesi gönderirsek işler karışır. Elips’in kontratını açıkça ifade etmek gerekirse, SetFoci’nin (postcondition)’ı giriş değerlerinin, üye değişkenlerine kopyalandığını ve ana eksen değişkeninin değerinin değişmediğini garanti eder. Tahmin edileceği gibi Daire SetFoci’nin ikinci giriş değerini dikkate almadığı için bu garantiyi sağlamaz.

Kontrat ile Tasarım (Design by Contract).
LDP’yi tekrar tanımlarsak, değiştirilebilir olması için, temel sınıfın kontratı, türetilen sınıflarcada yerine getirilmesi gerektiğini söyleyebiliriz. Örneğimizde, Daire Elips’in kontratını yerine getirmediği için değiştirilebilir bir sınıf değildir, bu nedenle LDP’yi ihlal eder. Bir metodun kontratını belirlerken metod çağırılmadan önce nelerin sağlanması gerektiği tanımlanır. Bu ön koşul (precondition) olarak adlandırılır. Eğer ön koşul sağlanmaz ise metodun sonucu tanımsızdır ve metod çağrılmamalıdır. Benzer şekilde metod tanımlandıktan sonra sağlanması gerekli koşullarda tanımlanır. Bu son koşul (postcondition) olarak adlandırılır. Son koşulu sağlamayan metod geri dönmemelidir. LDP’yi kontratları kullanarak yeniden tanımlarsak, türetilen bir sınıf sadece aşağıdaki koşullar gerçekleşiyorsa ait olduğu temel sınıf yerine kullanılabilir.

1. Ön koşulları temel sınıf metodundan daha güçlü değildir.

2. Son koşulları temel sınıf metodundan daha zayıf değildir.

Veya diğer bir tanımla, türetilen sınıflar daha fazlasını beklememeli ve daha azını sağlamamalıdır.

LDP’nin İhlal Edilmesi.Ne yazıkki, LBP’nin ihlal edildiği durumlar iş işten geçtikten sonra farkedilir. Daire/Elips açmazında bir istemci kontratın çalışmadığını ortaya çıkarıncaya kadar herşey yolunda gözükmektedir. Tekrar başa dönüp tasarımı değiştirerek var olan tüm istemcileri yeniden kurup test etmek çok ekonomik bir çözüm olmaz. Bu nedenle çözüm, muhtemelen ihlali ortaya çıkaran istemci içine if/else koyarak sağlanır. İf/else ifadesi Elips’in gerçekten Elips olduğunu ve bir Daire olmadığını garanti eder. Dikkatli incelersek AKP’yi ihlal ettiğini görürüz. Artık Elips’den türetilen her yeni sınıf için bu fonksiyonun yeni sınıf için çalışıp çalışmadığı kontrol edilmelidir. Böylece LDP’nin ihlali AKP’nin ihlaline yol açmaktadır.

Bağımsızlığın Tersyüz Edilmesi Prensibi (Dependency-İnversion) Soyutlamalara bağımlı ol. Somut tanımlara bağımlı olma. Eğer AKP Nesneye dayalı mimarinin amacını ifade ediyorsa, BTP’de bunun yerine getirilmesi için gerekli temel mekanizmayı tanımlar. Bağımlılığın tersyüz edilmesi somut fonksiyon ve sınıflar yerine, soyut arayüz veya soyut fonksiyon ve sınıflara dayalı olma stratejisidir. Bu prensip COM, CORBA ve EJB gibi eleman tasarımlarının arkasındaki itici güçtür. Prosedürel tasarımlar belirli bir bağımlılık yapısı gösterirler. Şekil 2-17’de görüldüğü gibi, bu yapı tepeden başlar ve aşağıya doğru detaylandırılır. Üst seviye modüller daha alt seviyedeki modüllere bağımlıdır. Biraz dikkatlice bir gözlem bu bağımlılık yapısının güçsüz bir yapı olduğunu ortaya çıkarır. Üst seviyedeki modüller uygulamanın üst seviye politikalarını içerir. Bu politikalar genel olarak onları uygulayan detaylar ile pek ilgilenmez. O halde neden bu üst seviye modüller uygulama modüllerine bağımlı durumdadır? Nesneye dayalı mimaride yapı çok farklıdır. Burada bağımlılıkların büyük çoğunluğu soyutlamalara karşı yapılır. Ayrıca, detayları içeren modüller başka modüllerin kendilerine bağımlı olması yerine, kendileri soyutlamalara bağımlıdır. Böylece bunlara olan bağımlılık hali tersyüz edilmiş haldedir.

Soyutlamalara Bağımlı Olma. Bu prensibin uygulanması çok basittir. Tasarımdaki her bağımlılık bir ana birime veya soyut sınıfa yapılmalıdır. Hiçbir somut sınıfa bağımlı kalınmamalıdır. Bu sınırlama çok iddialıdır. Ancak mümkün olduğu her durumda bu prensip uygulanmalıdır. Bunun nedeni çok basittir. Somut şeyler çok değişime uğrar, oysa soyut şeyler daha az sıklıkta değişir. Ayrıca, soyutlamalar bağlantı noktalarını oluşturur. Soyutlamalar tasarımın kendilerinin değiştirilmeden (AKP) yön değiştireceği veya genişletilebileceği yerleri temsil ederler. Com, en azından elemanlar arasında bunun örneğini oluşturur. Com elemanlarının görünen tek kısmı soyut anabirimidir. Böylece, Com içinde BTP’yi ihlal olasılığı çok düşüktür.

Mitigating Forces. BTP’nin uygulama nedenlerinden biri, sizlerin kırılgan modüllere bağımlı kalınmasını önlemektir. BTP somut olan herşeyin kırılgan olduğunu varsayar. Bu varsayımın genellikle doğru olmasına karşın, geliştirmenin başlangıç aşamalarında istisnalar olabilir. Örneğin denenmiş ve doğru çalışan somut fakat kırılgan olmayan modülleriniz var ise bu modüllere bağımlı olmak o kadar kötü değildir. Bu modüllerin değiştirilme olasılığı az olduğu için sizin tasarımınıza kırılganlık kazandırma olasılığı düşüktür.

Nesne Yaratma. Tasarımın somut sınıflara bağımlılığı gerektiren kısımları genellikle nesnelerin yaratıldığı kısımlardır. Tanım gereği soyut sınıfların nesnesi yaratılamaz Bu nedenle nesne yaratmak için somut sınıfa bağımlı olmak gerekir. Nesnelerin yaratılması tasarımın her noktasında gerek duyulan bir durumdur. Bu durum tüm mimarinin somut sınıflara bağımlı durumlarla dolu olacağı düşünülebilir. Ancak, bu problemin (ABSTACTFACTORY) SOYUT FABRİKA [GOF] olarak adlandırılan çok güzel bir çözümü vardır. Bu tasarım paterni ileride ki bölümlerde inceleyeceğiz.

İnterface Segregation Prensibi (ABP)
Arayüz Birikimi Prensibiİstemciye özel birçok arabirim yerine birtek genel amaçlı anabirim kullanılmalıdır. ABP olmasa idi elemanlar ve sınıflar çok daha kullanışsız ve taşınması zor olurdu. Bu prensibin gerekliliği çok açıktır. Birden fazla istemcisi olan bir sınfınız var ise, sınıfı istemcilerin ihtiyaç duyduğu tüm metodlar ile yüklemek yerine, her bir istemci için özel bir arabirim yaratılır ve bu arabirimlerin hepsi bu sınıftan kalıtım olur. Birçok istemcisi ve bu istemcilere hizmet eden tek bir arabirimi olan bir sınıfı göstermektedir. Dikkat edilirse, İstemci A’nın çağırdığı metodlardan biri değiştirilirse, İstemci B ve İstemci C’de bundan etkilenebilir. Tüm istemcilerin yeniden derlenmesi gerekebilir. Daha iyi bir teknik Şekil 2-20’de gösterilmiştir. Her istemcinin gereksinim duyduğu metodlar kendisine ait arabirime yerleştirilir. Bu arayüzler servis sınıfından kalıtım alır ve servis sınıfı içinde uygulaması yapılır. Eğer istemci A’nın arabirimi değiştirilirse istemci B ve istemci C bu değişmeden etkilenmez.

“İstemciye Özel” Terimi Ne Anlama Gelir? ABP servis kullanan her sınıfın, servisin kalıtım alacağı kendi özel arabirimini yaratmasını her zaman önermez. Eğer öyle olsaydı, servis her istemciye tuhaf bir şekilde bağımlı olurdu. Bunun yerine, istemciler tiplerine göre sınıflandırılmalıdır ve her tip istemci için bir arabirim yaratılmalıdır. Eğer bir veya daha fazla istemci aynı metoda ihtiyaç duyarsa, bu metod her birinin arabirimine etkilenmektedir. Bunun istemci açısından bir zararı yoktur.

Değişen Arabirimler.Nesneye dayalı uygulamaların bakımı yapılırken, var olan sınıflar ve elemanların arabirimleri sık sık değişikliğe uğrar. Bu değişikliklerin büyük etkilerinin olduğu ve kapsamlı derleme ve dağıtım işlemlerine neden olduğu zamanlar olabilir. Bu etki var olan arabirimleri değiştirmek yerine, var olan nesnelere yeni arabirimler ekleyerek bu etki azaltılabilir. (mitigate)Yeni arabirimin metodlarına erişmek isteyen eski arabirimin istemcileri, aşağıdaki kod ile gösterildiği gibi bu arabirimin nesnelerini sorgulayabilir. Tüm diğer prensiplerde olduğu gibi aşırıya kaçılmamalıdır. Yüzlerce farklı arabirimi olan bir sınıf hiçte kullanışlı olmaz.

Nesneye Dayalı Mimarinin Paternleri Nesneye dayalı mimariler oluşturmak için, yukarıda tanımlanan prensipleri uygularken, aynı yapının tekrar tekrar uygulandığı görülür. Tasarım ve mimarinin bu tekrar eden yapıları tasarım paternleri olarak bilinir. [GOF96]Tasarım paterni, ortak bir problemin iyi tanımlanmış ve iyi çalışan çözümüdür. Tasarım paternleri kesinlikle yeni değildir. Tam tersine uzun yıllar boyunca kullanımı kanıtlanmış eski tekniklerdir.Bazı yaygın tasarım paternleri aşağıda tanımlanmıştır.Soyut Sunucuİstemci direk olarak sunucuya bağımlı olduğunda, BTP ihlal edilmiş olur. Sunucuda yapılan değişmeler istemciye taşınır ve istemci benzer başka sunucuları kolayca kullanamaz. Bu durum aşağıdaki şekilde olduğu gibi istemci ile sunucu arasına soyut bir arabirim ekleyerek düzeltilebilir. Soyut arabirim tasarımın esnetilebileceği eklem noktasını oluşturur. Sunucunun farklı uygulamaları istemciye kolayca bağlanabilir.

AdapterSunucunun üçüncü tarafa ait bir yazılım olması veya kolayca değiştirilemeyecek ölçüde sunucuya bağımlı olunması nedeniyle, soyut arabirim eklemenin mümkün olmaması durumunda, soyut arabirimi sunucuyla ilişkilendirmek için ADAPTER kullanılabilir. Adapter, sunucuya işlemi yöneltmek için soyut arabirimin uygulanmasını yapan bir nesnedir.

Gözlemci (Observer)
Bir elemanın tasarımın bir yerinde bir olay (event) oluştuğu belirlendiğinde, tasarımın bir başka yerindeki başka bir elemanın belirli bir işlem yapması gerekebilir. Ancak genellike olayı belirleyenin işlemi yapanın kim olduğunu bilmesi istenmez. Bir algılayıcının durumunu gösteren bir metreyi göz önüne alalım. Algılayıcının değerindeki her değişmede, metrenin yeni değeri göstermesini isteyelim. Ancak, algılayıcı metre hakkında her hangi bir bilgiye sahip olmasın. Bu durumu, şekil 2-32’deki gibi (OBSERVER) Gözlemci ile gerçekleştirebiliriz. Algılayıcı, Subject isimli sınıftan, Metre ise, Gözlemci adındaki arabirimden türetilir. Subject Gözlemci’lerin listesini içerir. Bu liste Subject’in Kayıt Et isimli metodu ile doldurulur. Olayın haber verilmesi için, Metre nesnesi Algılayıcının temel sınıfı olan Subject sınıfı ile kayıt olması gerekir. Köprü (Bridge)

Soyut sınıfın kalıtım ile uygulamasındaki problemlerden birisi türetilen sınıfın temel sınıfa çok sıkı bir şekilde bağlanmış olmasıdır. Bu durum istemcilerin temel sınıf hiyerarşisini almadan türetilen sınıfa ait fonksiyonları kullanmak istediklerinde oluşur. Örneğin müzik sentezleyici sınıfını düşünelim. Temel sınıf, MIDI girişlerini, türetilen sınıflar tarafından uygulanan Sesi Algıla metod çağrılarına dönüştürür. Türetilen sınıftan Sesi Algıla fonksiyonu kendi içinde kullanımdadır. Bu fonksiyon ne yazıkki MüzikSentezleyici sınıfı ve Midi Çal fonksiyonuna bağlı olarak çalışır. Temel sınıfı beraberinde götürmeksizin Sesi Çal metoduna erişmek mümkün değildir. Aynı zamanda, aynı Sesi Algıla fonksiyonunu kullanan Midi Çal fonksiyonunun farklı uygulamalarını yaratmak mümkün değildir. Kısaca hiyerarşi birbirine çok sıkı bağlı durumdadır. Köprü paterni arabirim ile uygulama arasında güçlü bir ayrım oluşturarak bu problemi çözer. Şekil 2-35 bu işin nasıl yapıldığını göstermektedir. Müzik Sentezleyici sınıfı Müzik Sentezleyici-1 tarafından uygulanması yapılan soyut Midi Çal fonkasiyonunu içerir. Bu fonksiyon Sesi Algılayıcı arabirim işlemi havale etmek üzere Müzik Sentezleyici içinde uygulanan Sesi Algıla fonksiyonunu çağırır. Bu arabirim Sesi Algılayıcı-1 tarafından uygulanır ve gerekli sesleri algılar. Şimdiki durumda Sesi Algıla ve Midi Çal’ın birbirinden bağımsız olarak uygulanması yapılabilir. Bu iki fonksiyon arasındaki bağlılık ortadan kalkmış durumdadır. SesiAlgıla, Müzik Sentezleyici’yi beraberinde getirmeye gerek kalmadan çağırabilir ve SesiAlgıla fonksiyonunu kullanarak, MidiÇal fonksiyonunun farklı uygulamaları yapılabilir.

Soyut Fabrika BTP modüllerinin somut sınıflara bağlı olmamasını önermektedir. Ancak bir sınıfın nesnelerini yaratmak için, somut sınıflara bağımlı olmak zorundayız. SOYUT FABRİKA paterni somut sınıflara bağımlılığın sadece tek bir yerde olmasını sağlar. Modem yaratmak isteyen tüm kullanıcılar ModemFabrikası olarak adlandırılan arabirimi kullanırlar. Bu arabirimi gösteren bir göstergeç, gFabrika isimli bir global değişkende tutulur. Kullanıcılar istedikleri Modem’in özel bir alt sınıflarını tanımlayan bir sözdizinini (string) parametresiyle Make fonksiyonunu çağırırlar. Make fonksiyonu Modem arabirimini gösteren bir göstergeç döner. Modem Fabrikası arabirimi Modem Fabrikası-1 tarafından uygulanır. Bu sınıf Ana Program tarafından yaratılır, bu sınıfı gösteren bir göstergeç gFabrika global değişkenine yüklenir. Böylece, göstergeç Modem Fabrikası-1 dışındaki hiçbir modül somut modem sınıfı hakkında bir bilgiye sahip değildir. Ve AnaProgram dışında hiçbir modül Modem Fabrikası-1’i bilmez.
Yorum Gönder