Proxy Pattern

Proxy는 대리인 이라는 뜻으로, 뭔가를 대신해서 처리하는 것
Proxy Class를 통해서 대신 전달하는 형태로 설계되며, 실제Client는 Proxy로부터 결과를 받는다.

Cache의 기능으로도 활용이 가능하다. SOLID 원칙 중에서 개방폐쇄원칙과(OCP)과 의존 역전 원칙(DIP)을 따른다.

public interface IBrowser {
  Html show();
}
public class Html {

  private String url;

  public Html(String url){
    this.url = url;
  }
}
public class Browser implements IBrowser {

  private String url;

  public Browser(String url) {
    this.url = url;
  }

  @override
  public Html show() {
    System.out.println("browser loading html from : " + url);
    return new Html(url);
  }
}
//psvm
Browser browser = new Browser("www.somthing.com");
browser.show();
browser.show();
browser.show();
browser.show();

여기까지는 browser.show();를 할때마다 매번 불러오게 되는데, 프록시 패턴을 사용하여 캐시기능을 만들어 봅시다.

public class BroswerProxy implements IBroswer {

  private String url;
  private Html html;

  public BroswerProxy(String url) {
    this.url = url;
  }

  @override
  public Html show {
    if(html==null) {
      this.html = new Html(url);
      System.out.println("BroswerProxy loading html from : " + url);
    } else {
      System.out.println("BroswerProxy use cache from : " + url);
    }

    return html;
  }

}

이제 캐싱기능이 추가된 BroswerProxy를 통해서 show()메소드를 호출해보자.

//psvm
IBroswer broswer = new BroswerProxy("www.somthing.com");
browser.show(); //처음에만 loading으로 호출되고,
browser.show(); //이 아래로는 cache로부터 로드된다.
browser.show();
browser.show();

AOP 패턴

public class AopBroswer implements IBroswer {

  private String url;
  private Html html;
  private Runnable before;
  private Runnable after;

  public AopBroswer(String url, Runnable before, Runnable after) {
    this.url = url;
    this.before = before;
    this.after = after;    
  }

  @override
  public Html show {
    before.run();

    if(html==null){
      this.html = new Html(url);
      System.out.println("AopBroswer loading html from : " + url);
    } else {
      System.out.println("AopBroswer use cache from : " + url);
    }

    try{
      Thread.sleep(1500);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }    
    after.run();

    return html;
  }
}
//psvm

AtomicLong start = new AtomicLong();
AtomicLong end = new AtomicLong();

IBroswer aopBroswer = new AopBroswer("www.something.com",
  ()->{
    System.out.println("before");
    start.set(System.cvurrentTimeMillis());
  },
  () ->{
    long now = System.cvurrentTimeMillis();
    end.set(now - start.get());
  }
);
aopBroswer.show();
System.out.println("loading time : " + end.get());
aopBroswer.show();
System.out.println("loading time : " + end.get());

처음 show()메소드를 호출 하면 1500ms보다 살짝 더 걸리는 지연시간이 나오는데, 두번째 로딩할 때는 로딩 시간이 0ms으로 나온다. 이게 캐싱의 힘인가..?

Decorator Pattern

데코레이터 패턴은 기존 뼈대(클래스)는 유지하되, 이후 필요한 형태로 꾸밀 때에 사용한다. 확장이 필요한 경우 상속의 대안으로도 활용된다. SOLID원칙 중에서 개방폐쇄원칙 (OCP), 의존역전원칙 (DIP)를 따른다.

public interface ICar {

  int getPrice();
  void showPrice();

}
public class Audi implements ICar {

  private int price;
  public Audi(int price){
    this.price = price;
  }

  @Override
  public int getPrice(){
    return price;
  }

  @Override
  public void showPrice(){
    System.out.println("this audi costs : " + this.price);
  }
}
//psvm
ICar audi = new Audi(1000);
audi.showPrice(); // 1000;

아우디의 차종은 여러개이고, 가격도 제각각이므로 이걸 구현하기 위해 데코레이터 패턴을 적용해보자.

public class AudiDecorator implements ICar{

  protected ICar audi;
  protected String modelName;
  protected int modelPrice;

  public AudiDecorator(ICar audi, String modelName, int modelPrice){
    this.audi = audi;
    this.modelName = modelName;
    this.modelPrice = modelPrice;
  }

  @Override
  public int getPrice(){
    return audi.getPrice() + modelPrice;
  }

  @Override
  public void showPrice(){
    System.out.println("audi " +modelName + "costs : " + getPrice());
  }
}

아우디 데코레이터를 다 만들었으니, 이제 아우디 모델들을 만들어 보자.

A3

public class A3 extends AudiDecorator{
  public A3(ICar audi, String modelName){
    super(audi, modelName, 1000);
  }
}

A4

public class A4 extends AudiDecorator{
  public A3(Audi audi, String modelName){
    super(audi, modelName, 2000);
  }
}

A5

public class A5 extends AudiDecorator{
  public A3(Audi audi, String modelName){
    super(audi, modelName, 3000);
  }
}

아우디 모델을 전부 만들었다. 각각 가격을 A3는 +1000, A4는 +2000, A5는 +3000 해주었다. 이제 메인 함수에 적용시켜 보자.

//psvm
ICar audi = new Audi(1000);
audi.showPrice(); // 1000;

//A3
ICar audi3 = new A3(audi, "A3");
audi3.showPrice(); // 2000
//A4
ICar audi4 = new A4(audi, "A4");
audi4.showPrice(); // 3000
//A5
ICar audi5 = new A5(audi, "A5");
audi5.showPrice(); // 4000

데코레이터 패턴을 사용하여 각 아우디 모델에 대한 가격을 바꾸도록 하였다 끗.