Tasarım Örüntüsü etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Tasarım Örüntüsü etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

3 Ekim 2023 Salı

GoF - Abstract Factory Örüntüsü

Örnek
Elimizde şöyle bir hiyerarşi olsun
// Abstract Product - Button
public interface Button {
  void render();
}

// Concrete Product - WindowsButton
public class WindowsButton implements Button {
  @Override
  public void render() {
    System.out.println("Rendering a Windows button");
  }
}

// Concrete Product - MacButton
public class MacButton implements Button {
  @Override
  public void render() {
    System.out.println("Rendering a Mac button");
  }
}
Şöyle yaparız
// Abstract Factory
public interface GUIFactory {
  Button createButton();
}

// Concrete Factory - WindowsFactory
public class WindowsFactory implements GUIFactory {
  @Override
  public Button createButton() {
    return new WindowsButton();
  }
}

// Concrete Factory - MacFactory
public class MacFactory implements GUIFactory {
  @Override
  public Button createButton() {
    return new MacButton();
  }
}


24 Ağustos 2020 Pazartesi

GoF - Decorator Örüntüsü - Kalıtım Kullanarak

Giriş
Decorator'ün çalışabilmesi için sarmalanan nesnenin, bir arayüzden kalıtması gerekir. Yeni geliştirilen decorator da aynı arayüzden kalıtır.
The decorator implements the same interface as the type it decorates, each method calling the decorated type's members, adding functionality as needed.
Örnek
Örneğin ICar isimli bir arayüzümüz olsun.
public interface ICar
{
  void Drive();
  void Stop();
  void Park();
}
Basit bir decorator her işlemi sarmaladığı nesneye gönderir.
class Decorator : ICar
{
  private readonly ICar _car;

  public Decorator(ICar car) {
    _car = car;
  }

  public void Drive()
  {
    _car.Drive();
  }

  public void Stop()
  {
    _car.Stop();
  }

  public void Park()
  {
    _car.Park();
  }
}
Örnek
Elimizde şöyle bir kod olsun. Bu kod "DarkRoast with double mocha and a whip" yaratır.
Beverage beverage = new DarkRoast();
beverage = new Mocha(beverage);
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
Bazı Örnekler
Kullanımına bazı iyi örnekler:

1. O ana kadar sınıf hiyerarşisinden bulunmayan yeni bir özelliğin eklenmesi. Örneğin widget hiyerarşisinin draw() metoduna vertical/horizontal scroll bar veya border eklenmesi gibi.

2. Miktarı farklı ilaveler kullanarak fiyat hesaplanması. Örneğin süt vs. gibi ilaveler kendi fiyatlarını da sarmalanan kahve üzerine ekleyerek yeni bir hesaplama yapmamıza imkan tanıyor.

3. Loglamada hem loglama yapılması, hem de eğer log belli bir cümleyi içeriyorsa tweet atılması.Örnekte TweetingFileWriter gerekiyorsa Tweet atıyor.

Benim Gördüğüm Bazı Örnekler
1. Bir kaynaktan gelen event'leri işleyem callback yapısı vardı. Daha sonra bu event'leri sıralı işleme ihtiyacı ortaya çıkınca event'leri belli bir mantığa göre sıralayan bir callback decorator gördüm.


Bazı sıkıntılar
1. Eğer sarmalanan nesne çok büyük bir arayüze sahipse, decorator örüntüsü bir sürü metodu sarmalanan nesneye delege etmek zorunda kaldığı için kullanışsız hale gelebilir. Örnek'te durum görülebilir.
//decorator has to implement it, but just passes the operation
public ITearResult tear() {
    return page.tear();
}
2. Eğer kod nesnenin tipine göre çalışıyorsa decorator kodu bozabilir. Bu madde Head First Design Patterns kitabından anlatılıyor. Örneğin b nesnesini sarmalayan bir decorator yaratalım.
I b = new B; // I is an interface implemented by B
b = D(b); // the decorator D implements I as well
doSomething(b);
Eğer kod şöyle çalışıyorsa Decorator işlevi bozar.
doSomething(I i){
    if i instanceof B
        doThis();
    else
        doThat();
}
Dikkat Edilmesi Gereken Noktalar
Decorator Liskov Kuralına uygun davranmalıdır. Yani kalıttığı arayüzü bozmamalıdır.

Örnek
Örnekte WorkBank arayüzü görülüyor. Bu arayüz'den kalıtan SomeWordBank getWords() metodu ile bir dizi kelime dönüyor. WordSorter bir decorator olarak kelimeleri sıralayarak döndürüyor. Eğer arayüzümüz çift kelimelere izin verseydi ve WordSorter çift kelimeleri süzerek döndürseydi Liskov kuralını ihlal ettiği için iyi bir decorator olmazdı.


Decorator Nasıl Yaratılır
Bir çok decorator basit bir şekilde constructor metoduna sarmayalacağı nesneyi alır. Çok nadiren de olsa Decorator yaratmak için Factory kullanılabilir.

Örnek
Elimizde şöyle bir kod olsunn-
public interface Animal {
  void eat();
}

public class Lion implements Animal {
  public void eat() {
    // do something
  }
}

/* In the original Decorator pattern, 
the decorator is an abstract class, 
but for the sake of brevity, 
in this example it's a concrete class. */
public class AnimalWithEatCountDecorator implements Animal {
  private Animal animalWeWantToCountEats;
  private int eatCount=0;

  public AnimalWithEatCountDecorator(Animal animal) {
    this.animalWeWantToCountEats= animal;
  }
        
  public void eat(){ 
    this.animalWeWantToCountEats.eat();
    this.eatCount++;
  }
        
  public int getEatCount() {
    return this.eatCount;
  }   
}  
Şöyle yaparız
public static void main(String[] args) {
  AnimalWithEatCountDecorator lion = new AnimalWithEatCountDecorator(new Lion());
  lion.eat();
  lion.eat();
  lion.eat();
        
  System.out.println(lion.getEatCount());
}
Örnek
Şöyle yaparız. Factory verilen path'i InputStream ve daha sonra BufferedInputStream ile sarmalıyor. Ek olarak eğer dosya ismi .gz ile bitiyorsa GZIPInputStream ile de sarmalayıp sarmalamamaya karar veriyor.
public static InputStream openFile(File file) throws IOException {
  InputStream stream = null;
  try {
    stream = new FileInputStream(file);
    stream = new BufferedInputStream(stream);
    if (file.getName().endsWith(".gz"))
       stream = new GZIPInputStream(stream);
    return stream;
  } catch (IOException ex) {
    closeQuietly(stream);
    throw ex;
  }
}

22 Temmuz 2019 Pazartesi

GoF - Decorator Örüntüsü

Decorator İsmi
Decorator için isim seçmek her zaman en büyük problem :)
- FilteredCollectionDelegationWork gibi isimlerin iyi olmadığını düşünenler var.

Decorator - Yapısal Örüntü
Decorator bir yapısal örüntüdür. En önemli faydaları nesneleri birleştirerek daha geniş yapılar oluşturması, kalıtım hiyerarşisinde niceliğin artmasını engellemesi (explosion of subclasses) ve legacy kodlarda, kalıtıma dokunmadan değişiklik yapabilmeyi kolaylaştırması olarak sayılabilir.

Bu örüntü iki şekilde gerçekleştirilebilir.
1. Kaltım Kullanarak
2. Kalıtım Kullanmadan Sadece Sarmalayarak. Bu kullanımda ismine sadece Wrapper demek daha doğru olabilir.

Her iki kullanımda da amaç gerçek nesneyi sarmalayarak davranışını değiştirmektir. Böylece o ana kadar sınıf hiyerarşisinde olmayan yeni bir davranış şekli elde edilebilir. Açıklaması şöyle.
Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.
Decorator ile "Attribute,Aspect,Trait" arasında farklılıklar bulunur.

Decorator ve Arayüzden Kalıtım - Birinci Kullanım Şekli
Decorator Örüntüsü - Kalıtım Kullanarak yazısına taşıdım.

Sarmalama İşlemi - İkinci Kullanım Şekli
Sarmalama işleminde sınıfa Wrapper demek daha iyi. Bir yorum şöyle
Trust me, "Decorator" doesn't make any sense in English either. The reason you see this term is due to the influence of a software pattern book written 20 years ago by the so-called "Gang of Four." They described patterns in common use but decided to give them their own, sometimes odd names. "Decorator" is one example. Everyone I knew called this a "Wrapper" class because you "wrap" other classes inside another. Much more descriptive, don't you think?
Buna verilen cevap şöyle.
"Decorator" refers to the pattern's purpose, whereas "Wrapper" refers to the pattern's form.
Örnek
Sarmalama işlemini şöyle yaparız.
TextEmail txtEmail = new TextEmail();
SecuredEmail securedEmail = new SecuredEmail (txtEmail);
Decorator ve Proxy
Decoeator ve Proxy birbirlerine çok benziyorlar. Proxy şöyledir

Decorator ve Private Alanlar
Decorator, davranışını değiştirmek istediği nesnenin private alanlarına erişme konusunda sıkıntı çekebilir. Bu alanları public yapmak istemiyorsak reflection kullanmak gerekebilir.

Decorator Örüntüsüne Çok Benzeyen Nesneye Özellik Ekleme ve Çıkarma
Bazen nesneler belli özellikleri listeler halinde tutarlar. Örneğin roller gibi. Rollerin eklenip çıkarılması davranışı değiştirmiyor sadece nesneye veri ekleyip çıkarıyorsa decorator'e gerek yoktur.

Decorator ve Mock
Decorator ve Mock nesnesi içerdikleri nesneyi taklit ettikleri, yani oymuş gibi davrandıkları için bir açıdan benzerler. Ancak kullanım sahaları tamamen farklıdır. Decorator gerçek kodun içinde kullanılırken, Mock nesneleri kodu test etmek için kullanılırlar.