Computer Science/소프트웨어 공학

[SW 공학] 디자인 패턴 - 바퀴를 다시 발명하지 마라.

findTheValue 2021. 8. 22. 02:52

디자인 패턴(객체를 어떻게 구성할 것인가?)

  • 이름의 중요성
    • 디자인 패턴 이전에는 명칭이 없어서 같은 내용의 구조를 서로에게 설명해주기위해 수십시간을 낭비.
    • 이름이 곧 개념을 정의한다
    • 디자인 패턴도 이런 자주 사용되는 개념에 이름을 정의한 것.
    • *비슷한 문제 상황을 해결했던 해결책을 잘 기억하고 다시 적용할 수 있다면 유용할 것이다. *
    • 동료 개발자들과 잘 공유하기 위한 방법이 곧 ‘이름’을 지어주는 것이다.
  • 소프트웨어 공학에서 디자인 패턴(Design pattern)은 프로그램 개발 시에 자주 부닥치는 애로 상황에 대한 일반적이고 재사용 가능한 추상화된 해결책이다.
  • 소프트웨어 공학적으로 디자인 패턴은 패러다임과 알고리즘과는 다르다. OOP 패러다임으로 개발을 하든, 함수형 프로그래밍 패러다임으로 개발을 하든 문제상황은 일관되기 때문에 패러다임과 동의어가 될 수 없다. 또 디자인 패턴은 다수의 구체적인 상황과 알고리즘이 아닌, 일반화된 해결책이기 때문에 알고리즘과도 다르다. 한 디자인 패턴을 구현하는 알고리즘은 여러 가지일 수 있는 것이다.
  • 디자인 패턴을 찾아보면 거의 모든 내용이 객체지향의 내용들임을 알 수 있다. 하지만 디자인패턴은 꼭 OOP에 적합한 패턴이 아니어도 된다. 단지 OOP가 압도적인 패러다임이었기 때문에 이에 대한 연구가 더 많이 진행되었던 것이다.
  • “바퀴를 다시 발명하지 마라(Don’t reinvent the wheel)”
    이미 만들어져서 잘 되는 것을 처음부터 다시 만들 필요가 없다는 의미이다.

Creational patterns

  • 클래스의 인스턴스를 만드는 것과 관련이 있다.
  • 인스턴스 생성 시에 상속을 효과적으로 사용하는 데 집중하는 ‘Class-creation patterns’
  • 인스턴스를 효과적으로 생성하기 위해 상속 대신 Delegation을 활용하는 ‘Object-creation patterns’

Structural patterns

  • 클래스나 인스턴스들의 관계
  • 좋은 프로그램은 여러 기능들이 적절히 모듈화되어 서로간에 물고 물리는 관계 속에서 동작한다.
  • 이들의 구조를 확실히 하고, 단순하게 해 서로간의 인터페이스를 명확히 맞추는 것이 필요하다.
  • 패턴의 종류 중에는 ‘Adapter’ 패턴이 있다.

구조적 디자인 패턴

  • 어댑터는 서로 간 호환이 안 되는 기기를 이어준다. 다시 말하면 외부와 접촉하는 인터페이스가 불일치해 상호접근이 불가한 객체들 사이에서 둘을 이어주는 역할을 한다
  • 어댑터 패턴은 이 어댑터를 프로그래밍적으로 옮긴 것이다.

Behavioral patterns

  • 클래스나 인스턴스의 패턴뿐만 아니라, 이들이 동작하는 방식, 이들의 소통(Communication)방식에도 패턴을 정의한다.
  • 객체 속 작업이 진행되는 워크 플로우를 정의하고 따라갈 수 있기 때문에 유용하다.
  • Behavioral patterns는 여러 알고리즘이나 기능들이 어떻게 흐르는지, 어떤 순서로 소통하는지에 대해 정의하는 패턴이다.

디자인 패턴 구조

  • 콘텍스트(context)
    문제가 발생하는 여어 상황을 기술한다. 즉, 패턴이 적용될 수 있는 상황을 나타낸다.
    경우에 따라서는 패턴이 유용하지 못한 상황을 나타내기도 한다.
  • 문제(problem)
    패턴이 적용되어 해결될 필요가 있는 여러 디자인 이슈들을 기술한다.
    이때 여러 제약 사항과 영향력도 문제 해결을 위해 고려해야 한다.
  • 해결(solution)
    문제를 해결하도록 설계를 구성하는 요소들과 그 요소들 사이의 관계, 책임, 협력 관계를 기술한다.
    해결은 반드시 구체적인 구현 방법이나 언어에 의존적이지 않으며 다양한 상황에 적용할 수 있는 일종의 템플릿이다.

디자인패턴의 형식

  • 이름
  • 종류 : 생성, 객체생성 - 생성의 유연성, 코드 유지 용이
  • 구조 : 클래스, 객체의 정적 구조 - 프로그램 유지 용이
  • 행위 : 클래스와 객체의 반응과 책임할당 - 반복사용되는 객체들의 상호작용 패턴화
  • 의도
  • 별칭
  • 동기
  • 구조(UML다이어그램)
  • 구성물
  • 협력과정
  • 결과(효과, 문제점, 적용해야할 상황)
  • 구현
  • 샘플코드

GoF의 23가지 패턴

  • 사용자와 개발자의 단절. 본질은 어디에 있는가?
  • 켄트백의 XP(extreme programming) 제안
  • 에릭 감마, 리챠드 헬름, 랄프 존슨, 존 블리시드 4명의 갱단(GoF: Gang of Four)은 패턴 언어에서 영감을 받아 '소프트웨어 디자인 패턴'을 고안했다.
  • 수 많은 성공과 실패 경험속에서 정제된 노하우를 정제해 23가지 패턴을 제시.

 

  • 자주 사용하는 설계 형태를 정형화 해 유형별로 정리한 일종의 탬플릿
  • 소프트웨어 설계에 있어 공통된 문제들에 대한 표준적인 해법을 제시
  • 현대에 이르러 엔터프라이즈 App, 멀티스레드, UI, 데이터 구조, 아키텍쳐 등 다양한 분야로 패턴이 확장되는 중.
  • 디자인 패턴을 사용하지 않으면 훨씬 더 많은 코드를 작성하게 되며 테스트가 불가능할 정도로 버그가 많아지고 이해하고 리팩토링 하기도 어려워짐.

 

우리는 늘 해결하려는 문제가 이미 다른사람에 의해 해결 됐을 수 있음을 기억해야한다. 디자인 패턴은 최상의 소프트웨어 디자인 원칙을 따르기 위해 특정 방식으로 코드를 구조하는 방식이다. 즉 패턴을 알면 재사용 가능하고 유지 관리 가능한 코드를 작성하여 사전에 문제를 해결 할 수 있다.

 


디자인 패턴 종류

1. 생성 패턴

  • Builder
    - 복잡한 인스턴스를 조립하여 만드는 구조로, 복합 객체를 생성할 때 객체를 생성하는 방법(과정)과 객체를 구현(표현)하는 방법을 분리함으로써 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있는 디자인 패턴
    - 생성과 표기를 분리해서 복잡한 객체를 생성
  • Prototype
    - 처음부터 일반적인 원형을 만들어 놓고, 그것을 복사한 후 필요한 부분만 수정하여 사용하는 패턴으로, 생성할 객체의 원형을 제공하는 인스턴스에서 생성할 객체들의 타입이 결정되도록 설정하며 객체를 생성할 때 갖추어야 할 기본 형태가 있을 때 사용 되는 패턴
    - 기존 객체를 복제함으로써 객체를 생성
  • Factory Method
    - 상위 클래스에서 객체를 생성하는 인터페이스를 정의하고, 하위 클래스에서 인스턴스를 생성하도록 하는 방식으로, 상위 클래스에서 인스턴스를 만드는 방법만 결정하고, 하위 클래스에서 그 데이터의 생성을 책임지고 조작하는 함수들을 오버로딩하여 인터페이스와 실제 객체를 생성하는 클래스를 분리할 수 있는 특성을 갖는 디자인 패턴
    - 생성할 객체의 클래스를 국한하지 않고 객체를 생성
  • Abstract Factory
    - 구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 패턴으로 이 패턴을 통해 생성된 클래스에서는 사용자에게 인터페이스(API)를 제공하고, 구체적인 구현은 Con-crete Product 클래스에서 이루어지는 특징을 갖는 디자인 패턴
    - 동일한 주제의 다른 팩토리 묶음
  • Singleton
    - 전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 디자인 패턴
    - 한 클래스에 한 객체만 존재하도록 제한

2. 구조 패턴

  • Bridge
    - 기능의 클래스 계층과 구현의 클래스 계층을 연결하고, 구현부에서 추상 계층을 분리하여 추상화된 부분과 실제 구현 부분을 독립적으로 확장할 수 있는 디자인 패턴
    - 구현뿐만 아니라, 추상화된 부분까지 변겨해야 하는 경우 활용
  • Decorator
    - 기존에 구현되어 있는 클래스에 필요한 기능을 추가해 나가는 설계 패턴으로 기능 확장이 필요할 때 객체 간의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 해주어 상속의 대안으로 사용되는 디자인 패턴
    - 객체의 결합을 통해 기능을 동적으로 유연하게 확장
  • Facade
    - 복잡한 시스템에 대하여 단순한 인터페이스를 제공함으로써 사용자의 시스템 간 또는 여타 시스템과의 결합도를 낮추어 시스템 구조에 대한 파악을 쉽게 하는 패턴으로 오류에 대해서 단위별로 확인할 수 있게 하며, 사용자의 측면에서 단순한 인터페이스 제공을 통해 접근성을 높일 수 있는 디자인 패턴
    - 통합된 인터페이스 제공
  • Flyweight
    - 다수의 객체로 생성될 경우 모두가 갖는 본질적인 요소를 클래스 화하여 공유함으로써 메모리를 절약하고, '클래스의 경량화'를 목적으로 하는 디자인 패턴
    - 여러 개의 '가상 인스턴스'를 제공하여 메모리 절감
  • Proxy
    - '실체 객체에 대한 대리 객체'로 실체 객체에 대한 접근 이전에 필요한 행동을 취할 수 있게 만들며, 이 점을 이용해서 미리 할당하지 않아도 상관없는 것들을 실제 이용할 떄 할당하게 하여 메모리 용량을 아낄 수 있으며, 실체 객체를 드러나지 않게 하여 정보은닉의 역할도 수행하는 디자인 패턴
    - 특정 객체로의 접근을 제어하기 위한 용도로 사용
  • Composite
    - 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴으로, 사용자가 단일 객체과 복합 객체 모두 동일하게 다루도록 하는 패턴
    - 복합 객체와 단일 객체를 동일하게 취급
  • Adapter
    - 기존에 생성된 클래스를 재사용할 수 있도록 중간에서 맞춰주는 역할을 하는 인터페이스를 만드는 패턴으로, 상속을 이용하는 클래스 패턴과 위임을 이용하는 인스턴스 패턴의 두 가지 형태로 사용되는 디자인 패턴
    - 인터페이스가 호환되지 않는 클래스들을 함께 이용할 수 있도록 타 클래스의 인터페이스를 기존 인터페이스에 덧씌움

3. 행위 패턴

  • Mediator
    - 객체지향 설계에서 객체의 수가 너무 많아지면 서로 간 통신을 위해 복잡해져서 객체지향에서 가장 중요한 느스한 결합의 특성을 해칠 수 있기 때문에 이를 해결하는 방법으로 중간에 이를 통제하고 지시할 수 있는 역할을 하는 중재자를 두고, 중재자에게 모든 것을 요구하여 통신의 빈도수를 줄여 객체지향의 목표를 달성하게 해주는 디자인 패턴
    - 상호작용의 유연한 변경을 지원
  • Interpreter
    - 언어의 다양한 해석, 구체적으로 구문을 나누고 그 분리된 구문의 해석을 맡는 클래스를 각각 작성하여 여러 형태의 언어 구문을 해석할 수 있게 만드는 디자인 패턴
    - 문법 자체를 캡슐화하여 사용
  • Iterator
    - 컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 방법을 제공하는 디자인 패턴
    - 내부구조를 노출하지 않고, 복잡 객체의 원소를 순차적으로 접근 가능하게 해주는 행위 패턴
  • Template Method
    - 어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해서 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴으로 일반적으로 상위클래스(추상 클래스)에는 추상메서드를 통해 기능을 골격을 제공하고, 하위 클래스(구체 클래스)의 메서드에는 세부 처리를 구체화하는 방식으로 사용하며 코드 양을 줄이고 유지보수를 용이하게 만드는 특징을 갖는 디자인 패턴
    - 상위 작업의 구조를 바꾸지 않으면서 서브 클래스로 작업의 일부분을 수행
  • Observer
    - 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에 연락이 가고 자동으로 내용이 갱신되는 방버으로 일대 다의 의존성을 가지며 상호작용하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인 패턴
    • 객체의 상태 변화에 따라 다른 객체의 상태도 연동, 일대다 의존
  • State
    - 객체 상태를 캡슐화하여 클래스함으로써 그것을 참조하게 하는 방식으로 상태에 따라 다르게 처리할 수 있도록 행위 내용을 변경하여, 변경 시 원시코드의 수정을 최소화할 수 있고, 유지보수의 편의성도 갖는 디자인 패턴
    - 객체의 상태에 따라 행위 내용을 변경
  • Visitor
    - 각 클래스 데이터 구조로부터 처리 기능을 분리하여 별도의 클래스를 만들어 놓고 해당 클래스의 메서드가 각 클래스를 돌아다니며 특정 작업을 수행하도록 만드는 패턴으로, 객체의 구조는 변경하지 않으면서 기능만 따로 추가하거나 확장할 때 사용하는 디자인 패턴
    - 특정 구조를 이루는 복합 객체의 원소 특성에 따라 동작을 수행할 수 있도록 지원하는 행위
  • Command
    - 실행될 기능을 캡슐화함으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴으로 하나의 추상 클래스에 메서드를 만들어 각 명령이 들어오면 그에 맞는 서브 클래스가 선택되어 실행되는 특징을 갖는 디자인 패턴
    - 요구사항을 객체로 캡슐화
  • Strategy
    - 알고리즘 군을 정의하고(추상 클래스) 같은 알고리즘을 각각 하나의 클래스로 캡슐화한 다음, 필요할 때 서로 교환해서 사용할 수 있게 하는 패턴으로, 행위 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 디자인 패턴
    - 행위 객체를 클래스로 캡슐화해 동적으로 행위를 자유롭게 변환
  • Memento
    - 클래스 설계 관점에서 객체의 정보를 저장할 필요가 있을 때 적용하는 디자인 패턴으로 Undo 기능을 개발할 때 사용하는 디자인 패턴
    - 객체를 이전 상태로 복구시켜야 하느누 경우, '작업취소(Undo)' 요청 기능
  • Chain of Responsibility
    - 정적으로 어떤 기능에 대한 처리의 연결이 하드코딩 되어 있을 때 기능 처리의 연결 변경이 불가능한데, 이를 동적으로 연결되어 있는 경우에 따라 다르게 처리될 수 있도록 연결한 디자인 패턴
    - 한 요청을 2개 이상의 객체에서 처리

디자인 패턴을 써야하는 이유.

  1. 재사용
  • 항상 프로젝트는 예상 보다 오래 걸리고 예산도 더 많이 쓰는데 이는 변화로 인한 것이다. 변화는 요구사항이 변하거나, 시스템이 지속적으로 성장하거나, 새로운 기능이 추가되거나 성능을 최적화 할때도 일어난다.
  • 어떻게 변화를 최소화 하면서 어떻게 개발을 할꺼냐?
  • 소프트웨어 개발에서 가장 어려운 측면은 기존에 존재하는 코드를, 다른 사람이 작성한 코드를 이해하는 것이다. 그리고 새로운 버그를 넣지 않고, 의도치 않은 결과를 불러 일으키지 않도록 바꾸는 것이다.
  • 디자인 패턴은 객체지향 설계에서 변화를 최소한으로 줄여줄 수 있는 기술(전문가의 경험이 깃든 기술)을 제공한다.
  • 유지보수 하기 쉬운 객체지향 시스템을 만들어준다.
  1. 객체지향 스킬을 향상시킨다.
  • 객체지향의 꽃은 추상화, 상속, 다형성 그리고 캡슐화라고 생각하고 있습니다.
  • 일반적인 앱들에서는 코드 중복, 디자인의 파편화, 그리고 사용하는 클래스 수의 폭발적 증가 등의 문제가 일어난다.
  • 디자인 패턴들의 기반을 보면 클래스를 만들거나 이들을 가지고 조합을 맞출 때, “다양한 방식의 캡슐화를 하기”, “상속을 넘어서는 컴포지션을 선호하기”, “클래스는 확장을 지양하고 수정에는 열려있어야 한다 등의” 가이드라인을 제공해 준다.
  1. 라이브러리와 언어에서 패턴을 파악하기
  • 디자인 패턴은 객체지향 설계 문제의 공통된 해결책 즉, 우리 설계 문제의 솔루션을 제공한다.
  • 우리는 디자인 패턴 모듈을 다운 받아서 추가 설치할 필요가 없다. 이미 우리가 사용하는 시스템의 라이브러리, 패키지, 모듈에 이 디자인 패턴을 적용 되어있기 때문이다.
  • 예를 들어, 자바의 파일 I/O 패키지에서는 데코레이터 패턴을 사용한다. 이 패턴은 우리에게 파일 I/O의 핵심 기능을 사용할 수 있도록 하고 필요에 따라서 추가 기능을 제공한다. 이 패턴은 파일 I/O 시스템 설계에서 훌륭한 방법론이기도 하다.
  • 게다가 만약 디자인 패턴에 친숙하다면 (특히 데코레이터 패턴에서는) 객체 설계에서 이들이 어떻게 동작하고 구현되는지 즉시 이해할 수 있다. 이러한 장점은 설계를 이해하는 것에 도움이 될 것이다.
  • 우리는 자바 File 객체(데코레이터 패턴)을 사용하던, 자바스크립트의 Event object(옵저버 패턴), Cocoa의 NSUserDefault 객체(싱글톤), 혹은 MVC 패턴을 적용한 UI 패키지를 사용하던지 관계 없이, 우리는 새로운 라이브러리가 이미 패턴을 사용하고 있다는 사실을 알아 차릴 수 있고 이해할 수 있을 것이다.
  1. 공용어의 힘을 사용할 수 있다.
  • 디자인 패턴을 배우는 것의 장점 중 하나는 공통적인 문제에 친숙해 질 수 있다는 점이다.
  • 한번 디자인 패턴을 이해하면, 동료들이 어떠한 의도로 설계를 하였는지 빠르게 캐치가 가능하다.
  • 예를 들어, 동료가 설계의 새로운 파트를 작성할 것을 나에게 주문했다고 가정해보자.
  • 여기에 동일한 설계를 기술하는 두가지 방법이 있다.
    • “자, 나는 broadcast라는 클래스를 만들었어. 이 클래스는 모든 객체가 이 클래스를 예의 주시하고 있지. 언제라도 새로운 데이터가 이 클래스로부터 만들어지면 모든 리스너가 이 메시지를 받아. 가장 멋진 점은 리스너가 broadcast에 어 때이든지 참가할 수 있고 빠질 수 있다는 거야.”
    • 아니면, “자, 난 broadcast 클래스를 옵저버 패턴으로 만들었어”
  • 첫 번쨰 기술에서는 broadcast 클래스가 정말 뭘 하는지 이해하는데 많은 생각을 해야만 했다. 하지만 두번째는 이미 이 문장을 보자마자 알차릴 수 있다.
  • 이게 디자인 패턴을 사용하는 것의 힘이다. 만약 패턴의 용어로 의사소통을 한다면, 다른 개발자들도 즉시 캐치할 수 있을 것이다.
  1. 진리와 아름다움을 찾을 수 있습니다.
  • 객체지향 시스템을 한땀한땀 만드는 것이 매우 많은 노력을 요구함에도, 디자인 패턴은 이러한 노력과 지혜를 패턴을 이해함으로써 얻을 수 있다.
  • 디자인 패턴은 단순히 고안되고 발명된 것은 아니다. 이것들은 부단한 노력과 많은 시스템을 구축한 경험을 바탕으로한 깊은 통찰에서 비롯된 것이다.
  • 때문에 우리가 디자인 패턴을 배우면, 마치 숙련된 소프트웨어 아키텍트의 어깨 위에 있는 듯한 느낌을 받게 될 것이다.
  • 더 쉽고 좋고, 유지보수가 용이한 소프트웨어를 만들 수 있게 되는 것이다.

현 시대의 디자인 패턴.

  • 디자인 패턴은 정말로 중요성이 작아진 걸까?
  • 패턴의 중요성은 더욱 높아가고 있다. 그럼 왜 이러한 현상이 발생하는 것인가?

1) 패턴의 대중화(자동차 스틱과 오토 운전의 차이)
예전 자동차 변속기는 모두 스틱이었다. 초보자에게는 스틱과 클러치는 넘기 어려운 산이었다. 오토매틱 운전으로 바뀌면서 기어 조작이라는 불편함이 일순간에 사라졌다. 예전에는 모두 코딩을 통해서 구현하였다가 지금은 컴포넌트 등에 핵심 로직이 숨어버렸다. 따라서 개발자는 비즈니스 로직(Business Logic)만 신경 쓰면 된다.

2) 개발방식의 변화(프레임워크 기반 개발 방식)
대표적으로 MDD(Model Driven Development, 모델 기반 개발)을 꼽을 수 있다. 만들어진 기성품을 선만 연결하거나 Pseudo code를 통해서 구현하는 방식으로 진화하고 있다. 예를 들어 과거에는 JDBC Driver연결을 위해 실제 코딩 했지만 지금은 Drag&Drop방식으로 개발 방식이 변경되고 있다.

3) 솔루션 기반의 사용 문화(인건비의 부담)
점차 시스템을 자체 개발하기보다는 구현이 완료된 패키지를 구입한 후 이를 커스터마이징하여 사용하는 것이 일반화 되고 있다. 따라서, 예전보다 자체개발 프로젝트가 많지 않아지고 있는 것이 특징이라고 할 수 있다.

  • 결국 디자인 패턴으로
    • 그러나 결국 S/W개발은 디자인 패턴이 계속 중요할 것이다. 디자인 패턴은 단순히 개발에만 적용되지 않고 그 대상 범위가 오히려 확대될 것이다. Cloud Design Pattern, Enterprise Integration Design Pattern 등이 대표적인 사례가 될 것이다.
    • 가장 주목할 분야는 AI와 디자인 패턴의 결합이라고 할 수 있을 것이다. AI를 적용하여 문제를 해결하는 방식은 지금까지와는 다른 새로운 접근이다. 어떤 문제에 대해서 전문가들이 이렇게 해결할 수 있다는 모범 답안을 제시하는 방식으로 디자인 패턴을 적용한다면, 매력적인 새로운 분야가 열릴 수도 있다. 데이터 패턴(Data Pattern)이 대표적인 분야가 될 가능성이 높다. 방대한 데이터를 저장하는 기술은 갖출 수 있지만, 이를 어떻게 분석해서 업무에 적용할 것인가는 아직은 걸음마 단계이기 때문이다.