본문 바로가기
SW

GoF의 디자인 패턴(Design Patterns: Elements of Reusable Object-Oriented Software) - 1장 서론(1.6)

by 라꾸스떼(YR) 2020. 4. 5.
반응형

[재사용을 실현 가능한 것으로]

[상속 대 합성]

객체지향 시스템에서 기능의 재사용을 위해 구사하는 가장 대표적인 기법은 클래스 상속, 그리고 객체 합성(object compostion)이다.

서브클래싱에 의한 재사용을 화이트박스 재사용(white-box reuse). 상속을 받으면 부모 클래스의 내부가 서브클래스에 공개되기 때문에 화이트박스

객체 합성은 클래스 상속에 대한 대안이다. 다른 객체를 여러 개 붙여서 새로운 기능 혹은 객체를 구성하는 것이다. 객체를 합성하려면, 합성에 들어가는 객체들의 인터페이스를 명확하게 정의해 두어야 한다. 블랙박스 재사용(black-box reuse). 객체의 내부는 공개되지 않고 인터페이스를 통해서만 재사용되기 때문이다.

클래스 상속은 컴파일 시점에 정적으로 정의되고 프로그래밍 언어가 직접 지원하므로 그대로 사용하면 된다. 클래스 상속으로 부모 클래스의 구현을 쉽게 수정할 수도 있는데, 서브클래스는 모든 연산이 아닌 일부만 재정의할 수도 있다.

클래스 상속의 단점.

1.런타임에 상속받은 부모 클래스의 구현을 변경할 수는 없다는 점이다.

2.부모 클래스는 서브클래스의 물리적 표현의 최소 부분만을 정의하기 때문에 서브클래스는 부모 클래스가 정의한 물리적 표현들을 전부 또는 일부 상속받는 점이다. 부모 클래스 구현에 변경이 생기면 서브클래스도 변경해야 한다.

이 구현의 종속성이 걸림돌로 작용하면서, 서브클래스를 재사용하려고 할 때 문제가 발생한다. 상속한 구현이 새로운 문제에 맞지 않을 때, 부모 클래스를 재작성해야 하거나 다른 것으로 대체하는 일이 생기게 된다. 해결하는 방법 한 가지는 추상 클래스에서만 상속받는 것이다. 추상 클래스를 상속했다는 것은 구현이 아닌 인터페이스를 상속한 것이므로 구현 자체는 서브클래스가 정의한다.

객체 합성은 한 객체가 다른 객체에 대한 참조자를 얻는 방식으로 런타임에 동적으로 정의된다. 합성은 객체가 다른 객체의 인터페이스만을 바라보게 하기 때문에, 인터페이스 정의에 더 많은 주의를 기울여야 한다.

클래스 상속보다 객체 합성을 더 선호하는 이유는 각 클래스의 캡슐화를 유지할 수 있다. 객체 합성으로 설계되면 클래스의 수는 적어지고 객체의 수는 좀더 많아질 수 있지만, 시스템의 행동은 클래스에 정의된 정적인 내용보다는 런타임에 드러나는 객체 합성에 의한 상호 관련성에 따라 달라질 수 있다.

객체 합성이 클래스 합성보다 더 나은 방법!

상속에 의한 재사용은 기존 클래스들을 조합해서 새로운 구성요소를 쉽게 만들 수 있도록 해준다. 그러므로 상속과 객체 합성은 적절히 조합되어야 완벽한 재사용이 가능하다.

 

[위임(delegation)]

위임에서는 두 객체가 하나의 요청을 처리한다. 수신 객체가 연산의 처리를 위임자(delegate)에게 보낸다. C++에서는 this를 이용해서 수신 객체를 참조한다. 위임과 동일한 효과를 얻으려면 수신 객체는 대리자에게 자신을 매개변수로 전달해서 위임된 연산이 수신자를 참조하게 한다.

위임의 가장 중요한 장점은 런타임에 행동의 복합을 가능하게 하고, 복합하는 방식도 변경해 준다는 것이다.

위임이 갖는 단점은, 객체 합성을 통해 소프트웨어 설계의 유연성을 보장하는 방법과 동일하게 동적인데다가 고도로 매개변수화된 소프트웨어는 ㅈ어적인 소프트웨어 구조보다 이해하기가 더 어렵다는 것이다. 이런 위임이 만들어 내는 복잡함보다 단순화의 효과를 더 크게 할 수 있다면 그 설계는 사용하기 좋은 설계이다.

상태, 전략 방문자 패턴에서 위임 방식을 사용한다. 상태 패턴에서 객체는 현재 상태를 표현하는 상태 객체에 요청의 처리를 위임한다. 전략 패턴에서 객체는 요청을 수행하는 추상화한 전략 객체에게 특정 요청을 위임한다. 이 두 패턴의 목적은 처리를 전달하는 객체를 변경하지 않고 객체의 행동을 변경할 수 있게 하자는 것이다. 방문자 패턴에서, 객체 구조의 각 요소에 수행하는 연산은 언제나 방문자 객체에게 위임된 연산이다.

위임에 전적으로 의존하는 패턴들. 중재자 패턴은 객체 간의 교류를 중재하는 객체를 도입하여 중재자 객체가 다른 객체로 연산을 전달하도록 구현한다. 책임 연쇄 패턴은 한 객체에서 다른 객체로 고리를 따라서 요청의 처리를 계속 위임한다. 가교 패턴은 구현과 추상적 개념을 분리하는 패턴이다. 추상화와 특정 구현을 대응시키고 추상화는 단순히 자신의 연산을 구현에 전달한다.

 

[상속 대 매개변수화된 타입(parameterized type)]

제네릭(generic), c++에서는 템플릿(template). 이 기법은 타입을 정의할 때 타입이 사용하는 다른 모든 타입을 다 지정하지 않은 채 정의한다. 미리 정의하지 않은 타입은 매개변수로 제공한다.

매개변수화된 타입은 객체지향 시스템에서 행동을 복합할 수 있는 세 번째 방법. 1. 클래스 상속, 2.객체 합성.

1.서브클래스에 의해 연산을 구현하는 방법(템플릿 메서드 패턴의 응용) : 상속

2.정렬 루틴으로 전달된 객체(전략) : 합성

3.C++ 템플릿이나, 제네릭으로 정의한 클래스의 인자로 원소를 비교할 함수 이름을 명시 : 매개변수화

객체 합성은 런타임에 행동을 변경할 수 있지만, 행동이 위임되기 때문에 비효율적일 수 있다. 상속이 연산에 대한 기본 행동을 부모 클래스가 제공하고 이를 서브클래스에서 재정의하도록 하는 것이라면, 매개변수화된 타입은 클래스가 사용하는 타입을 변경하게 하는 것이다. 상속도 매개변수화된 타입이라고 볼 수 있지만, 런타임에 변경이 일어나지는 않는다.

 

[런타임 및 컴파일 타임의 구조를 관계짓기]

객체 관계 중에는 집합(aggregation)과 인지(acquaintance)라는 것이 있다. 집합은 한 객체가 다른 객체를 소유하거나 그것에 책임을 진다는 뜻이다. 객체 통합에는 통합된 객체 및 그 객체를 소유한 객체의 생존주기가 똑같다는 의미도 들어 있다. 객체 인지는 한 객체가 다른 객체에 대해 알고 있음을 의미한다. 이를 “연관(association)” 관계 또는 “사용(using)” 관계라고도 한다. 인지를 받는 객체는 서로의 연산을 요청할 수도 있지만 서로에 대해 책임은 지지 않는다.

C++에서는 멤버 변수를 다른 객체의 인스턴스로 정의하여 집합 관계를 구현한다. 그러나 집합 관계를 표현하는 더 일반적인 방법은 다른 인스턴스를 가리키는 포인터를 정의하는 것이다. 인지 관계 역시 포인터로 구현한다. 인지 관계와 집합 관계는 언어의 처리 방식이 아닌 사용 목적에 따라 결정해야 한다. 집합 관계는 인지 관계보다는 강력한 영속성의 개념을 갖는다. 인지 관계는 자주 바뀌게 된다.

많은 디자인 패턴이 컴파일 시점과 런타임 구조를 명시적으로 구분하고 있다. 복합체 패턴과 장식자 패턴은 복잡한 실행 구조를 구축하는 데 유용한 패턴이다. 감시자 패턴으로 만드는 런타임 구조는 이 패턴을 잘 알고 있지 않는 한 이해하기가 종종 까다롭다. 책임 연쇄 패턴은 상속이 드러나지 않는 교류 패턴을 만들어 낸다.

 

[변화에 대비한 설계]

재사용을 최대화하기 위해서는 새로운 요구 사항과 기존 요구 사항에 발생한 변경을 예측하여 앞으로의 시스템 설계가 진화할 수 있도록 해야 한다. 변화를 수용하지 못하는 설계는 앞으로 재설계가 필요하게 된다. 이런 변경들은 클래스의 재설계와 재구현, 사용자의 수정, 새로운 테스팅을 유발한다.

디자인 패턴을 써서 재설계를 할 수 밖에 없게 하는 이유와 해당 문제 해결에 사용하는 디자인 패턴

1.특정 클래스에서 객체 생성. 이를 방지하려면 객체를 직접 생성해서는 안된다.

디자인 패턴 : 추상 팩토리, 팩토리 메서드, 원형

2.특정 연산에 대한 의존성. 요청의 처리 방법을 직접 코딩하는 방식을 피한다.

디자인 패턴 : 책임 연쇄, 명령

3.하드웨어와 소프트웨어 플랫폼에 대한 의존성. 플랫폼 종속성을 제거한다.

디자인 패턴 : 추상 팩토리, 가교

4.객체의 표현이나 구현에 대한 의존성. 정보를 사용자에게 감춤

디자인 패턴 : 추상 팩토리, 가교, 메멘토, 프록시

5.알고리즘 의존성. 변경이 가능한 알고리즘은 분리해낸다.

디자인 패턴 : 빌더, 반복자, 전략, 템플릿 메서드, 방문자

6.높은 결합도. 추상 클래스 수준에서 결합도를 정의한다거나 계층화시키는 방법으로 디자인 패턴은 낮은 결합도의 시스템을 만들도록 한다.

디자인 패턴 : 추상 팩토리, 가교, 책임 연쇄, 명령, 퍼사드, 중재자, 감시자

7.서브클래싱을 통한 기능 확장. 일반적으로 객체 합성과 위임은 행동 조합을 위한 상속보다 훨씬 유연한 방법이다. 많은 디자인 패턴에서는 그냥 서브클래스를 정의하고 다른 인스턴스와 새로 정의한 클래스의 인스턴스를 합성해서 기능을 재정의하는 방법을 도입한다.

디자인 패턴 : 가교, 책임 연쇄, 장식자, 감시자, 전략

8.클래스 변경이 편하지 못한 점.

디자인 패턴 : 적응자, 장식자, 방문자

반응형

댓글