16 Eylül 2020 Çarşamba

GoF - Composite Örüntüsü

Composite - Yapısal Örüntü
Composite örüntüsü parça-bütün ilişkisine sahip ağaç yapılarıdır. Yapıdaki tüm nesnelere aynı işlemi kolayca uygulamayı sağlar.
"A composite contains components. Components come in two flavors : composites and leaf elements."
Örüntüdeki Bileşenler
Açıklaması şöyle
Below is the list of classes/objects used in the composite pattern, which has four :

Component – Component is the interface (or abstract class) for the composition of the objects and methods for accessing/processing its child or node components. It also implements a default interface to define common functionalities/behaviors for all component classes.

Leaf – The leaf class defines a concrete component class, which does not have any further composition. The leaf class implements the component interface. It performs the command/task at its end only.

Composite – The composite class defines a concrete component class, which stores its child components. The composite class implements the component interface. It forwards the command/task to the composite objects it contains. It may also perform additional operations before and after forwarding the command/task.

Client – The client class uses the component interface to interact/manipulate the objects in the composition (Leaf and Composite).
Hiyerarşi
Yapısal olarak şöyledir. Composite Base nesnenin listesini içerir.
Base
^
|
Composite --(has) List of Base
Klasik Örnek
GUI'de en üstte bulunan Frame/Panel gibi bir nesne diğer nesneleri içerir. Ana bileşene show() komutu gönderilince içerdiği diğer tüm bileşenlere de aynı komutu gönderir.

İş Yükü Dağıtımı -  Work Load Distribution
Elimizde şöyle bir arayüz olsun
public interface Worker {
  void assignWork(Employee manager, Work work);
  void performWork();
}
Bu arayüzü gerçekleştiren şöyle sınıflar olsun
public abstract class Employee implements Worker {
  ...
}
public class Engineer extends Employee {
  private List<Work> works = new ArrayList<>();
  ...
  @Override
  public void assignWork(Work work) {
    this.works.add(work);
  }
  @Override
  public void performWork() {
  works.stream().forEach(work -> {...});
  works.clear();
  }
}

public class Manager extends Employee {
  List<Employee> managingEmployees = new ArrayList<>();

  public boolean manages(Employee employee) {
    return managingEmployees.add(employee);
  }
  @Override
  public void assignWork(Work work) {
    //Tüm işleri astlara eşit böl ve ata
  }
  @Override
  public void performWork() {
    managingEmployees.stream().forEach(employee -> employee.performWork());
  }
}
En üst yöneticiye işleri atadıktan sonra tüm astlara işlerin dağıtan kod şöyledir.
public static void main(String[] args) {
  Engineer ajay =...;
  Engineer vijay =...;
  Engineer jay =...;
  Engineer martin =...;
  Manager kim =...;
  Engineer anders = ...;
  Manager niels = ...;
  Engineer robert = ...;
  Manager rachelle = ...;
  Engineer shailesh = ...;
  kim.manages(ajay);
  kim.manages(martin);
  kim.manages(vijay);
  niels.manages(jay);
  niels.manages(anders);
  niels.manages(shailesh);
  rachelle.manages(kim);
  rachelle.manages(robert);
  rachelle.manages(niels);
  rachelle.assignWork(...);
  rachelle.performWork();
}
Mesaj Örneği
XML yapısını Composite olarak okuyan bir yapı olsun. Her nesne FieldDefinition arayüzünden kalıtır.
RecordDefiniton has --> AlternativeFieldDefinition
                                  --> NormalFieldDefinition
                                  --> SpareFieldDefinition
Daha sonra bu definition'ları bir composite mesaj haline getirelim.Her bir field kendi içinde FieldDefinition yapısını içerir Her nesne CompositeData ata sınıfından kalıtır.
GenericRecord has --> AlternativeField
                                --> NormalField
                                --> AdaptorField
Böylece stream'e okuma yazma yapabilen generic bir composite elde ederiz.

Bir başka örnek
Önce bir ata sınıf veya arayüz yaratılır. Vehicle isimli bir sınıfımız olsun
public abstract class Vehicle
{
  public void drive(int distance) {
    System.out.println("Drove " + distance + ". Vroom vroom!");
  }

  public abstract int getWeight();
  // etc.
}
Daha sonra ata sınıfı gerçekleştiren başka sınılar yazılır.
public class Car extends Vehicle
{
  protected int weight;

  // ...

  @Override
  public int getWeight() {
    return this.weight;
  }
}
Son olarak bu sınıflar bünyesinde barındıran bir composite yaratılır.
public class CarCarrier extends Vehicle
{
  protected int weight;
  protected Vehicle[] cars;

  // ...

  @Override
  public int getWeight() {
    int weight = this.weight;
    for(Vehicle car : cars) {
      weight += car.getWeight();
    }
    return weight;
  }
}
Örnek
Elimizde şöyle bir arayüz olsun. Burada bir composite içinde olabilecek root, leaf ve ara düğümler gösteriliyor.
interface Human {
  void addChild(Human child);
  void addParent(Human parent);
}
Elimizde şöyle sınıflar olsun
public class GrandParent implements Human {

  private List<Human> children = new ArrayList<>();

  @Override
  public void addChild(Human child) {
    childs.add(child);
  }
  @Override
  public void addParent(Human parent) {
  }
}
public class Parent implements Human {
  private List<Human> children = new ArrayList<>();
  private List<Human> parents  = new ArrayList<>();

  @Override
  public void addChild(Human child) {
    children.add(child);
  }
  @Override
  public void addParent(Human parent) {
    parents.add(parent);
  }
}
public class Child implements Human {

  private List<Human> parents = new ArrayList<>();
   
  @Override
  public void addChild(Human child) {
  }
  @Override
  public void addParent(Human parent) {
    parents.add(parent);
  }
}
Şöyle yaparız
Child child = ...
Parent father = ...
Parent mother = ...

child.addParent(father);
child.addParent(mother);
father.addChild(child);
mother.addChild(child);

GrandParent granpa = ...;

granpa.addChild(father);
father.addParent(granpa);
Zor Tarafları

Composite veri yapısını dolaşması (traverse) işlemi zor olabiliyor.




Hiç yorum yok:

Yorum Gönder