안드로이드에서 Decorator 패턴

개발 이야기 2012. 12. 8. 21:08

100% 데코레이터 패턴을 적용한 사례가 아닐 수 있으니 이점 유의해서 읽어주시기 바랍니다




데코레이터 패턴은 핵심기능이 되는 핵심 클래스와 핵심 기능을 추가적으로 꾸며주는 부가기능 클래스으로 나뉩니다.


이때 핵심 클래스는 부가 클래스에 의해 래핑 되는 방식으로 구현됩니다.

무슨 말인지 모르겠다고요?


코드로 보면 아래와 같다고 보시면 됩니다.


IShape mShape = new ShapeColor(new ShapeStyle(new RectShape()); 


위의 코드는 사각형을 그리는 RectShape 라는 클래스를 사각형의 색상과 스타일을 정의하는 ShapeColorShapeStyle 클래스로 래핑을 한 것이라고 보시면 됩니다.


그렇다면 왜 저렇게 정의할까요 부터 얘기를 꺼내봐야겠죠?

사실 핵심기능과 부가기능을 정의하는 클래스를 표현하는 방법은 다양합니다.

팩토리 패턴을 사용해도 되고 빌더패턴을 사용해도 됩니다. 하지만 굳이 저렇게 부가기능과 핵심기능을 분리해서 정의하는 이유가 뭘까요?


그것은 부가기능이 많아질수록 특정 클래스가 담당하는 기능이 많아질수록 클래스내에서 정의해야하는 기능들이 매우 커지게 되고 더 큰 문제는 이를 상속받는 클래스가 생겼을 때 확장성과 유지보수성에 심각한 문제가 생길 수 있다는것입니다.


예를 한번 들어보겠습니다.


도형을 그리는 로직이 있는 클래스를 만들어봅시다.

그려야할 도형은 삼각형과 사각형이라고 정의를 할 것입니다.


이 도형들은 색상, 선의 두께, 내부 채움 여부등의 정의해야합니다.


그렇다면 대개는 작업을 아래와 같이 합니다.


삼각형 m삼각형 = new 삼각형();

m삼각형.setStokeWidth(10);

m삼각형.setColor(Color.BLACK);

m삼각형.setStyle(Style.FILL);


위와 같이 정의할 것입니다.

(물론 사각형도 위와 같이 정의를 해야겠죠?)


하지만 정의해야할 값이 10여개로 늘어나게 될 것이고

도형도 10여개로 늘어나게 되었을 때

여러분들은 모든 도형에 해당 함수를 정의해야 할 것입니다.



이를 극복하고자 나온 것이 핵심 클래스 / 부가 클래스로 구분하여 부가기능을 동적으로 추가/제거할 수 있도록 제어하고자 하는 데코레이터 패턴이 생기게 되는것입니다.


이제 위의 사례를 한번 데코레이터 패턴으로 옮겨보도록 하겠습니다.


우선 위의 사례에서 기능을 구분해봐야겠죠?


우선 도형을 그리는 것이 핵심 기능이고 속성을 정의하는 것이 부가 기능으로 정의해서

기능을 다음과 같이 단계적으로 정의할 수 있습니다.

 

핵심기능

부가기능 

 1. 기능 구분

 도형을 그린다 

 도형의 속성을 정의한다 

 2. 기능 상세 정의

삼각형을 그린다.

사각형을 그린다.

도형의 색상을 정의한다.
도형의 외선 두께를 정의한다.
도형의 내부를 그릴지 정의한다 


그럼 위의 데코레이터 패턴이 코드상으로 구현이 되는지 보기 앞서서 클래스 다이어그램으로 구조를 파악해보도록 하겠습니다.












위의 구조를 보면 IShape 는 getPaint() 와 draw(Canvas) 를 가진 인터페이스 클래스입니다.

그리고 IShape 를 상속 받는 ShapeDecorator(부가기능)AbstractShape(핵심기능) 를 보실 수 있습니다.

AbstractShape(핵심기능) : 상속 받은 TriagleShape 와 RectShape 는 삼각형과 사각형을 그리는 클래스입니다.

ShapeDecotrator(부가기능) : 상속받은 ShapeWidth, ShapeColor, ShapeStyle 는 속성을 정의하는 클래스입니다.

그럼 간단하게 4개의 클래스를 살펴보도록 하겠습니다.

(IShape 클래스는 인터페이스로 위의 2개 함수만 정의를 해놨기 때문에 더이상 언급하지 않도록 하겠습니다.)


ShapeDecorator.java(부가기능)

public class ShapeDecorator implements IShape {

    private IShape shape;

    public ShapeDecorator(IShape shape) {

        this.shape = shape;

    }

    @Override

    public Paint getPaint() {

        return shape.getPaint();

    }

    @Override

    public void draw(Canvas mCanvas) {

        getPaint();

        shape.draw(mCanvas);

    }


ShapeColor.java(부가기능)

 

public class ShapeColor extends ShapeDecorator {

    private int color;

    public ShapeColor(IShape shape, int color) {

        super(shape);

        this.color = color;      

    }

    @Override

    public Paint getPaint() {

        Paint mPaint = super.getPaint();

        mPaint.setColor(color);

        return mPaint;

    }

}


AbstractShape.java(핵심기능)

public abstract class AbstractShape implements IShape {

    private Paint mPaint;

    public AbstractShape() {

        mPaint = new Paint();

    }    

    @Override

    public Paint getPaint() {

        return mPaint;

    }


RectShape.java(핵심기능)

public class RectShape extends AbstractShape {

    private int left;

    private int top;

    private int right;

    private int bottom;

    public RectShape(int left, int top, int right, int bottom) {

        super();

        this.left = left;

        this.top = top;

        this.right = right;

        this.bottom = bottom;

    }

    @Override

    public void draw(Canvas mCanvas) {

        mCanvas.drawRect(left, top, right, bottom, getPaint());

    }


위의 소스를 잘 보시면 RectShape(핵심기능) 는 사각형을 그린다는 것만 정의되어 있습니다.

그리고 ShapeColor(부가기능) 에서는 도형의 색상을 정의합니다.


이렇게 정의된 도형은 아래와 같이 씌여집니다.

 IShape mShape = new ShapeColor(new ShapeWidth(new ShapeStyle(new RectShape());

 mShape.draw(mCanvas);


다음은 예제의 스크린샷입니다.

     


예제 프로젝트

CanvasDecorator.zip 



코드를 통해 데코레이터 패턴을 설명하였습니다.

데코레이터 패턴을 사용하는 가장 큰 이유는 핵심기능-부가기능의 분리라고 보시면 됩니다.

하지만 데코레이터 패턴이 기능을 나누는 효율적 방법이라고 딱 집을 수도 없습니다.

그 이유는 핵심 기능과 부가기능이 나뉘게 되면 서비스를 인수인계하는 후임자 입장에서는 코드 리뷰가 매우 어려울 수도 있습니다.

심지어 기능의 정의가 정확히 구분되어 있지 않다면 어느 기능이 부가기능에 속해서 제어되고 있는지 파악하는 것도 어려울 수 있다는 말이 될 수 있습니다.

관점의 차이로 인해 받아들이는 사람이 거부감을 느낄 수도 있으니 데코레이터 패턴의 적용은 꼭 함께 개발하는 사람과 상의해서 적용하시기 바랍니다 ^^




설정

트랙백

댓글