OOP거듭나기

구조패턴 - Decorator패턴

손병환 2007. 7. 26. 02:08

거의 1년만에 다시 글을 올린다. 프로젝트 관리 때문에 잠시 디자인패턴에서 눈을 돌렸지만 최근에 하는 프로젝트가 한국, 중국, 일본 3국이 동시에 하는 프로젝트이기에 각 모듈에 디자인 패턴을 적용해야할 필요성에 정리 차원에서 부활시켰다.

 

이전에 적었던 4개의 글을 다시금 읽어보니 새삼 그 때는 마치 미친듯이 디자인 패턴을 파고들었고 가장 알기 쉬운 패턴들만을 골라서 했었는데 이번 프로젝트는 3국이 동시에 하는 프로젝트이면서 개발은 중국과 한국, 설계는 일본과 한국에서 맡아서 가기 때문에 중국에서의 개발에 일관성 및 차후에 변화할 업무 흐름에 좀 더 유연하게 대처하기 위해서 디자인 패턴에 다시금 눈을 돌렸다.

 

그에 따른 첫번째로 데코레이터 즉 한국말로 번역하면 치장이다. 치장이란 치장할 대상이 필요하다. 대상이 없다면 치장이 될리가 없다. 즉, 기존에 잘 사용하고 있던 코드에 영향이 덜가면서 새로운 기능을 추가하고자 할 때 사용할 수 있을 것 같다. 이 부분이 가장 잘 드러나 있는 예제가 바로 .NET에서 Stream클래스이다.

 

아래의 그림에 대해서 간단히 설명을 하자면,

Component클래스는 추상클래스로서 Component클래스를 구현한 ConcreteComponent클래스가 다수 존재할 것이다. 이렇게 존재하는 ConcreteComponent 클래스는 대부분이 비슷비슷한 목적과  속성들을 가지고서 사용될 것이다. 이러한 상황에서 기존의 각각 존재하는 ConcreteComponent 클래스에 기존에 존재하는 메소드의 기능은 그대로 가지면서 기능이나 속성등을 추가해서 사용하고자 할 경우 이미 다수의 클라이언트 어플리케이션이 이들 ConcreteComponent클래스를 이용해서 사용하고 있다면 ConcreteComponent클래스에 손을 대기가 무척 난감하리라는 것은 불을 보듯 뻔하다. 즉, 기존에 존재하는 클래스에 치장을 하는 것은 그다지 쉬운 일이 아니라는 것이다.

이 때 Decorator라는 또다른 클래스를 하나 두어서 해결이 가능하다. 아래의 그림을 보면 ConcreteComponent와 Decorator와 그다지 다른 점이 없는 것처럼 보이지만 중요한 포인트는 Decorator클래스가 추상클래스라는 점과 이 클래스가 Component의 인스턴스를 가진다는 것이다. 하지만 추상클래스는 인스턴스 생성을 할 수 없다는 것은 다들 잘 알고 있다. 이를 해결하는 방법은 이렇게 인스턴스를 가지고 있는 Decorator에 ConcreteComponent의 인스턴스를 할당해 주는 속성이나 생성자에서 할당을 해 주는 방식으로 해결이 가능하다.

   ※ 이러한 형태가 마치 Stream클래스를 사용하는 것과 거의 흡사하다.

또한 Decorator 추상 클래스에 Component 추상 클래스의 Operation메소드를 호출해 주는 부분을 추가해야 한다. 왜 추가하냐면 기존의 ConcreteComponent클래스의 내부에 있는 Operation 메소드를 실행시키기 위해서다. 즉, 치장을 하기 위해서는 기존에 있는 클래스의 기능을 100% 사용하면서 조금의 기능을 추가하는 것이 목적이기 때문이다.

 

이러한 Decorator 추상 클래스를 상속받는 ConcreteDecorator클래스들은 기존의 클래스들이 가지는 기능들, 즉 ConcreteComponent의 Operation 메소드에 치장을 하는 것이 목적이기 때문에 ConcreteComponent의 Operation메소드를 실행시킬 필요가 있다. 이 때 필요한 것이 위해서 설명했던 Decorator 추상 클래스의 Operation메소드다. 즉, base.Operation() 이라는 한줄의 코드가 필요하다.

 

중요한 것은 ConcreteComponent 클래스를 깡그리 무시하는 것이 아니라 치장이 목적이라는 점을 잊지 말아야 할 것이다. 치장하는 녀석, 즉 Decorator...

 

 

아래에 소스를 퍼 둔다.

 

// Decorator pattern -- Structural example

using System;

namespace DoFactory.GangOfFour.Decorator.Structural
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      // Create ConcreteComponent and two Decorators
      ConcreteComponent c = new ConcreteComponent();
      ConcreteDecoratorA d1 = new ConcreteDecoratorA();
      ConcreteDecoratorB d2 = new ConcreteDecoratorB();

      // Link decorators
      d1.SetComponent(c);
      d2.SetComponent(d1);

      d2.Operation();

      // Wait for user
      Console.Read();
    }
  }

  // "Component"

  abstract class Component
  {
    public abstract void Operation();
  }

  // "ConcreteComponent"

  class ConcreteComponent : Component
  {
    public override void Operation()
    {
      Console.WriteLine("ConcreteComponent.Operation()");
    }
  }

  // "Decorator"

  abstract class Decorator : Component
  {
    protected Component component;

    public void SetComponent(Component component)
    {
      this.component = component;
    }

    public override void Operation()
    {
      if (component != null)
      {
        component.Operation();
      }
    }
  }

  // "ConcreteDecoratorA"

  class ConcreteDecoratorA : Decorator
  {
    private string addedState;

    public override void Operation()
    {
      base.Operation();
      addedState = "New State";
      Console.WriteLine("ConcreteDecoratorA.Operation()");
    }
  }

  // "ConcreteDecoratorB"

  class ConcreteDecoratorB : Decorator
  {
    public override void Operation()
    {
      base.Operation();
      AddedBehavior();
      Console.WriteLine("ConcreteDecoratorB.Operation()");
    }

    void AddedBehavior()
    {
    }
  }
}

출처 : http://www.dofactory.com/Patterns/PatternDecorator.aspx

'OOP거듭나기' 카테고리의 다른 글

인터페이스는 왜, 언제 사용하는가?  (0) 2008.08.16
구조패턴 - Facade 패턴  (0) 2006.09.26
구조패턴 - 컴포지트 패턴  (0) 2006.09.26
구조패턴 - Bridge 패턴  (0) 2006.09.25
구조패턴 - Adapter 패턴  (0) 2006.09.23