'Pattern' 이란 특정 컨텍스트 내에서 주어진 문제에 대한 해결책이다.


- 'Context' 란 패턴이 적용되는 상황을 뜻하고, 반복적으로 일어날 수 있는 상황이어야만 한다.

- 'Problem' 이란 이루고자 하는 목적을 뜻하며 제약조건도 포함된다.

- 'Solution' 이란 누구든지 적용해서 일련이 제약조건 내에서 목적을 달성할 수 있는 일반적인 디자인을 뜻한다.


※ 'Force' : Problem 이 목적과 제약조건으로 구성되는데, 이를 합쳐서 Force 라 한다.

Solution 이 Force 의 양면 사이에서 균형을 이룰 수 있어야만 제대로 된 패턴이 만들어진다.



# 1~11 장 까지 배웠던 패턴 복습 #

- Strategy

교환 가능한 행동을 캡슐화하고 위임을 통해서 어떤 행동을 사용할지 결정한다.

- Observer

상태가 변경되면 다른 객체들에게 연락을 돌릴 수 있게 해준다.

- Decorator

객체를 감싸서 새로운 행동을 제공한다.

- Factory Method

생성할 구상 클래스를 서브클래스에서 결정한다.

- Abstract Factory :

클라이언트에서 구상 클래스를 지정하지 않으면서도 일군의 객체를 생성할 수 있도록 해준다.

- Singleton :

오직 하나의 객체만 생성되도록 한다.

- Command :

요청을 객체로 감싼다.

- Adapter

객체를 감싸서 다른 인터페이스를 제공한다.

- Facade

일련의 클래스에 대해서 간단한 인터페이스를 제공한다.

- Template Method :

알고리즘의 개별 단계를 구현하는 방법을 서브클래스에서 결정한다.

- Iterator

컬렉션이 어떻게 구현되었는지 드러내지 않으면서 컬렉션 내에 있는 모든 객체에 대해

반복 작업을 처리할 수 있게 해준다.

- Composite :

클라이언트에서 객체 컬렉션과 개별 객체를 똑같이 다룰 수 있도록 해준다.

- State

상태를 기반으로 한 행동을 캡슐화한 다음 위임을 통해서 필요한 행동을 선택한다.

- Proxy :

객체를 감싸서 그 객체에 대한 접근을 제어한다.



* 디자인 패턴 분류하기

1) 생성 패턴(Creational pattern) :

객체 인스턴스 생성을 위한 패턴으로, 클라이언트와 그 클라이언트에서 생성해야 할

객체 인스턴스 사이의 연결을 끊어주는 패턴이다.

- Singleton, Factory Method, Abstract Factory


2) 행동 패턴(Behavioral pattern) :

클래스와 객체들이 상호작용하는 방법 및 역할을 분담하는 방법과 관련된 패턴이다.

- Observer, Command, Iterator, State, Strategy, Template Method


3) 구조 패턴(Structural pattern) :

클래스 및 객체들을 구성을 통해서 더 큰 구조로 만들 수 있게 해주는 것과 관련된 패턴이다.

- Adapter, Composite, Decorator, Facade, Proxy



* GoF (Gang of Four)

- 가장 훌륭한 패턴 카탈로그이자 디자인 패턴의 정석인

'Design Patterns : Element of Reusable Object-Oriented Software'를 

(한글판 이름은 'GoF 의 디자인 패턴 : Design Patterns'기술한 4인방 (Gang of Four) 을 가르키는 말이다.


- 에릭 감마(Erich Gamma), 리차드 헬름(Richard Helm), 랠프 존슨(Ralph Johnson),

존 블라사이즈(John Vlissides) 를 가르켜 GoF 라고 한다. 처음으로 패턴 카탈로그를 만드신 분들이다.


by kelicia 2014. 6. 5. 00:12


* Compound Pattern :

일반적으로 자주 생길 수 있는 문제를 해결하기 위한 용도로 

2개 이상의 패턴을 결합해서 사용하는 것을 뜻한다.



패턴 몇 개를 결합해서 쓴다고 해서 무조건 컴파운드 패턴이 되는 것은 아니다.

위의 정의에도 이미 언급했듯이 무엇을 위한 용도인지 확실히 이해하자.


진정한 컴파운드 패턴이라 할 수 있는 'MVC' 에 대해 알아보고,

이 MVC 를 웹 환경에 맞게 변형시킨 '모델 2 (Model 2)' 까지 살펴보겠다.


이 책을 공부하기 전까지 MVC 가 컴파운드 패턴인지도 몰랐다.

예전에 MVC 관련 포스팅을 한 적이 있지만 내가 무지했다는 기분을 떨쳐낼 수 없다ㅠ_ㅠ



1. MVC


MVC 는 잘 알다시피 Model - View - Controller 의 약자이다.


Model :

모든 데이터, 상태 및 어플리케이션 로직이 들어있다. View 와 Controller 에서 Model 의 상태를 조작하거나

가져오기 위한 인터페이스를 제공한다. 또한 Model 에서 자신의 상태 변화에 대해서 Observer 들한테 연락을

해주긴 하지만, 기본적으로 Model 은 View 및 Controller 에게 별 관심이 없다.

View :

Model 을 표현하는 방법을 제공한다. 일반적으로 화면에 표시하기 위해 필요한 상태 및 데이터는

Model 에서 직접 가져온다.

Controller :

사용자로부터 입력을 받아서 그것이 Model 에게 어떤 의미가 있는지 파악한다.



이 MVC 에는 어떤 패턴들이 결합되어 있는 형태인지 알아보자.


이미 앞 장에서 살펴본 패턴들이 눈에 띄는데 하나씩 살펴보겠다.



Model 이 Observable 에 해당하고, Model 의 상태가 바뀔 때마다 

Model 로부터 정보를 전달받고 싶은 녀석들이 Observer 에 해당한다.

이를 이용하면 Model 을 View 와 Controller 로부터 완전히 독립시킬 수 있다.


이 패턴에 대해 잘 모른다면 '2장 Observer 패턴'을 참고하자.



Controller 는 View 의 행동(Behavior)에 해당하며, 

View 가 다른 행동을 원한다면 간단하게 다른 Controller 로 교환하기만 하면 된다.


즉, Controller 가 전략을 제공하고 View 에서는 어플리케이션의 겉모습에만 신경쓰고

인터페이스의 행동에 대한 결정은 모두 Controller 에게 맡긴다.

이는 View 를 Model 로부터 분리시키는 데에 도움이 된다.


자세한 'Strategy 패턴'은 1장을 참고하자.



View 안에서는 내부적으로 Composite 패턴을 써서 윈도우, 패널, 버튼 같은 다양한 구성요소를 관리한다.

각 디스플레이 항목은 복합 객체(윈도우 등) 또는 잎(버튼)이 될 수 있다.


Controller 가 View 에게 화면을 갱신해달라고 요청할 때, 최상위 View 구성요소한테만

화면을 갱신하라고 요청하면 된다. 나머지는 Composite 패턴에 의해 자동으로 처리된다.


이해가 잘 안 된다면 'Composite 패턴'은 9장을 참고하자.



책에 예제 코드가 있긴하지만 포스팅하기엔 양이 많아 생략한다ㅡ_ㅡ

MVC 가 왜 진정한 Compound 패턴인지만 이해하기로 하자.




2. Model 2


MVC 를 브라우저/서버 모델에 맞게 변형시켜서 사용하고는 하는데,

가장 많이 쓰이는 방법이 '모델 2 (Model 2)' 이다.


어떤 식으로 돌아가는 지 살펴보자.


① HTTP 요청

보통 사용자 ID, 비밀번호와 같은 Form 데이터가 함께 서블릿으로 전달된다.

서블릿에서는 이러한 데이터를 받아 Parsing 한다.


② Servlet 이 Controller 역할을 한다.

사용자 요청을 처리하고 대부분의 경우에 모델(보통 DB)에 어떤 요청을 하게 된다.

요청을 처리한 결과는 일반적으로 자바빈(JavaBean) 형태로 포장된다.


③ Controller 는 컨트롤을 View 에게 넘긴다.

View 는 JSP 에 의해 표현된다. JSP 에서는 (④ 자바빈을 통해 얻은) 모델의 View 를

나타내는 페이지만 만들어주면 된다. 물론 다음 단계를 위해 몇 가지 제어해야 할 일이 있을 수도 있다.


⑤ View 에서 HTTP 를 통해 웹 브라우저에게 페이지를 전달한다.



- Model 2 는 단지 깔끔한 디자인에 불과한 것이 아니다 -

디자인적인 면에서 Model, View, Controller 를 분리시켜줄 뿐만이 아니라

제작 책임까지도 분리시켜줄 수 있다.

Model 2 가 등장하면서 개발자들은 Servlet 에만 전념하면 되고,

웹 제작자들은 간단한 Model 2 스타일의 JSP 만 다루면 되는 환경이 조성되었다.

그래서 웹 제작자들은 HTML 과 간단한 JavaBeans 만 건드리면 된다.



* Design Patterns 과 Model 2

- 모델 2 는 MVC 를 웹에 맞게 적응시킨 것이다. 

비록 모델 2 가 MVC 와 똑같이 생긴 것은 아니나 여전히 MVC 의 핵심 요소들을 포함하고 있다.


Observer 패턴

View 는 더 이상 Model 의 Observer 라 할 수 없다. 

Model 한테 등록해서 Model 의 상태가 바뀌었다는 연락을 받는다거나 하지 않기 때문이다. 

하지만 Controller 가 빈(Bean)을 건네주는 덕분에 Model 의 상태가 바뀐 것을 간접적으로 연락을 받는다.

View 에서는 브라우저로 HTTP 응답을 할 때만 Model 의 상태 정보가 필요하기 때문에

HTTP 응답을 하지 않는 상태에서 Model 로부터 연락을 받아봤자 할 일이 없다.


Strategy 패턴

고전적인 MVC 처럼 View 객체에 Controller 객체에 대한 레퍼런스가 들어가는 방식으로 구현되지 않는다.

하지만 여전히 (Servlet 으로 만든) Controller 가 View 의 행동을 구현하는 전략 객체로 쓰이고,

다른 행동을 원할 때 다른 Controller 로 바꿀 수 있다.


Composite 패턴

HTML 코드를 통해서 웹 브라우저에 렌더링된다는 차이점은 있지만 

결국은 여기에서 쓰이는 View 도 중첩된 그래픽 구성요소로 이루어진다.




* 객체지향의 기초 :

1) 추상화    2) 캡슐화    3) 다형성    4) 상속



* 객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다.

2) 상속보다는 구성을 활용한다.

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

4) 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

5) 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다(OCP).

6) 추상화된 것에 의존해라. 구상 클래스에 의존해서는 안 된다(의존성 뒤집기 법칙).

7) 친한 친구들하고만 이야기한다(최소 지식 원칙).

8) 먼저 연락하지 마세요. 저희가 연락 드리겠습니다(헐리우드 원칙).

9) 어떤 클래스가 바뀌게 되는 이유는 한 가지 뿐이어야만 한다(단일 역할 원칙).



* 이 장에서의 정리 :

- MVC 패턴은 Observer, Strategy, Composite 패턴으로 이루어진 Compound 패턴이다.

- Model 에서는 Observer 패턴을 이용해 옵저버들에 대한 의존성은 없애고, 옵저버들에게

  자신의 상태가 변경되었음을 알릴 수 있다.

- View 에서는 Composite 패턴을 이용해 사용자 인터페이스를 구현한다. 

  보통 패널이나 프레임, 버튼과 같은 중첩된 구성요소로 구성된다.

- Controller 는 Strategy 패턴을 이용해 View 에 대한 전략 객체가 된다.

  View 에서는 Controller 를 바꾸기만 하면 다른 행동을 활용할 수 있다.

- MVC 는 이 3 가지 패턴을 통해 서로 연결된다. 

  느슨하게 결합되기 때문에 깔끔하면서도 유연하게 구현할 수 있다.

- 새로운 Model 을 기존의 View 및 Controller 하고 연결해서 쓸 때는 Adapter 패턴을 활용하자.

- Model 2 는 MVC 를 웹 어플리케이션에 맞게 적용한 디자인이라 할 수 있다.



(이미지 출처 :

http://www.07net01.com/linux/Head_First_Design_Patterns_Notes_286645_1367843500.html

http://stackoverflow.com/questions/5217611/the-mvc-pattern-and-swing)


by kelicia 2014. 6. 3. 17:27


이번에는 프록시 패턴의 활용 예제를 살펴보겠다.

앞서 포스팅한 내용 중에 가상 프록시(Virtual Proxy), 보호 프록시(Protection Proxy) 에 대해 

간단히 언급했는데 하나씩 자세히 살펴보겠다.


그 전에 Proxy 패턴의 클래스 다이어그램은 중요하니 다시 복습하자.


앞에서 포스팅했던 다이어그램 그림과는 조금 차이가 있지만 결국 같은 말이다.



1. 가상 프록시 (Virtual Proxy)

- 생성하기 힘든 자원에 대한 접근을 제어할 수 있다.


여기서 생성하기 힘든 자원이란 예를 들어 Image 같이 객체 생성 시간이 긴 녀석들을 의미한다.

예제 코드로는 이미지 뷰어를 만들어 볼 것이다. 


가상 프록시가 백그라운드에서 서버로부터 이미지를 불러들이는 동안 

화면에서는 로딩중이라는 메시지를 보여주게 할 것이다.


설계는 다음과 같다.


Icon 인터페이스와 ImageIcon 클래스는 javax.swing 을 이용할 것이다.


그러면 ImageProxy 코드를 보자. 앞서 먼저 본 원격 프록시(Remote Proxy)와 헷갈리지 말자.

class ImageProxy implements Icon
{
    ImageIcon imageIcon;    // RealSubject
    URL imageURL;
    Thread retrievalThread;
    boolean retrieving = false;

    public ImageProxy(URL url) { imageURL = url; }

    public int getIconWidth() {
        if ( imageIcon != null ) { return imageIcon.getIconWidth(); }
            else { return 800; }
    }

    public int getIconHeight() { // getIconWidth() 와 비슷 }

    public void paintIcon(final Component c, Graphics g, int x, int y) {
        if ( imageIcon != null ) {
            imageIcon.paintIcon(c, g, x, y);
        } else {
            g.drawString("Loading Image, please wait...", x+300, y+200);
            if ( !retrieving ) {
                retrieving = true;
                retrievalThread = new Thread(new Runnable() {
                    public void run() {
                        try {
                            imageIcon = new ImageIcon(imageURL, "Image");
                            c.repaint();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                retrievalThread.start();
            }
        }
    }
}

메소드 별로 if, else 문으로 나뉘는데 10장에서 본 State 패턴을 이용해

좀 더 깔끔한 코드를 만들 수 있다.


다음은 ImageProxy 를 감싸줄 Component 와 테스트 코드이다.

class ImageComponent extends JComponent 
{
    private Icon icon;

    // ImageComponent(Icon icon), setIcon() 생략

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 기타 인자값 생략
        icon.paintIcon(this, g, x, y);
    }
}
public class ImageProxyTestDrive 
{
    ImageComponent imageComponent;
    public static void main(String[] args) throws Exception {
        ImageProxyTestDrive testDrive = new ImageProxyTestDrive();
    }

    public ImageProxyTestDrive() throws Exception {
        // 프레임 및 기타 설정 코드 생략

        Icon icon = new ImageProxy(initialURL);
        imageComponent = new ImageComponent(icon);
        frame.getContextPane().add(imageComponent);
    }
}


Q. 전에 가져왔던 이미지를 캐시에 저장해 두는 ImageProxy 와 비슷한 객체를 구현할 수 있지 않나?

- 가상 프록시의 변종 가운데 하나인 캐싱 프록시(Caching Proxy) 라는 것이 있다.

캐싱 프록시는 기존에 생성했던 객체들을 캐시에 저장해 뒀다가 재요청이 들어왔을 때,

상황에 따라 캐시에 저장해두었던 객체를 리턴할 수 있다.


Q. 원격 프록시와 가상 프록시가 같은 패턴에 속하는 게 맞나?

- 프록시 패턴는 다양하게 활용된다. 활용된 그 모든 녀석들의 공통점은

'클라이언트에서 실제 객체의 메소드를 호출하면, 그 호출을 중간에 가로챈다'는 점이다.


이렇게 간접적으로 작업을 처리하면 

요청 내역을 원격 시스템에 있는 객체에 전달할 수도 있고,

생성하는 데 많은 비용이 드는 객체를 대변해줄 수도 있고,

클라이언트 별로 호출할 수 있는 메소드를 제한하는 보디가드 역할을 하는 것도 가능하다.


이 장에서 배우는 프록시 패턴은 시작에 불과하다.

이제 보호 프록시에 대해 알아보고 그 외에 활용되는 방법들을 더 알아볼 것이다.



2. 보호 프록시 (Protection Proxy)

- 접근 권한이 필요한 자원에 대한 접근을 제어할 수 있다.


- Java 에는 java.lang.reflect 패키지에 프록시 기능이 내장되어 있다. 이 패키지를 이용하면

즉석에서 1개 이상의 인터페이스를 구현하고, 메소드 호출을 지정해 준 클래스에 전달할 수 있는

프록시 클래스를 만들 수 있다. 실제 프록시 클래스는 실행중에 생성되기 때문에 

이러한 자바 기술을 '동적 프록시(dynamic proxy)' 라고 부른다.


이번에는 이 동적 프록시를 활용해서 보호 프록시를 만들어 볼 것이다.

보호 프록시를 만들기 전에 동적 프록시가 어떤 식으로 돌아가는지 클래스 다이어그램을 보자.


Proxy 패턴의 공식적인 다이어그램과는 조금 차이가 있다.


동적 프록시의 경우, Proxy 클래스를 Java 에서 만들어 주기 때문에 Proxy 클래스에게

무슨 일을 할지 알려주기 위한 방법이 필요하다. 

하지만 Proxy 클래스를 직접 만들지 않기 때문에 필요한 코드를 InvocationHandler 에 집어넣어 

InvocationHandler 가 Proxy 에 대해서 호출되는 모든 메소드에 대해 응답하는 역할을 맡도록 한다.



예제로는 결혼 정보 서비스를 만들 것이다.

고객들이 서로 상대방에 대한 선호도 점수를 줄 수 있는 기능을 추가할 것이고,

이 서비스는 어떤 사람에 대한 정보를 가져오거나 설정할 수 있게끔 'PersonBean' 을 중심으로 삼는다.


본인이라면 당연히 내 정보를 설정할 수 있고, 타인이 내 정보를 설정하게 하는 건 불가해야 한다.

본인이라면 내 선호도 점수를 매기는 건 불가하고, 타인이라면 내 선호도 점수를 매길 수 있다.

여기서 접근 권한에 따른 제어가 필요하다는 것을 느낄 것이다.


먼저 Subject 인 'PersonBean' 과 RealSubject 인 'PersonBeanImpl' 부터 코드를 살펴보자.

public interface PersonBean 
{
    // String getName(), getGender(), getInterests() 선언
    int getHotOrNotRating();    // 선호도의 평균 점수를 리턴

    // void setName(), setGender(), setInterests() 선언
    void setHotOrNotRating(int rating);
}
public class PersonBeanImpl implements PersonBean 
{
    String name, gender, interests;
    int rating, ratingCount;

    // getName(), getGender(), getInterests(), getHotOrNotRating() 구현

    // setName(), setGender(), setInterests(), setHotOrNotRating() 구현
}


< PersonBean 용 동적 프록시 만들기 3 단계 >

① InvocationHandler 를 만든다.

- 프록시의 메소드가 호출되었을 때 실제 할 일을 지정해 주는 핸들러만 만들면 된다.

② 동적 프록시를 생성하는 코드를 작성한다.

- 프록시 클래스를 생성하고 그 인스턴스를 만들기 위한 코드를 말한다.

③ PersonBean 객체를 적절한 프록시로 감싼다.

- 적절함의 척도는 본인(Owner)이냐 본인이 아니냐(Non-Owner) 이다.

PersonBean 객체를 사용하고자 하는 객체에 따라 적절한 프록시를 생성해주자.



① InvocationHandler 를 만든다.

- 호출핸들러는 본인을 위한 핸들러, 타인을 위한 핸들러 이렇게 2개를 만든다.

- 이 핸들러에는 invoke() 하나 뿐이다. 코드 보기 전에 어떻게 돌아가는지 살펴보자.

1) proxy.setHotOrNotRating(10); 라고 프록시의 메소드가 호출되었다고 가정해보면,

2) 프록시는 핸들러의 invoke(Object proxy, Method method, Object[] args) 를 호출한다.

그러면 invoke() 에서는 1) 에서 호출된 메소드를 어떻게 처리할 것인지 구현하면 된다.

import java.lang.reflect.*;

public class OwnerInvocationHandler implements InvocationHandler 
{
    PersonBean person;

    public OwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
        try {
            if (method.getName().startsWith("get")) {
                return method.invoke(person, args);
            } else if (method.getName().equals("setHotOrNotRating")) {
                throw new IllegalAccessException();
            } else if (method.getName().startsWith("set")) {
                return method.invoke(person, args);
            }
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

위 코드는 보다시피 본인을 위한 핸들러이고, 타인을 위한 핸들러 NonOwnerInvocationHandler 는

try/catch 구문 안의 조건문만 조금 바꾸면 되니 생략하겠다.


② 동적 프록시를 생성하는 코드를 작성한다.

PersonBean getOwnerProxy(PersonBean person) 
{
    // 이 메소드에서는 RealSubject 를 인자로 받아오고, Proxy 를 리턴한다.
    // 'Proxy 의 인터페이스 = RealSubject 의 인터페이스' 이므로 리턴 타입은 PersonBean 이다.

    return (PersonBean) Proxy.newProxyInstance( 
                person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new OwnerInvocationHandler(person));
}

이렇게 newProxyInstance 의 마지막 인자 값만 바꿔서 getNonOwnerProxy() 메소드도 작성하면 된다.


③ PersonBean 객체를 적절한 프록시로 감싼다.

public class MatchMakingTestDrive 
{
    // 인스턴스 변수 선언

    public static void main(String[] args) {
        MatchMakingTestDrive test = new MatchMakingTestDrive();
        test.drive();
    }

    public MatchMakingTestDrive() {
        initializeDatabase();
    }

    public void drive() {
        PersonBean joe = getPersonFromDatabase("Joe JavaBean");

        // Owner 인 경우, 본인용 프록시 생성
        PersonBean ownerProxy = getOwnerProxy(joe);

        // ownerProxy.setHotOrNotRating() 대한 예외처리 필요

        // Non-Owner 인 경우, 타인용 프록시 생성
        PersonBean nonOwnerProxy = getNonOwnerProxy(joe);

        // nonOwnerProxy.setHotOrNotRating() 를 제외한 setter 메소드에 대한 예외처리 필요
    }

    // getOwnerProxy(), getNonOwnerProxy() 같은 메소드
}

위 코드는 테스트 코드이니 경우에 따라 어떤 프록시를 생성할 것인지만 살펴보면 된다.



Q. 동적 프록시에서 어느 부분이 '동적'이라는 거지?

- 프록시가 동적이라고 한 이유는 클래스가 실행중에 생성되기 때문이다.

실제로 코드가 실행되기 전까지는 프록시 클래스 자체가 없다. 

전달받은 인터페이스를 바탕으로 즉석에서 클래스가 생성된다. ->  단계 코드 참고


Q. InvocationHandler 는 특이한 프록시인건가?

InvocationHandler 자체는 프록시가 아니다. 

메소드 호출을 처리하기 위해 프록시에서 활용하는 클래스일 뿐이다.


Q. 어떤 클래스가 Proxy 클래스인지 알아낼 방법이 있나?

- Proxy 클래스에는 isProxyClass() 라는 정적 메소드가 있다. 

동적 프록시 클래스에 대해서 이 메소드를 호출하면 true 라고 리턴될 것이다.


Q. 왜 스켈레톤(Skeleton) 을 사용해야 되나?

- Java 1.2 부터는 RMI 런타임에서 클라이언트 호출을 reflection 을 이용해 직접

원격 서비스로 넘길 수 있게 되어서 사실 스켈레톤은 안 써도 된다.

하지만 이 장에서는 메커니즘을 이해하는 데 도움이 되기 때문에 스켈레톤을 언급했다.


Q. Java 5 부터는 스터브(Stub) 도 더 이상 만들 필요가 없다고 하는데 정말인가?

- 그렇다. Java 5 부터는 RMI 와 동적 프록시가 결합되어서 스터브 마저도 동적 프록시를 통해

동적으로 생성된다. 따라서 'rmic' 을 전혀 쓰지 않아도 된다. 원격 객체의 메소드를

호출하고 그 결과를 리턴받는 작업이 전부 자동으로 처리되기 때문이다.



3. 기타 프록시 패턴의 활용

- 방화벽 프록시

일련의 네트워크 자원에 대한 접근을 제어함으로써 주 객체를 '나쁜' 클라이언트들로부터 보호해준다. 

기업용 방화벽 시스템에서 자주 쓰인다.

- 스마트 레퍼런스 프록시 (Smart Reference Proxy) :

주 객체가 참조될 때마다 추가 행동을 제공한다. ex) 객체에 대한 레퍼런스 갯수를 카운트.

- 캐싱 프록시 (Caching Proxy) :

비용이 많이 드는 작업의 결과를 임시로 저장해준다. 

여러 클라이언트에서 결과를 공유하게 해 줌으로써 계산 시간 or 네트워크 지연을 줄여주는 효과가 있다. 

웹 서버 프록시 또는 컨텐츠 관리 및 퍼블리싱 시스템에서 자주 쓰인다.

- 동기화 프록시 (Synchronization Proxy) :

여러 스레드에서 주 객체에 접근하는 경우에 안전하게 작업을 처리할 수 있게 해준다.

분산 환경에서 일련의 객체에 대한 동기화 된 접근을 제어해주는 자바 스페이스에서 쓰인다.

- 복잡도 숨김 프록시 (Complexity Hiding Proxy) :

복잡한 클래스들의 집합에 대한 접근을 제어하고, 복잡도를 숨겨준다. 

'퍼사드 프록시(Facade Proxy)' 라고 부르기도 한다.

- 지연 복사 프록시 (Copy-On-Write Proxy) :

클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사를 제어한다.

'변형된 가상 프록시'라고 할 수 있다.

Java 5 의 CopyOnWriteArrayList 에서 쓰인다.



* 객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다.

2) 상속보다는 구성을 활용한다.

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

4) 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

5) 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다(OCP).

6) 추상화된 것에 의존해라. 구상 클래스에 의존해서는 안 된다(의존성 뒤집기 법칙).

7) 친한 친구들하고만 이야기한다(최소 지식 원칙).

8) 먼저 연락하지 마세요. 저희가 연락 드리겠습니다(헐리우드 원칙).

9) 어떤 클래스가 바뀌게 되는 이유는 한 가지 뿐이어야만 한다(단일 역할 원칙).



* 이 장에서의 정리 :

- Proxy Pattern 을 이용하면 어떤 객체에 대한 대변인을 내세워서 클라이언트의 접근을 제어할 수 있다.

- 원격 프록시는 클라이언트와 원격 객체 사이의 데이터 전달을 관리해준다.

- 가상 프록시는 인스턴스를 생성하는 데 비용이 많이 드는 객체에 대한 접근을 제어한다.

- 보호 프록시는 호출한 쪽의 권한에 따라 객체에 있는 메소드에 대한 접근을 제어한다.

- Decorator 패턴에서는 객체에 행동을 추가하지만, Proxy 패턴에서는 접근을 제어한다.

- 다른 Wrapper 를 쓸 때와 마찬가지로 프록시를 쓰면 디자인에 포함되는 클래스와 객체의 수가 늘어난다.



※ Proxy 의 사전적 의미 : 

1) 대리[위임](권)    2) 대리인    3) 대용물



# Pattern 짧게 복습 #

- Decorator : 다른 객체를 감싸서 새로운 행동을 추가해준다.

- Facade : 여러 객체를 감싸서 인터페이스를 단순화 시킨다.

- Adapter : 다른 객체를 감싸서 다른 인터페이스를 제공한다.

- Proxy : 다른 객체를 감싸서 접근을 제어한다.



이번 포스팅도 길었다, 휴 힘들어ㅡ_ㅡ


(이미지 출처 : http://www.jamessugrue.ie/softwaredev/design-patterns-uncovered-the-proxy-pattern)


by kelicia 2014. 6. 2. 00:04