6 Kasım 2018 Salı

Curiously Recurring Template Pattern - CRTP

Giriş
Curiously recurring template pattern kullanımının iki tane ana konusu var.

1. Polymorphism . Örneğin Template Method Pattern için kullanılabilir.
2. Ata sınıfta kalıtan sınıf tipine olan ihtiyaç. Örneğin Binary Tree, Fluent Methods gibi kullanım çeşitlerinde gerek olabilir.

Data Structures kullanımında ata sınıf T tipinden bir başka child veya parent nesneye erişim ister. Örneğin Binary Tree, Linked List gibi veri yapılarında bu durum vardır.Eğer CRTP kullanmazsak kod şöyle olur.
public class Node {
  private Node parent;
  private List<Node> children;
}
Fluent Methods kullanımında ata sınıf metodu T tipi dönecek şekilde yazılır. Böylece kalıtan sınıf otomatik olarak kendini döner. Cast etmeye gerek kalmaz.

Builder Pattern
Elimizde şöyle bir kod olsun.
class A {

  private final int value;

  public static <T extends Builder<T>> T builder() {
    return (T)new Builder<>();
  }

  protected A(Builder<? extends Builder<?>> builder) {
    value = builder.value;
  }

  public static class Builder<T extends Builder<T>> {

    private int value;

    public T withValue(int value) {
      this.value = value;
      return (T)this;
    }

    public A build() {
      return new A(this);
    }
  }
}
Şöyle bir kod olsun.
class B extends A {

  private final String name;

  public static <T extends Builder<T>> T builder() {
    return (T)new Builder<>();
  }

  protected B(Builder<? extends Builder<?>> builder) {
    super(builder);
    name = builder.name;
  }

  public static class Builder<T extends Builder<T>> extends A.Builder<T> {

    private String name;

    public Builder<T> withName(String name) {
      this.name = name;
      return this;
    }

    public B build() {
      return new B(this);
    }
  }
}
Şöyle yaparız.
A a = A.builder().withValue(1).build();
B b = B.builder().withValue(2).withName("xx").build();
Template Pattern
Örnek
Şöyle yaparız. Burada hem arayüzün gerektirdiği copy() metodu tanımlanıyor hem de bu metodun kalıtan T tipinden döneceği belirtiliyor
public interface Recur<T extends Recur<T>> {
  T copy();
}
Kalıtan sınıf için şöyle yaparız.
class A implements Recur<A> {
  @Override
  public A copy() {
    return new A();
  }
}
Şu kod derlenmez çünkü T tipinin Recur sınıfından kalıtması gerekir.
class A implements Recur<String> {...}
Örnek
Kendisinden yeni nesne yaratan kod için şöyle yaparız.
public class Sample<T extends Sample<T>> {
  public static Sample<? extends Sample<?>> get() {
    final Sample<?> s = get0();
    return s;
  }

  private static <T extends Sample<T>> Sample<T> get0() {
    return new Sample<T>();
  }
}
get metodu şöyle de olabilirdi
public static <X extends Sample<X>> Sample<? extends Sample<?>> get() {
  return new Sample<X>();
}
Örnek
Kalıtan sınıfın kendi tipinden bir abstract metodu yapmasını isteyelim. Elimizde şöyle bir kod olsun.
abstract class Event<T extends Event<T>> {
  abstract boolean intersect(T object);
}
Şöyle yaparız
class SubClassEvent extends Event<SubClassEvent> {
   @Override
  boolean intersect(SubClassEvent object){return true;}
}

Hiç yorum yok:

Yorum Gönder