디자인 패턴이 설계 문제를 해결하는 방법
다음은 이러한 문제 중 몇 가지와 디자인 패턴이 이를 해결하는 방법입니다.
요청은 작업을 실행할 개체를 가져오는 유일한 방법입니다.
작업은 개체의 내부 데이터를 변경하는 유일한 방법입니다.
이러한 제한으로 인해 개체의 내부 상태가 캡슐화되었다고 합니다. 직접 액세스할 수 없으며 해당 표현은 개체 외부에서 보이지 않습니다.
객체 지향 설계에서 어려운 부분은 시스템을 객체로 분해하는 것입니다.
캡슐화, 세분성, 종속성, 유연성, 성능, 진화, 재사용성 등 많은 요소가 작용하기 때문에 작업이 어렵습니다.
그것들은 종종 상충되는 방식으로 분해에 영향을 미칩니다.
객체 지향 설계 방법론은 다양한 접근 방식을 제시합니다.
문제 진술을 작성하고 명사와 동사를 골라내어 해당하는 클래스와 함수를 생성할 수 있습니다.
또는 시스템의 협업 및 책임에 집중할 수도 있습니다.
또는 실제 세계를 모델링하고 분석 중에 발견된 개체를 설계로 변환할 수 있습니다.
접근 방식이 최선인지에 대해서는 항상 이견이 있을 것입니다.
설계의 많은 객체는 해석 모델에서 가져옵니다. 그러나 객체 지향 설계는 종종 현실 세계에 대응하는 클래스가 없는 클래스로 끝납니다.
이들 중 일부는 배열과 같은 저수준 클래스입니다. 다른 것들은 훨씬 더 높은 수준입니다.
현실 세계에 대한 엄격한 모델링은 오늘날의 현실을 반영하지만 반드시 내일의 현실은 아닌 시스템으로 이어집니다.
디자인 중에 나타나는 추상화는 디자인을 유연하게 만드는 핵심입니다.
디자인 패턴은 덜 분명한 추상화와 이를 포착할 수 있는 객체를 식별하는 데 도움이 됩니다.
예를 들어, 프로세스나 알고리즘(process or algorithm)을 나타내는 객체는 자연에서 발생하지 않지만 유연한 디자인의 중요한 부분입니다.
Strategy 패턴은 교환 가능한 알고리즘 제품군을 구현하는 방법을 설명합니다.
State 패턴은 개체의 각 상태를 개체로 나타냅니다.
이러한 객체는 분석 중이나 설계 초기 단계에서 거의 발견되지 않습니다.
설계을 보다 유연하고 재사용할 수 있도록 만드는 과정에서 나중에 발견됩니다.
code 구조는 compile-time에 고정됩니다. 그것은 고정 상속 관계의 클래스로 구성됩니다.
프로그램의 run-time 구조는 빠르게 변화하는 통신 객체들의 네트워크(networks of communicating objects)로 구성됩니다.
사실, 두 구조는 대체로 독립적입니다. 서로를 이해하려고 하는 것은 식물과 동물의 정적인 분류에서 생물 생태계의 역동성을 이해하려고 하는 것과 같으며 그 반대의 경우도 마찬가지입니다.
친한 객체는 서로의 작업을 요청할 수 있지만 서로에 대한 책임은 없습니다. acquaintance은 aggregation보다 약한 관계이며 객체 간의 훨씬 느슨한 결합을 나타냅니다.
aggregation와 지인은 동일한 방식으로 구현되는 경우가 많기 때문에 혼동하기 쉽습니다.
스몰토크에서 모든 변수는 다른 객체에 대한 참조입니다.
프로그래밍 언어에서는 집계와 지인 간의 구분이 없습니다.
C++에서 aggregation는 실제 인스턴스인 멤버 변수를 정의하여 구현할 수 있지만 인스턴스에 대한 포인터 또는 참조로 정의하는 것이 더 일반적입니다. 지인은 포인터와 참조로도 구현됩니다.
궁극적으로 지인과 집계는 명시적 언어 메커니즘보다 의도(intent)에 의해 더 많이 결정됩니다.
컴파일 타임 구조에서는 구별이 어려울 수 있지만 중요합니다.
aggregation 관계는 아는 사람보다 적고 영구적인 경향이 있습니다.
대조적으로, 지인은 더 자주 만들어지고 다시 만들어지며 때로는 수행 동안에만 존재합니다.
아는 사람도 더 역동적이어서 소스 코드에서 식별하기가 더 어렵습니다.
프로그램의 런타임 구조와 컴파일 시간 구조 사이에 이러한 차이가 있기 때문에 코드가 시스템 작동 방식에 대한 모든 것을 나타내지는 않을 것이 분명합니다.
시스템의 런타임 구조는 언어보다 설계자가 더 많이 부과해야 합니다.
개체와 해당 형식 간의 관계는 런타임 구조가 얼마나 좋은지 나쁜지를 결정하기 때문에 세심한 주의를 기울여 설계해야 합니다.
많은 디자인 패턴(특히 개체 범위가 있는 패턴)은 컴파일 시간과 런타임 구조를 명시적으로 구분합니다.
Composite(163) 및 Decorator(175)는 복잡한 런타임 구조를 구축하는 데 특히 유용합니다.
Observer(293)는 패턴을 알지 못하면 이해하기 어려운 런타임 구조를 포함합니다.
Chain of Responsibility(223) 상속이 드러내지 않는 커뮤니케이션 패턴을 초래합니다.
일반적으로 런타임 구조는 패턴을 이해할 때까지 코드에서 명확하지 않습니다.
(7-1 ) 재설계시 발생하는 대표적인 원인과 이를 해결하는 디자인 패턴
문제 1) 클래스를 명시적으로 지정하여 객체를 생성합니다 Creating an object by specifying a class explicitly
객체를 생성할 때 클래스 이름을 지정하면 특정 인터페이스 대신 특정 구현이 적용됩니다.
이러한 약속은 향후 변경 사항을 복잡하게 만들 수 있습니다. 이를 피하려면 간접적으로 객체를 생성하십시오.
디자인 패턴: Abstract Factory, Factory Method, Prototype
문제 2) Dependence on specific operations 특정 작업에 대한 의존성
특정 작업을 지정할 때 요청을 충족하는 한 가지 방법을 커밋합니다.
하드 코딩된 요청을 피함으로써 컴파일 타임과 런타임 모두에서 요청이 충족되는 방식을 더 쉽게 변경할 수 있습니다.
디자인 패턴: Chain of Responsibility, Command
문제 3) Dependence on hardware and software platform 하드웨어 및 소프트웨어 플랫폼에 대한 의존성
외부 운영 체제 인터페이스와 API(응용 프로그래밍 인터페이스)는 하드웨어 및 소프트웨어 플랫폼에 따라 다릅니다.
특정 플랫폼에 의존하는 소프트웨어는 다른 플랫폼으로 이식하기가 더 어려울 것입니다. 기본 플랫폼에서 최신 상태로 유지하는 것이 어려울 수도 있습니다.
따라서 플랫폼 종속성을 제한하도록 시스템을 설계하는 것이 중요합니다.
디자인 패턴: Abstract Factory, Bridge
문제 4) Dependence on object representations or implementations 객체 표현 또는 구현에 대한 의존성
개체가 어떻게 표시, 저장, 위치 지정 또는 구현되는지 알고 있는 클라이언트는 개체가 변경될 때 변경해야 할 수도 있습니다.
클라이언트에게 이 정보를 숨기면 변경 사항이 계단식으로 바뀌는 것을 방지할 수 있습니다.
디자인 패턴: Abstract Factory, Bridge, Memento, Proxy
문제 5) Algorithmic dependencies 알고리즘 종속성
알고리즘은 개발 및 재사용 중에 확장, 최적화 및 교체되는 경우가 많습니다. 알고리즘에 의존하는 객체는 알고리즘이 변경되면 변경되어야 합니다.
따라서 변경될 가능성이 있는 알고리즘은 격리되어야 합니다.
디자인 패턴: Builder, Iterator, Strategy, Template Method, Visitor
문제 6) Tight coupling 긴밀한 연결
밀접하게 결합된 클래스는 서로 의존하기 때문에 단독으로 재사용하기 어렵습니다. 긴밀한 결합은 다른 많은 클래스를 이해하고 변경하지 않고는 클래스를 변경하거나 제거할 수 없는 모놀리식 시스템으로 이어집니다. 시스템은 학습, 이식 및 유지 관리가 어려운 밀집된 덩어리가 됩니다.
느슨한 결합은 클래스가 자체적으로 재사용될 수 있고 시스템이 더 쉽게 학습, 이식, 수정 및 확장될 수 있는 가능성을 높입니다. 디자인 패턴은 추상 결합 및 계층화와 같은 기술을 사용하여 느슨하게 결합된 시스템을 촉진합니다.
디자인 패턴: Abstract Factory, Bridge, Chain of Responsibility, Command, Facade, Mediator, Observer
문제 7) Extending functionality by subclassing 서브클래싱을 통한 기능 확장
서브클래싱으로 개체를 사용자 정의하는 것은 종종 쉬운 일이 아닙니다. 모든 새 클래스에는 고정 구현 오버헤드(초기화, 종료 등)가 있습니다. 하위 클래스를 정의하려면 상위 클래스에 대한 심층적인 이해도 필요합니다. 예를 들어, 한 작업을 재정의하려면 다른 작업을 재정의해야 할 수 있습니다. 상속된 작업을 호출하려면 재정의된 작업이 필요할 수 있습니다. 그리고 서브클래싱은 클래스의 폭발로 이어질 수 있습니다. 간단한 확장을 위해 많은 새로운 서브클래스를 도입해야 할 수도 있기 때문입니다.
일반적으로 개체 구성과 특히 위임은 동작 결합을 위한 상속에 대한 유연한 대안을 제공합니다. 기존 클래스의 새 하위 클래스를 정의하는 대신 기존 개체를 새로운 방식으로 구성하여 응용 프로그램에 새 기능을 추가할 수 있습니다. 반면에 오브젝트 구성을 많이 사용하면 디자인을 이해하기가 더 어려워질 수 있습니다. 많은 디자인 패턴은 하나의 서브클래스를 정의하고 해당 인스턴스를 기존 서브클래스로 구성하여 사용자 정의 기능을 도입할 수 있는 디자인을 생성합니다.
디자인 패턴: Bridge, Chain of Responsibility, Composite, Decorator, Observer, Strategy
문제 8) Inability to alter classes conveniently 클래스를 편리하게 변경할 수 없음
가끔 수정이 불가능한 클래스를 편리하게 수정해야 할 때가 있습니다. 아마도 당신은 소스 코드가 필요하고 그것을 가지고 있지 않을 것입니다(상업 클래스 라이브러리의 경우처럼). 또는 변경하려면 기존 하위 클래스를 많이 수정해야 합니다. 디자인 패턴은 이러한 상황에서 클래스를 수정하는 방법을 제공합니다.
디자인 패턴: Adapter, Decorator, Visitor
(7- 2) 응용 프로그램, 툴킷, 프레임워크 세 가지 광범위한 소프트웨어 클래스의 개발에서 디자인 패턴이 수행하는 역할
1) 응용 프로그램 (Application Programs)
2 )툴킷(Toolkits)
3) 프레임워크(Frameworks)
참고서적 GoF의 디자인 패턴
'Clean Software > Design Pattern' 카테고리의 다른 글
[디자인 패턴] Iterator(반복자 패턴) (0) | 2022.02.16 |
---|---|
디자인 패턴(Design patterns) - 적절한 디자인패턴 선택방법과 사용법 (0) | 2022.02.14 |
디자인 패턴(Design pattern) - 정의 및 분류 (0) | 2021.04.25 |
Android 아키텍처(Architecture) 비교하기 - MVP, MVC, MVVM (0) | 2021.04.09 |
디자인 패턴(Design pattern) - 목차 (0) | 2021.04.08 |