15장: 디자인 패턴과 프레임워크

디자인 패턴과 설계 재사용

소프트웨어 패턴

  • 소프트웨어 설계에서 반복적으로 발생하는 문제에 대해 반복적으로 적용할 수 있는 해결 방법

  • 설계를 재사용하는 것이 목적이다.

  • 패턴이란

    • 반복적으로 발생하는 문제와 해법의 쌍

    • 이미 알려진 문제와 이에 대한 해법을 문서로 정리할 수 있으며, 이 지식을 다른 사람과 의사소통할 수 있다.

    • 추상적인 원칙과 실제 코드 작성 사이의 간극을 메워주며 실질적인 코드 작성을 돕는다.

    • 패턴은 실무에서 유용하게 사용해왔고 다른 실무 컨텍스트에서도 유용할 것으로 예상되는 아이디어라고 정의할 수도 있다.

  • 패턴은 경험을 통해 축적된 실무 지식을 효과적으로 요약하고 전달하여 유연하고 품질 높은 소프트웨어 개발에 도움을 준다.

  • 패턴의 범위에는 개발과 직접적인 연관성을 가진 설계, 구현을 비롯하여 프로젝트 일정 추정 방법, 요구사항 관리 방법 등 반복적인 규칙을 발견할 수 있는 모든 영역이 해당된다.

  • 패턴 언어: 연관된 패턴들의 집합이 모여 하나의 언어를 구성하는 것. 패턴 생성 규칙, 같은 패턴 언어에 속한 다른 패턴과의 관계 및 협력 규칙 등을 포함한다. 패턴시스템과 유사한 의미이다.

패턴 분류

  • 패턴의 범위나 적용 단계에 따라 패턴이 분류된다.

  • 아키텍처 패턴

    • 소프트웨어의 전체적인 구조 결정에 사용한다.

    • 미리 정의된 서브시스템들을 제공하고, 각 서브시스템들의 책임을 정의하며, 서브시스템들 사이의 관계를 조직화하는 규칙과 가이드라인을 포함한다.

    • 구체적인 소프트웨어 아키택처를 위한 템플릿을 제공한다.

    • 디자인 패턴의 상위 개념이며 프로그래밍 언어나 패러다임에 독립적이다.

  • 분석 패턴

    • 도메인 내의 개념적인 문제를 해결을 목적으로 하며 업무 모델링 시에 발견되는 공통적인 구조를 표현하는 개념들의 집합이다.

    • 단 하나의 도메인에 대해서만 적절할 수도 있고 여러 도메인에 걸쳐 적용할 수도 있다.

  • 디자인 패턴

    • 특정 정황 내에서 일반적인 설계 문제를 해결하며, 협력하는 컴포넌트들 사이에서 반복적으로 발생하는 구조를 서술한다.

    • 중간 규모의 패턴으로, 특정한 설계 문제를 해결하는 것을 목적으로 하며, 프로그래밍 언어나 프로그래밍 패러다임에 독립적이다.

  • 이디엄

    • 특정 프로그래밍 언어에만 국한된 하위 레벨 패턴으로, 주어진 언어의 기능을 사용해 컴포넌트, 혹은 컴포넌트 간의 특정 측면을 구현하는 방법을 서술한다.

    • 특정 언어의 이디엄은 다른 언어에서 무용지물인 경우가 존재한다. 예를 들어 C++에서 자신을 참조하는 객체의 개수가 없을 경우 스스로 삭제하는 Count Pointer 이디엄은 GC 기능을 가진 자바 언어에서는 전혀 유용하지 않다.

패턴 & 책임 주도 설계

  • 패턴은 반복적으로 발생하는 문제에 대해 공통으로 사용할 수 있는 역할, 책임, 협력의 템플릿이다.

  • 특정한 상황에 적용 가능한 패턴을 잘 알고 있다면 책임 주도 설계의 절차를 하나하나 따르지 않고도 시스템 안에 구현할 객체들의 역할과 책임, 협력 관계를 빠르고 손쉽게 구성할 수 있다.

  • 패턴의 구성 요소는 클래스가 아니라 역할과 책임이다. 역할은 동일한 오퍼레이션에 대해 응답할 수 있는 책임의 집합을 암시하므로 하나의 객체가 여러 역할을 수행할 수도 있다*.*

  • 디자인 패턴을 따른다는 것은 특정 구현 방식을 강제하는 것이 아니라 역할, 책임, 협력의 관점에서 유사성을 공유한다는 것이다.

캡슐화

  • 각 디자인 패턴은 특정한 변경을 캡슐화하기 위한 독자적인 방법을 정의하고 있다.

  • 디자인 패턴에서 어떤 변경을 캡슐화하는지캡슐화 하기 위해 사용되는 방법을 이해하는 것이 중요하다.

  • 예를 들어 전략 패턴에서는 알고리즘의 변경을 캡슐화하기 위해 객체 합성을 이용한다. Movie 클래스 내에 DiscountPolicy를 합성으로 두고, 실제로는 이 인터페이스를 구현한 클래스들을 사용하는 것이다.

  • 템플릿 메서드 패턴에서는 변경을 캡슐화하기 위해 상속을 이용한다. 변경하지 않는 부분은 부모 클래스에 두고 변경이 발생하는 부분은 자식 클래스가 구현할 수 있도록 분리한다. Movie 클래스 내에서 구체적으로 가격을 계산하는 로직은 calculateDiscountAmount라는 추상 메서드를 두고 이를 자식 클래스에서 구현해 사용한다.

  • 데코레이터 패턴에서는 객체의 행동을 결합하기 위해 객체 합성을 사용한다. 선택 가능한 옵션의 개수와 순서에 대한 변경을 캡슐화할 수 있다.

패턴의 목적

  • 패턴이 제시하는 구조를 맹목적으로 따르거나 패턴이 필요하지 않은 상태에서 적용한다면, 불필요하게 복잡하고 난해하며 유지보수하기 어려운 시스템을 만들게 된다.

  • 컨텍스트의 적절성을 파악하여 패턴을 현재 상황에 맞게 수정해 사용해야 한다.

  • 패턴은 출발점이다. 현재 코드를 제대로 파악하여 패턴을 지향하거나 패턴을 목표로 리팩토링하며 적용해야 한다.

프레임워크와 코드 재사용

  • 프레임워크란 추상 클래스나 인터페이스를 정의하고 인스턴스 사이의 상호작용을 통해 시스템 전체 혹은 일부를 구현해 놓은 재사용 가능한 설계이다.

  • 설계와 코드를 함께 재사용하는 것이 목적이다.

  • 프레임워크는 애플리케이션의 아키텍처를 구현 코드의 형태로 제공하므로, 이를 요구사항에 맞게 커스터마이징하면 쉽고 빠르게 시스템을 구현할 수 있다.

  • 프레임워크는 클래스와 객체들의 분할, 전체 구조, 클래스와 객체들 간의 상호작용, 객체와 클래스 조합 방법, 제어 흐름에 대해 미리 정의해두므로 애플리케이션 구현 시 애플리케이션에 종속된 부분에 대해서만 설계하면 된다.

  • 프레임워크는 변하는 부분과 변하지 않는 부분을 서로 다른주기로 배포할 수 있도록 별도의 패키지로 세분화해야 한다.

  • 이 때 세부 사항을 구현한 패키지는 항상 상위 정책을 구현한 패키지에 의존해야 한다.

  • 프레임워크는 여러 애플리케이션 간 일관성 있는 협력을 구현하고 코드를 재사용할 수 있도록 한다.

  • 요구사항이 빠르게 진화하는 코드에서 의존성 역전 원리가 적절하게 지켜지지 않고 있다면 그곳에는 변경을 적절하게 수용할 수 없는 하향식의 절차적인 코드가 존재할 수밖에 없다.

  • 프레임워크를 사용할 경우 전체적인 협력 흐름은 프레임워크가 가지며, 애플리케이션에 속하는 서브클래스의 메서드를 호출하게 된다. 이 때 개별 애플리케이션에서 프레임워크로 제어 흐름의 주체가 이동하는 것을 Inversion of Control(제어의 역전) 이라고 부른다.

  • 프레임워크에서 애플리케이션에 따라 달라질 수 있는 동작은 비워두며 이를 **훅(hook)**이라고 부른다. 사용자에 의해 정의된 훅은 제어 역전 원리에 따라 프레임워크에서 필요한 시점에 호출된다.

Last updated