Tür: Yapısal
Şablon
Diğer Adı:
Sarmalayıcı (Wrapper)
Adında
anlaşılacağı gibi bezeme şablonu ilişkilendirildiği (bu manada Ingilizce
'attach' fiili kullanılır) sınıfı dinamik olarak dekore eder. Bezeme şablonuna
ayrıca sarmalıyıcı (Wrapper) da denir. Bu isimle daha çok karşılaşmak mümkündür
çünkü C# dilinin kendi içinde doğal türlerin (int, float, double gibi) birer
Wrapper sınıfları bulunur. Buradaki konsept her ne kadar farklı da olsa gerçek
hayatta bir şablonun zaten tek ve nihayi bir hali yoktur.
Bezeme şablonuna
alternatif olarak sınıf türetme düşünülebilir. Fakat bu durumda bir sınıfın
alacağı durum önceden belirlenmiş (statik olarak) ve sınırlandırılmış olur. Bu
genellikle karşılaşılan bir durumdur fakat bazı durumlar vardır ki dinamik
olarak bezemek daha çok işimize gelebilir. Örneğin bir sınıfımıza
ekleyebileceğimiz n tane yeteneğimiz (capability) olsun. Bu yeteneklerin bir
yada bir kaçını destekleyen alt sınıflar elde etmek istersek n'in bütün
kombinasyonlarını toplamamız gerekir. Fakat bu işi bezeme sınıfları ile
yaparsak sadece n tane bezeme sınıfını türetmek yeterli olur.
Bezeme şablonu
oldukça sık karşılaşılan bir şablondur. Son zamanlarda moda olan AOP (Aspect
Oriented Programming) özellikle bezeme şablonu üzerine bina olmuştur. AOP'ta
Interceptor denilen nesneler kendisi için özel olarak bezenmiş sınıflara
sonradan raporlama gibi iş hayatında önemli özellikler katabilir.
Bezeme şablonu en
çok katmanlı yapılarda kullanılır. Her sınıf bir katman teşkil eder. Katmanlı
yapılara da en iyi örnek görsel uygulamalardır (GUI App - Graphic User
Interface Application). İlk önce boş ekranı temsil eden bir sınıf olur. Bu
sınıf bile tek başına görüntü oluşturabilir. Fakat çeşitli kütüphanelerde
(.NET, Java Swift, Qt gibi) farklı olmakla birlikte çok benzer katmanlı görsel
sınıflar vardır. Bu sınıflar hepsi aynı sınıftan türerler. Hepsinin kendini
çizdirme metodları vardır. İstenilen çizdirme özelliği için var olan görsel
nesne bu yeteneği temsil eden sınıf tarafından sarmalanır.
Burada GoF'un meşhur kitabı olan "Design Patterns: Elements of Reusable Object Oriented Software"dan alıntı. Burada görsel olarak katmanlamanın nasıl yapıldığı ve sonucun neye dönüştüğü gösterilmiştir. Aşağıda da bu yapının sınıf çizelgesi de vardır.
Burada VisualComponent ana sınıfımızdır. Genellikle soyut sınıf (abstract class) olur. TextView sınıfı sadece verilen yazıyı göstermekle yükümlüdür. Diğer Decorator'dan türeyen ScrollDecorator ve BorderDecorator sınıfları TextView sınıfını isteğe bağlı olarak bezer. İster biri, ikisi yada hiçbiri kullanılmayabilir.
Çünkü tüm sınıflar bir birinden bağımsızdırlar (decoupled, loose coupling çok önemli bir konudur ayrıca bahsedeceğim).
Ayrıca Java'daki
dosya işleme sınıfları tamamen bezeme sınıfları üzerine kurulmuştur.
public class FrequencyCountedInputStream extends FilterInputStream implements Iterable<String> { // constructor @Override public int read() throws IOException { int ic = super.read(); char c = (char)ic; if (c == ' ' || c == '\r' || c == '\n' || c == '\t') { word = word.trim(); if (word == "") return ic; putWord(word); word = ""; } else word += c; return ic; } @Override public int read(byte[] buffer, int offset, int length) throws IOException { int result = super.read(buffer, offset, length); String word = ""; char c; for (int i = offset; i < offset + result; ++i) { c = (char)buffer[i]; if (c == ' ' || c == '\r' || c == '\n' || c == '\t') { word = word.trim(); if (word == "") continue; putWord(word); word = ""; } else word += c; } return result; } void putWord(String word) { Integer value = wordFrequencies.get(word); if (value == null) value = 0; wordFrequencies.put(word, value + 1); } // other functions String word; HashMap<String, Integer> wordFrequencies; }
Buradaki FilterInputStream dosya işleme sınıfı bezeyicisinden başka birşey değildir. Bizim sınıfımız da açılmış dosyadaki kelimelerin ne sıklıkla geçtiklerini (frekanslarını) daha dosyayı okurken hashmap'a kaydeder. Ayrıca burada iki tane read fonksiyonu da kullanılmıştır çünkü az sonra kullanacağımız örnek read fonksiyonunun parametreli olanını çağırırken genel kullanımda parametresiz read çağrılır. Aşağıda ise sınıfımızı kullanan örneği yazcağım.
// imports and package info public class CustomFileInputTest { @Override public static void main(String[] args) { String fileName; System.out.print("Enter file name: "); try { BufferedReader keyReader = new BufferedReader( new InputStreamReader(System.in)); fileName = keyReader.readLine(); InputStream in = new FrequencyCountedInputStream( new BufferedInputStream( new FileInputStream(fileName) ) ); BufferedReader fileReader = new BufferedReader( new InputStreamReader(in)); String line; System.out.println("File : " + fileName + "\n"); while ((line = fileReader.readLine()) != null) System.out.println(line); System.out.println("------ End of File -------\n"); System.out.println("Words and their frequencies:\n"); FrequencyCountedInputStream fcin = (FrequencyCountedInputStream)in; int i = 0; for (String word : fcin) { System.out.println(word + " - " + fcin.getValue(word)); i++; } System.out.println("\nNumber of words : " + i); } catch (IOException e) { e.printStackTrace(); } } }
Stream sadece dosya değil ayrıca veri sağlayan herhangi bir şey olabilir. Klavye'yi buna örnek olarak gösterebiliriz. Klavye'nin InputStream nesnesi System.in ile temsil edilir. InputStreamReader System.in'i kolay okunabilir hale getirir. BufferedReader ise klavyeden satır satır okumayı sağlar. Bu sınıflar bezeme sınıfı olmamasına rağmen çok benzer yapıdalardır. Sınıflara yeni özellikler katarlar ama tamamen başka sınıfa dönüştürürler. Bu kompozisyondur.
İkinci kısımda ise BufferedInputStream dosyamızı bezer. Sonra bu bezenmiş nesneyi bizim yazdığımız FrequencyCountedInputStream daha çok bezer ve bu yine bir InputStream nesnesi olarak saklanabilir. Dolayısıyla biz aynı işlemleri bizim nesnemize de uygularsak dosyadan satır satır okuyabiliriz.
İlk önce dosyayı yazdırırız tam bu sırada arka planda frekanslar hesaplanır. Sonra da kelimeleri ve frekanslarını yazdırırız.
Şimdiye kadarki proje kodlarına buradan ulaşabilirisiniz.
Şimdiye kadarki proje kodlarına buradan ulaşabilirisiniz.
NOT: HashMap'e kelime eklerken eğer o kelime varsa yeni kelime eklenmez yeni değer ile yenilenir. Bu da frekans saymak için tam da gerekli şeydir.
Bu yapının kötü
yanı ise bir sürü bağımsız genişletme sınıfı düşünülüp aşırı derecede sınıf
yazımına yol açabilir. Yani rutin bir iş için bile bir sınıfı 10 kere bezemek
kullanışlı değildir. Bu bir tasarım mevzusudur.
Bazen gereksiz
yere bezeme sınıfları düşünülebilir. Unutulmamalıdır ki bir birine tamamen
bağlı kodları ayırmak mantıksız olabilir. Eğer ayrılan parçalar sonra başka bir
şeyle birleşemeyecekse yada olmazsa olmaz ise sınıfı tek bir parça halinde
yazmak gerekli olur.
Buna örnek olarak
3B koşu simulasyonunda koşucuyu temsil ederken, koşucunun hem animasyonunu, hem
bir koşu pistindeki konumunun belirlenmesini ve kamera açılarının belirlenmesi
farklı bezeme sınıflarına bölünse daha iyi gözükebilir fakat uygulamada bu
yetenekler bir birlerinden ayrılamazlar.
Bezeme şablonu ve
strateji şablonu ikisi de bir sınıf nesnesini dinamik olarak değiştirmeyi
hedefler. Fakat strateji şablonu davranışlarını değiştirirken bezeme şablonu
tamamen yeni davranışlar ekler veya çıkarır.
Bu çocuk işini biliyor! Açıklamalarında sahip olduğun düzeni de özellikle takdir ediyorum, insan her iş üzerinde böyle çalışmalı.
YanıtlaSil