CS/디자인패턴

[디자인패턴] Proxy 패턴 (대리인 패턴)

mabb 2023. 6. 25. 08:22
반응형

▶디자인 패턴 ( Design Pattern)

:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four) 라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 

▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.

 

---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Proxy 패턴

"대리인"
"필요하면 생성"
"미리 만들면 불필요하게 초기 로딩이 생김"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스

▶Proxy 패턴에 대한 이해

-proxy는 대리인을 의미한다.

-proxy를 앞에 두는 주체를 subject라고 칭한다.

-대리인 자신이 처리할 수 없을 때 subject를 부른다. 필요하면 생성하는 것.

-proxy는 가벼운 일을 처리하고, 처리할 수 없는 일을 요청받았을 때  subject를 생성하여(불러) 요청한다.

-애초에 subject가 가벼운 일도 모두 처리하면 되지 않을까? ㅡ> 원할 경우 proxy가 중간에 끼지 않도록 하면 된다.

-subject를  초기화(생성)에 시간이 많이 걸리는 시스템으로 생각한다. 시스템 기동 시점에 subject를 생성하면 애플리케이션을 기동 하는 시간이 매번 느려지게 된다.

-특정 기능 처리를 위한 객체의 생성에 긴 시간이 소요된다고 할 때, 기동 시점에 그 객체를 매번 생성하여 기동 시간을 지연시킬 필요는 없다. 특정 기능이 필요한 시점에 객체를 생성하면 사용자 편의가 증대된다.

-초기에 다 만들지 않고 필요할 때 생성하는 것이다.

-프록시에는 Virtual Proxy, Remote Proxy, Access Proxy가 있다. 필요할 때 생성하는 것은 Virtual Proxy에 해당한다. Remote Proxy는 원격의 대상에 대한 proxy, Access Proxy는 대상에 대한 접근을 앞에서 허용하거나 막는 proxy 역할을 한다.

-HTTP Proxy 웹서버의 캐싱 기능을 비슷한 예로 생각할 수 있다. 매번 서버에서 페이지를 취득하지 않고 proxy에서 캐시 한 페이지를 반환한다. proxy는 캐시의 유효기간이 지났거나 갱신이 필요한 경우 서버의 페이지를 캐싱한다.

-클라이언트와 Subject사이에 proxy가 끼어도 되고 안 끼어도 된다. 이 경우 중간에 끼어도 아무 상관없는 proxy를 투과적이라고 표현한다.

 

▶왜 사용하는가

필요할 때 생성하여 초기 기동 시간을 줄이는데 사용할 수 있다. 특정 기능을 처리할 수 있는 객체의 생성에 10초가 걸린다고 할 때, 기동 시점에 객체를 생성하면 기동이 매번 느려지게 된다.

 

▶클래스다이어그램

Proxy패턴의 클래스다이어그램

 

▶각 클래스(인터페이스)의 역할

▷Subject(주체) 역할 -  Printable.java

프록시와 subject를 동일시할 수 있도록 하는 인터페이스 역할이다.

▷RealSubject 역할 - Printer.java

생성이 오래걸리는 무거운 객체이다. 간단한 처리는 대리인에게 위임하고 있다.

▷Proxy(대리인) 역할 - PrinterProxy.java

subject를 대신하여 간단한 처리를 수행하고 subject가 필요한 경우 subject를 생성한다.

▷Main 역할 - Main.java

proxy에게 요청하는 역할을 한다. 프록시를 사용하고 싶지 않다면 subject를 이용할 수 있다.

 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)

 

▷Subject(주체) 역할 -  Printable.java

package main.java.designpattern.proxy;

public interface Printable {
    public abstract void setPrinterName(String name);
    public abstract String getPrinterName();
    public abstract void print(String string);
}

 

▷RealSubject 역할 - Printer.java

package main.java.designpattern.proxy;

public class Printer implements Printable{
    public String name;
    public Printer(){
        heavyjob("Printer의 인스턴스 생성중");
    }
    public Printer(String name){
        this.name = name;
        heavyjob("Printer의 인스턴스 생성중");
    }

    @Override
    public void setPrinterName(String name){
        this.name = name;
    }
    @Override
    public String getPrinterName(){
        return name;
    }
    @Override
    public void print(String string){
        System.out.println("---"+name+"---");
        System.out.println(string);
    }
    private void heavyjob(String msg){
        System.out.print(msg);
        for(int i=0; i<5; i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.print(". ");
        }
        System.out.println("완료");
    }
}

 

▷Proxy(대리인) 역할 - PrinterProxy.java

package main.java.designpattern.proxy;

public class PrinterProxy implements Printable {
    private String name;
    private Printable real;

    public PrinterProxy(String name){
        this.name = name;
    }

    @Override
    public synchronized void setPrinterName(String name) {
        if (real != null){
            real.setPrinterName(name);
        }
        this.name = name;
    }

    @Override
    public String getPrinterName() {
        return name;
    }

    @Override
    public void print(String string) {
        realize();
        real.print(string);
    }

    private synchronized void realize(){
        if(real == null){
            real = new Printer(name);
        }
    }
}

 

▷Main 역할 - Main.java

package main.java.designpattern.proxy;

public class Main {
    public static void main(String[] args){
        Printable p = new PrinterProxy("프린터대리");
        System.out.println(p.getPrinterName());
        p.setPrinterName("프린터 대리!");
        System.out.println(p.getPrinterName());

        p.print("Hello World");
    }
}

---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형