Post

디자인 패턴과 프레임워크

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

소프트웨어 설계에서 반복적으로 발생하는 문제에 대해 반복적으로 적용할 수 있는 해결 방법을 디자인 패턴이라고 부른다.

디자인 패턴의 목적은 설계를 재사용하는 것

디자인 패턴은 다양한 변경을 다루기 위해 반복적으로 재사용할 수 있는 설계의 묶음

디자인 패턴이 설계를 재사용하기 위한 것이라면 프레임워크는 설계와 코드를 함께 재사용하기 위한 것

프레임워크는 애플리케이션의 아키텍처를 구현 코드의 형태로 제공

프레임워크가 제공하는 아키텍처가 요구사항에 적합하다면 다양한 환경에서 테스트를 거친 견고한 구현 코드로 쉽고 빠르게 재사용 가능

디자인 패턴과 프레임워크는 모두 일관성 있는 협력과 관계있다.

디자인 패턴은 특정한 변경을 일관성 있게 다룰 수 있는 협력 템플릿을 제공

프레임워크는 특정한 변경을 일관성 있게 다룰 수 있는 확장 가능한 코드 템플릿을 제공

디자인 패턴이 협력을 일관성 있게 만들기 위해 재사용할 수 있는 설계의 묶음이라면, 프레임워크는 일관성 있는 협력을 제공하는 확장 가능한 코드

15.1 디자인 패턴과 설계 재사용

소프트웨어 패턴

패턴의 핵심적인 특징

  • 패턴은 반복적으로 발생하는 문제와 해법의 쌍
  • 패턴을 사용함으로써 이미 알려진 문제와 이에 대한 해법을 문서로 정리할 수 있으며, 이 지식을 다른 사람과 의사소통 가능
  • 패턴은 추상적인 원칙과 실제 코드 작성 사이의 간극을 메워주며 실질적인 코드 작성을 돕는다
  • 패턴의 요점은 패턴이 실무에서 탄생했다는 점

하나의 실무 컨텍스트에서 유용하게 사용해왔고 다른 실무 컨텍스트에서 유용할 것이라고 예상되는 아이디어

패턴은 한 컨텍스트에서 유용한 동시에 다른 컨텍스트에서도 유용한 ‘아이디어’

패턴이 지닌 가장 큰 가치는 경험을 통해 축적된 실무 지식을 효과적으로 요약하고 전달할 수 있다는 점

패턴은 지식 전달과 커뮤니케이션의 수단으로 활용할 수 있기 때문에 패턴에서 가장 중요한 요소는 패턴의 ‘이름’

패턴의 이름은 높은 수준의 대화를 가능하게 하는 원천

패턴은 특정 패턴 내에 포함된 컴포넌트와 컴포넌트 간의 관계는 더 작은 패턴에 의해 서술될 수 있으며, 패턴들을 포함하는 더 큰 패턴 내에 통합될 수 있다.

연관된 패턴들의 집합들이 모여 하나의 페턴 언어(Pattern Language)를 구성함

패턴 언어는 연관된 패턴 카테고리뿐만 아니라 패턴의 생성 규칙과 함께 패턴 언어에 속한 다른 패턴과의 관계 및 협력 규칙을 포함

패턴 분류

패턴을 분류하는 방법은 가장 일반적인 방법은 패턴의 범위나 적용 단계에 따라 아키텍처 패턴(Architecture Pattern), 분석 패턴(Analysis Pattern), 디자인 패턴(Design Pattern), 이디엄(Idiom)의 4가지로 분류

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

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

디자인 패턴의 상위에는 소프트웨어의 전체적인 구조를 결정하기 위해 사용할 수 있는 아키텍처 패턴

아키텍처 패턴은 미리 정의된 서브시스템들을 제공하고, 각 서브시스템들의 책임을 정의하며, 서브시스템들 사이의 관계를 조직화하는 규칙과 가이드라인을 포함

프로그래밍 언어나 프로그래핑 패러다임에 독립적

디자인 패턴의 하위에는 이디엄

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

분석 패턴은 도메인 내의 개념적인 문제를 해결하는 데 초점

분석 패턴은 업무 모델링 시에 발견되는 공통적인 구조를 표현하는 개념들의 집합

패턴과 책임-주도 설계

패턴은 공통으로 사용할 수 있는 역할, 책임, 협력의 템플릿

STRATEGY 패턴은 다양한 알고리즘을 동적으로 교체할 수 있는 역할과 책임의 집합을 제공

BRIDGE 패턴은 추상화의 조합으로 인한 클래스의 폭발적인 증가 문제를 해결하기 위해 역할과 책임을 추상화와 구현의 두 개의 커다란 집합으로 분해함으로써 설계를 확장 가능하게 만든다.

OBSERVER 패턴은 유연한 통지 메커니즘을 구축하기 위해 객체 간의 결합도를 낮출 수 있는 역할과 책임의 집합을 제공

패턴을 따르면 특정한 상황에 적용할 수 있는 설계를 쉽고 빠르게 떠올릴 수 있다

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

패턴의 구성 요소는 클래스가 아니라 ‘역할’

디자인 패턴의 구성요소가 클래스와 메서드가 아니라 역할과 책임이라는 사실을 이해하는 것이 중요

어떤 구현 코드가 어떤 디자인 패턴을 따른다고 이야기할 때는 역할, 책임, 협력의 관점에서 유사성을 공유한다는 것이지 특정한 구현 방식을 강제하는 것은 아니라는 점을 이해하는 것 역시 중요

캡슐화와 디자인 패턴

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

STRATEGY 패턴의 목적은 알고리즘의 변경을 캡슐화하는 것이고 이를 구현하기 위해 객체 합성을 이용함

TEMPLATE METHOD 패턴: 알고리즘을 캡슐화하기 위해 합성 관계가 아닌 상속 관계를 사용하는 것

TEMPLATE METHOD 패턴은 부모 클래스가 알고리즘의 기본 구조를 정의하고 구체적인 단계는 자식 클래스에서 정의하게 함으로써 변경을 캡슐화할 수 있는 디자인 패턴

DECORATOR 패턴은 객체의 행동을 동적으로 추가할 수 있게 해주는 패턴으로서 기본적으로 객체의 행동을 결합하기 위해 객체 합성을 사용

DECORATOR 패턴은 선택적인 행동의 개수와 순서에 대한 변경을 캡슐화 가능

대부분의 디자인 패턴의 목적은 특정한 변경을 캡슐화함으로써 유연하고 일관성 있는 협력을 설계할 수 있는 경험을 공유하는 것

어떤 디자인 패턴이 어떤 변경을 캡슐화하는지를 이해하는 것이 중요

각 디자인 패턴이 변경을 캡슐화하기 위해 어떤 방법을 사용하는지를 이해하는 것이 중요

객체의 수를 캡슐화하는 COMPOSITE 패턴

COMPOSITE 패턴은 개별 객체와 복합 객체라는 객체의 수와 관련된 변경을 캡슐화하는 것이 목적

협력하는 객체의 수를 변경하더라고 영향을 미치지 않음

패턴은 출발점이다

패턴은 단지 목표로 하는 설계에 이를 수 있는 방향을 제시하는 나침반에 불과하다.

디자인 패턴이 현재의 요구사항이나 적용 기술, 프레임워크에 적합하지 않다면 패턴을 그대로 따르지 말고 목적에 맞게 패턴을 수정하라.

패턴을 사용하면서 부딪히게 되는 대부분의 문제는 패턴을 맹목적으로 사용할 때 발생

대부분의 패턴 입문자가 빠지기 쉬운 함정은 패턴을 적용하는 컨텍스트의 적절성은 무시한 채 패턴의 구조에만 초점을 맞추는 것

패턴을 남용하지 않기 위해서는 다양한 트레이드오프 관계 속에서 패턴을 적용하고 사용해 본 경험이 필요함

패턴을 가장 효과적으로 적용하는 방법은 패턴을 지향하거나 패턴을 목표로 리팩터링 하는 것

패턴은 출발점

문제를 분석하고 창의력을 발휘함으로써 패턴을 현재의 문제에 적합하도록 적절하게 수정하라

프레임워크와 코드 재사용

코드 재사용 대 설계 재사용

재사용 관점에서 설계 재사용보다 더 좋은 방법은 코드 재사용

여러 프로젝트나 도메인 사이에 비슷한 문제가 충분히 많이 존재한다면 컴포넌트 기반의 접근법이 효과가 있을 수 있겠지만 애플리케이션과 도메인의 다양성으로 인해 두 가지 문제가 아주 비슷한 경우는 거의 없다.

따라서 가장 기본이 되는 아주 적은 부분만이 일반화

결국 다양한 도메인에 재사용 가능한 컴포넌트라는 개념은 비현실적

가장 이상적인 형태의 재사용 방법은 설계 재사용과 코드 재사용을 적절한 수준으로 조합하는 것

설계를 재사용하면서도 유사한 코드를 반복적으로 구현하는 문제를 피할 수 있는 방법 ? -> 프레임워크

프레임워크란 ‘추상 클래스나 인터페이스를 정의하고 인스턴스 사이의 상호작용을 통해 시스템 전체 혹은 일부를 구현해 놓은 재사용 가능한 설계’ 또는 ‘애플리케이션 개발자가 현재의 요구사항에 맞게 커스터마이징할 수 있는 애플리케이션의 골격’

프레임워크는 코드를 재사용함으로써 설계 아이디어를 재사용

프레임워크는 애플리케이션에 대한 아키텍처를 제공

프레임워크는 클래스와 객체들의 분할, 전체 구조, 클래스와 객체들 간의 상호작용, 객체와 클래스 조합 방법, 제어 흐름에 대해 미리 정의

상위 정책과 하위 정책으로 패키지 분리하기

프레임워크의 핵심은 추상 클래스나 인터페이스와 같은 추상화

추상 클래스와 인터페이스가 일관성 있는 협력을 만드는 핵심 재료

객체지향 이전의 구조적인 설계와 같은 전통적인 소프트웨어 개발 방법의 경우 상위 레벨 모듈이 하위 레벨 모듈에, 그리고 상위 정책이 구체적인 세부적인 사항에 의존하도록 소프트웨어를 구성

하지만 상위 정책은 상대적으로 변경에 안정적이지만 세부 사항은 자주 변경됨

만약 변하지 않는 상위 정책이 자주 변하는 세부 사항에 의존한다면 변경에 대한 파급효과로 인해 상위 정책이 불안정해짐

요점은 상위 정책이 세부 사항보다 더 다양한 상황에서 재사용될 수 있어야 함

하지만 상위 정책이 세부 사항에 의존하게 되면 상위 정책에 필요한 모든 경우에 세부 사항도 항상 존재해야 하기 때문에 상위 정책의 재사용성이 낮아짐

이 문제를 해결할 수 있는 가장 좋은 방법은 의존성 역전 원칙에 맞게 상위 정책과 세부 사항 모두 추상화에 의존하게 만드는 것

의존성 역전 원칙의 관점에서 세부 사항은 ‘변경’을 의미

동일한 역할을 수행하는 객체들 사이의 협력 구조를 다양한 애플리키에션 안에서 재사용하는 것이 핵심

이를 위해서는 변하는 것과 변하지 않느 것을 서로 분리해야 함

프레임워크는 여러 애플리케이션에 걸쳐 재사용 가능해야 하기 때문에 변하는 것과 변하지 않는 것들을 서로 다른 주기로 배포할 수 있도록 별도의 ‘배포 단위’로 분리해야 함

변하는 부분과 변하지 않는 부분을 별도의 패키지로 분리

의존성 역전 원리에 따라 추상화에만 의존하도록 의존성의 방향을 조정하고 추상화를 경계로 패키지를 분리했기 때문에 세부 사항을 구현한 패키지는 항상 상위 정책을 구현한 패키지에 의존해야 함

상위 정책을 구현하고 있는 패키지가 충분히 안정적이고 성숙했다면 하위 정책 패키지로부터 완벽히 분리해서 별도의 배포 단위로 만들 수 있다.

프레임워크는 여러 애플리케이션에 걸쳐 일관성 있는 협력을 구현할 수 있게 해줌

일관성 있는 협력이 제공하는 다양한 장점들은 프레임워크에도 적용됨

일관성 + 이해하기 쉬움 + 재사용 가능

제어 역전 원리

의존성 역전 원리는 전통적인 설계 방법과 객체지향을 구분하는 가장 핵심적인 원리

의존성 역전 원리에 따라 구축되지 않은 시스템은 협력 흐름을 재사용할 수도 없으며 변경에 유연하게 대처할 수 없다.

객체지향 설계는 의존성이 역전된 설계

좋은 객체지향 설계의 증명이 의존성의 역전

의존성 역전 원리는 프레임워크의 가장 기본적인 설계 매커니즘

의존성 역전은 의존성의 방향뿐만 아니라 제어 흐름의 주체 역시 역전

-> 제어 역전(Inversion of Control) 원리, 할리우드(Hollywood) 원리

프레임워크에서는 일반적인 해결책만 제공하고 애플리케이션에 따라 달라질 수 잇는 특정한 동작은 비워둔다. -> 훅(hook)

훅의 구현 방식은 애플리케이션의 컨텍스트에 따라 다름

재정의된 훅은 제어 역전 원리에 따라 프레임워크가 원하는 시점에 호출됨

협력을 제어하는 것은 프레임워크

설계 수준의 재사용은 애플리케이션과 기반이 되는 소프트웨어 간에 제어를 바꾸게 함

라이브러리를 사용해서 애플리케이션을 작성하면 애플리케이션이 필요한 라이브러러의 코드를 호출

즉, 애플리케이션이 어떤 라이브러리를 사용할 것인지를 스스로 제어

그러나 프레임워크를 재사용할 때는 프레임워크가 제공하는 메인 프로그램을 재사용하고 이 메인 프로그램이 호출하는 코드를 애플리케이션 개발자가 작성해야 함

제어 주체는 자신이 아닌 프레임워크

참조

  1. 오브젝트
This post is licensed under CC BY 4.0 by the author.