블로그 이미지
Kanais
Researcher & Developer 퍼즐을 완성하려면 퍼즐 조각들을 하나 둘씩 맞춰나가야 한다. 인생의 퍼즐 조각들을 하나 둘씩 맞춰나가다 보면 인생이란 퍼즐도 완성되는 날이 오려나...?

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

05-05 08:09

Recent Post

Recent Comment

Recent Trackback

Archive

2015. 4. 16. 10:12 Programming/Design Pattern



참고 서적 : GOF의 디자인패턴 / 저자 :  에릭 감마, 리처드 헬름, 랄프 존슨, 존 블리시디스 / 김정아 옮김 / 출판사 : Addison Wesley

참고 : http://www.dofactory.com/Patterns/PatternBridge.aspx#_self1
http://ani2life.egloos.com/2904131

http://ljeljy.egloos.com/4935644

http://ko.wikipedia.org/wiki/%EB%B8%8C%EB%A6%AC%EC%A7%80_%ED%8C%A8%ED%84%B4

 



의도

구현에서 추상을 분리하여, 이들이 독립적으로 다양성을 가질 수 있도록 합니다.

 

다른 이름

핸들 / 구현부(Handle / Body)

 

동기

하나의 추상적 개념이 여러 가지 구현으로 구체화될 수 있을 때, 대부분은 상속을 통해서 이 문제를 해결합니다. 추상 클래스로 추상적 개념에 대한 인터페이스를 정의하고, 구체적인 서브 클래스들에서 서로 다른 방식으로 이들 인터페이스를 구현합니다. 그러나 이 방법만으로는 충분한 융통성을 얻을 수 없습니다. 상속은 구현과 추상적 개념을 영구적으로 종속시키기 때문에, 추상적 개념과 구현을 분리해서 재사용하거나 수정확장하기가 쉽지 않습니다.

 

이식성이 있는 Window를 추상적 개념으로 보고 이를 사용자 인터페이스 툴킷을 써서 구현하는 예를 생각해 봅시다. 이 Window 추상화는 X 윈도우 시스템에서 운영이 가능한 응용 프로그램에서 사용되거나, IBM의 프레젠테이션 매니저(PM)에서 운영되는 응용 프로그램에 적용될 수 있습니다. 상속을 이용한다면 우리는 Window 추상 클래스를 정의하고 이를 상속받아서 서로 다른 플랫폼에서 운영될 PMWindow, XWindow 클래스를 구현하면 됩니다.

 

상속의 문제점

-      새로운 종류의 개체마다 서로 다른 플랫폼을 지원하는데 필요한 클래스를 계속 개발해야 한다.

-      사용자 코드가 플랫폼에 종속되므로 응용 프로그램들이 서로 다른 플랫폼에 이식하기가 어렵다.

 

이런 문제를 해결하기 위해 가교 패턴을 사용할 수 있습니다. 이는 추상적 개념에 해당하는 클래스 계통과 구현에 해당하는 클래스 계통을 분리함으로써 문제를 해결합니다. 즉, 윈도우 인터페이스에 대한 계통 하나와 구현에 해당하는 계통 하나를 따로 구축합니다. 윈도우 인터페이스의 최상위 클래스로 Window를 정의하고, 이를 구현하는 클래스의 최상위 클래스로 WindowImp를 정의합니다. 물론 Window 클래스는 자신의 인터페이스를 실제로 구현하는 클래스가 어느 것인지에 대한 참조자(imp)를 정의해야 합니다. Window 클래스에 정의된 연산은 WindowImp에 정의된 연산을 이용하여 기능이 완성되는 셈입니다. WindowImp 클래스가 플랫폼에 종속적인 구현을 정의하므로 플랫폼별 서브 클래스를 가지게 되고, Window 클래스는 윈도우에 대한 새로운 개념이 만들어 질 때마다 그 개념을 추상화한 클래스로 Window의 서브 클래스를 만들고 새로운 연산을 정의합니다. 이러한 새로운 연산 역시, WindowImp 클래스를 상속받는 플랫폼에 종속적인 서브 클래스가 정의한 연산에 의해 구현될 수 있습니다.

이때, Window 클래스와 WindowImp 클래스 간의 관련성을 가리켜 가교(bridge)라고 정의합니다.

 

 

활용성

-      추상적 개념과 이에 대한 구현 사이의 지속적인 종속 관계를 피하고 싶을때, 이를테면, 런타임에 구현 방법을 선택하거나 구현 내용을 변경하고 싶을 때가 여기에 해당합니다.

-      추상적 개념과 구현 모두가 독립적으로 서브 클래싱을 통해 확장되어야 할 때. 이때, 가교 패턴은 개발자가 구현을 또 다른 추상적 개념과 연결할 수 있게 할 뿐 아니라, 각각을 독립적으로 확장 가능하게 합니다.

-      추상적 개념에 대한 구현 내용을 변경하는 것이 다른 관련 프로그램에 아무런 영향을 주지 않아야 할 때. 즉, 추상적 개념에 해당하는 클래스를 사용하는 코드들은 구현 클래스가 변경되었다고 해서 다시 컴파일되지 않아야 합니다.

-      클래스 계통에서 클래스 수가 급증하는 것을 방지하고자 할 때. 이러한 클래스는 클래스 상속 계통에서 하나의 이유로 여러 개 클래스가 갑자기 정의되어야 하는 상황이라면 객체를 두 부분으로 분리할 필요가 있음을 보여 줍니다.

-      여러 객체들에 걸쳐 구현을 공유하고자 하며(아마도 참조 카운팅 등의 방법을 써서), 또 이런 사실을 사용자 쪽에 공개하고 싶지 않을 때.

 

구조

 

 

 

참여자

l  Abstraction : 추상적 개념에 대한 인터페이스를 제공하고 객체 구현자(Implementor)에 대한 참조자를 관리합니다.

l  RefinedAbstraction : 추상적 개념에 정의된 인터페이스를 확장합니다.

l  Implementor : 구현 클래스에 대한 인터페이스를 제공합니다. 다시 말해, 실질적인 구현을 제공한 서브 클래스들에 공통적인 연산의 시그니처만을 정의합니다. 이 인터페이스는 Abstraction 클래스에 정의된 인터페이스에 정확하게 대응할 필요가 없습니다. 즉, 두 인터페이스는 서로 다른 형태일 수 있습니다. 일반적으로 Implementor 인터페이스는 기본적인 구현 연산을 수행하고, Abstraction은 더 추상화된 서비스 관점의 인터페이스를 제공합니다.

l  ConcreteImplementor : Implementor 인터페이스를 구현하는 것으로 실제적인 구현 내용을 담았습니다.

 

협력 방법

Abstraction 클래스가 사용자 요청을 implementor 객체에 전달합니다.

 

결과

1.    인터페이스와 구현 분리.

구현이 인터페이스에 얽매이지 않게 됩니다. 추상적 개념에 대한 어떤 방식의 구현을 택할 지가 런타임에 결정될 수 있습니다. 이는 런타임에 어떤 객체가 자신의 구현을 수시로 변경할 수 있음을 의미합니다.

 

Abstraction과 Implementor의 분리는 컴파일 타입 의존성을 제거할 수 있습니다. 구현을 변경하더라도 추상적 개념에 대한 클래스를 다시 컴파일 할 필요가 없고, 추상적 개념 클래스와 관련된 다른 코드 역시도 다시 컴파일 할 필요가 없습니다.

 

그뿐만 아니라, 더 잘 구조화된 시스템을 이끄는 계층화(layering)도 가능해집니다. 이런 시스템의 상위 수준 영역에서는 Abstraction과 Implementor만 알면 됩니다.

 

2.    확장성 제고.

Abstraction과 Implementor를 독립적으로 확장할 수 있습니다.

 

3.    구현 세부 사항을 사용자에게서 숨기기.

상세한 구현 내용을 사용자에게서 은닉할 수 있습니다.

 

구현

가교 패턴을 사용할 때 다음 사항을 고려합니다.

1.    Implementor 하나만 둡니다.

2.    정확한 Implementor 객체를 생성합니다.

3.    Implementor를 공유합니다.

4.    다중 상속을 이용합니다.

 

예제 소스

 

클래스 다이어그램

 


Program.cs

static void Main(string[] args)

{

    IChef[] chefs = new IChef[3];

    chefs[0] = new ChineseChef(new Jajangmyeon());

    chefs[1] = new ChineseChef(new Jjamppong());

    chefs[2] = new ChineseChef(new Sweetandsourpork());

 

    foreach (IChef chef in chefs)

    {

        chef.Cooking();

        Console.WriteLine("\n");

    }

}

Client 실질적으로 실행하는 부분

 

IChef.cs

interface IChef

    {

        void Cooking();

    }

Abstraction(추상화) 요리사가 요리하기 위한 기본적인 기능이 기술되어 있다.

 

ChineseChef.cs

class ChineseChef : IChef

{

    ICook cook;

    public ChineseChef(ICook _cook)

    {
        cook = _cook;

    }

    public void Cooking()

    {

        cook.Cooking();

    }

}

RefinedAbstraction(개선된 추상화) 중식 요리사가 요리하는 부분

 

Cook.cs

interface ICook

    {

        void Cooking();

    }

Implementor(구현자) 요리 부분이다. 추상 메소드만 정의되어 있다.

 

Jajangmyeon.cs

class Jajangmyeon : ICook

    {

        public void Cooking()

        {

            Console.WriteLine("짜장면을 만듭니다");

            Console.WriteLine("끓는 물에 면을 넣고");

            Console.WriteLine("짜장과 볶으면~");

            Console.WriteLine("짜장면 완성!");

        }

    }

ConcreteImplementor(구체적인 구현자) 짜장면을 만드는 부분이다.

 

Jjamppong.cs

class Jjamppong : ICook

    {

        public void Cooking()

        {

            Console.WriteLine("짬뽕을 만듭니다");

            Console.WriteLine("끓는 물에 면을 넣고");

            Console.WriteLine("해삼물을 볶고 육수를 넣고 끓이면~");

            Console.WriteLine("짬뽕 완성!");

        }

    }

ConcreteImplementor(구체적인 구현자) 짬뽕을 만드는 부분이다.

 

Sweetandsourpork.cs

class Sweetandsourpork : ICook

{

    public void Cooking()

    {

        Console.WriteLine("탕수육을 만듭니다");

Console.WriteLine("돼지고기에 튀김가루를 입혀서 기름에 튀기면");

        Console.WriteLine("탕수육 완성!");

    }

}

ConcreteImplementor(구체적인 구현자) 탕수육을 만드는 부분이다.

 

 

실행 화면

 

 

 

'Programming > Design Pattern' 카테고리의 다른 글

[Design Pattern] Template Method  (0) 2015.04.16
[Design Pattern] Builder  (0) 2015.04.16
[Design Pattern] Abstract Factory  (0) 2015.04.16
posted by Kanais