구조 디자인패턴(3) -플라이웨이트 패턴, 프록시 패턴

플라이웨이트 패턴

객체를 가볍게 만들어 메모리사용을 줄이는패턴

  • 자주변하는속성과 변하지않는 속성을 분리하고 재사용하여 매모리사용을 줄일수있다.
public class Character {

    private char value;

    private String color;

    private String fontFamily;

    private int fontSize;

    public Character(char value, String color, String fontFamily, int fontSize) {
        this.value = value;
        this.color = color;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
    }
}

 public static void main(String[] args) {
        Character c1 = new Character('h', "white", "Nanum", 12);
        Character c2 = new Character('e', "white", "Nanum", 12);
        Character c3 = new Character('l', "white", "Nanum", 12);
        Character c4 = new Character('l', "white", "Nanum", 12);
        Character c5 = new Character('o', "white", "Nanum", 12);
    }

폰트패밀리,폰트사이즈 색상등이 겹쳐서 메모리를 많이 차지하고있는듯한 모습이다.


public final class Font {

    final String family;
    final int size;

    /**
     *  플라이웨이트에해당하는 인스턴스는 불변해야한다.
     *     다른여러 객체에서 공유하는 객체이기때문이다.
     *     클래스에 final 필드값에 final을 붙였다.    
     */

    public Font(String family, int size) {
        this.family = family;
        this.size = size;
    }

    public String getFamily() {
        return family;
    }

    public int getSize() {
        return size;
    }
}

플라이웨이트 인스턴스는 불변해야한다.

public class Character {

    private char value;

    private String color;

    private Font font;

    public Character(char value, String color, Font font) {
        this.value = value;
        this.color = color;
        this.font = font;
    }
}

public class FontFactory {

    private Map<String, Font> cache = new HashMap<>();

    public Font getFont(String font) {
        if (cache.containsKey(font)) {
            return cache.get(font);
        } else {
            String[] split = font.split(":");
            Font newFont = new Font(split[0], Integer.parseInt(split[1]));
            cache.put(font, newFont);
            return newFont;
        }
    }
}

   public static void main(String[] args) {
        FontFactory fontFactory = new FontFactory();
        Character c1 = new Character('h', "white", fontFactory.getFont("nanum:12"));
        Character c2 = new Character('e', "white", fontFactory.getFont("nanum:12"));
        Character c3 = new Character('l', "white", fontFactory.getFont("nanum:12"));
    }


플라이웨이트 패턴

장점

  • 애플리케이션에서 사용하는 메모리를 줄일수있다.

단점

  • 코드의 복잡도가 증가한다.

숫자나 문자열비교할때 == 쓰지말자

프록시 패턴

특정객체에 대한 접근을 제어하거나 기능을 추가할수있는 패턴

  • 초기화지연, 접근제어, 로깅, 캐싱등 다양하게 사용할수있다.

프록시 = 인터페이스를 필드값으로 가지고있다.
RealSubject 를 참조하기위함이다.

public class GameService {

    public void startGame() {
        System.out.println("이 자리에 오신 여러분을 진심으로 환영합니다.");
    }
    // 코드의 실행시간을 확인하고싶은데 코드는 변경할수없다.

}
    public static void main(String[] args) throws InterruptedException {
        GameService gameService = new GameService();
        gameService.startGame();
    }
    // 코드를 추가하면 시간을 잴수있겠지만, 코드를 유지하면서 시행하고싶다.
public class GameServiceProxy extends GameService {

    @Override
    public void startGame() {
        long before = System.currentTimeMillis();
        super.startGame();
        System.out.println(System.currentTimeMillis() - before);
    }
}

   public static void main(String[] args) {
        GameService gameService = new GameServiceProxy();
        gameService.startGame();
    }

클래스를 하나 만들어 상속으로 해결한다
GameService 를 고칠수없는경우엔 해결법이 될수있겠다.
하지만 손댈수있는경우라면


public interface GameService {

    void startGame();

}

public class DefaultGameService implements GameService{

    @Override
    public void startGame() {
        System.out.println("이자리에 오신걸 환영합니다");
    }
}

GameService를 인터페이스로 만들고

public class GameServiceProxy implements GameService {

    private GameService gameService;

  //    public GameServiceProxy(DefaultGameService defaultGameService) {
//        this.gameService = gameService;
//    }

    @Override
    public void startGame() {
        long before = System.currentTimeMillis();
          if (gameService == null) {
            this.gameService = new DefaultGameService();
        }
        gameService.startGame();
        System.out.println(System.currentTimeMillis() - before);

    }
}
  public static void main(String[] args) {
       // GameService gameService = new GameServiceProxy(new DefaultGameService());
        //gameService.startGame();
       
          GameService gameService = new GameServiceProxy();
        gameService.startGame();
    }

클라이언트가 DefaultGameService를 쓰긴써야되는데 GameServiceProxy를 거쳐서 사용하고있다.

프록시패턴의 장점

  • 기존코드를 변경하지않고 새로운기능을 추가할수있다.
  • 기존코드가 해야하는일만 유지할수있다.
  • 기능추가 및 초기화지연등으로 다양하게 활용할수있다.

단점

  • 코드의 복잡도가 증가한다.

사용사례

  public static void main(String[] args) {
        ProxyInJava proxyInJava = new ProxyInJava();
        proxyInJava.dynamicProxy();
    }

    private void dynamicProxy() {
        GameService gameServiceProxy = getGameServiceProxy(new DefaultGameService());
        gameServiceProxy.startGame();
    }

    private GameService getGameServiceProxy(GameService target) {
        return  (GameService) Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[]{GameService.class}, (proxy, method, args) -> {
                    System.out.println("O");
                    method.invoke(target, args);
                    System.out.println("ㅁ");
                    return null;
                });
    }

동적으로 런타임시에 생성되는 프록시
이러한기능들이 여러메서드에 적용해야된다면
비슷한코드가 많이 들어가지만 동적으로 런타임시에 생성해서 조금더 효율적으로 사용할수있다.

스프링의 AOP
여러곳에 흩어져있는 개념을 한곳에 모아서 처리하게끔 하는개념

@Service
public class GameService {

    public void startGame() {
        System.out.println("이 자리에 오신 여러분을 진심으로 환영합니다.");
    }

}

@Aspect
@Component
public class PerfAspect {

    @Around("bean(gameService)")
    public void timestamp(ProceedingJoinPoint point) throws Throwable {
        long before = System.currentTimeMillis();
        point.proceed();
        System.out.println(System.currentTimeMillis() - before);
    }
}

스프링이 관리하는 Bean에만 Aspect를 적용할수있어서
@Component 로 빈으로 등록.

스프링이 구동될떄 동적으로 Bean을 만들어서 등록해주고
프록시빈을 주입받아서 사용하게된다.

참조: https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4#

좋은 웹페이지 즐겨찾기