치춘짱베리굿나이스
디자인 패턴이란 본문
디자인 패턴
MVC 패턴에 대한 글을 적고 있었는데 아예 카테고리를 따로 파서 디자인 패턴 관련된 글을 묶어놓는 게 나을 것 같아 일단 디자인 패턴에 관해 알아보기로 했따..
개요
디자인 패턴?
건축학에서 유래된 용어로, 건축학에서는 몇몇 눈에 띄는 공통적인 부분을 디자인 패턴으로 정의하여 이를 조합하여 더 큰 건축물 (또는 도시) 을 설계하는 방법이다
이 건축학 기법이 소프트웨어 프로그래밍 계에 큰 영감을 주면서 비슷한 방식으로 도입하게 되었다
소프트웨어를 설계하면서 발견된 문제와 해결 노하우 (이 부분은 이렇게 짜면 문제가 해결된다 / 개선된다 싶은 것들) 를 다른 큰 소프트웨어를 설계할 때 응용할 수 있도록 잘 정리해 둔 패턴이 바로 디자인 패턴이다
내가 지금 겪고 있는 문제를 과거 다른 사람들이 다 이미 겪어봤을 확률이 매우 높으며, 그 때 사람들이 해결했던 방식을 정리해서 ‘너희들은 우리처럼 해메지 마라…’ 라고 알려주는 것이라고 할 수 있다
주로 객체지향 프로그래밍을 할 때 클래스 ↔ 인스턴스 간의 관계나 객체간의 관계를 유기적으로 정의하기 위해 디자인 패턴을 자주 사용하는 편이고, 소프트웨어 설계 시에도 MVC
, MVP
, MVVM
, MVW
등 다양한 패턴을 차용한다
디자인 패턴의 필수요소
- 패턴의 이름
- 이 패턴의 의도를 이름만 보고도 파악할 수 있게 명명되어야 한다
- 함수 이름 짓기 힘들듯… 패턴 이름 짓기도 상당히 어려울 것
- 문제
- 어떤 문제에 봉착했을 때 패턴을 도입해야 하는지에 대한 적절한 사례가 정의되어야 한다
- 해법
- 이 패턴을 사용함으로써 어떻게 동작하는지, 설계의 구성요소간 관계는 어떻게 되는지 정의한다
- ‘해법' 이라는 이름과는 다르게 실질적인 구현 해답을 주는 것은 아니고, 클래스와 객체를 어떤 식으로 사용할 지에 대한 방법만을 제시한다
- 결과
- 이 패턴을 사용함으로써 어떤 부분이 개선될 수 있고 어떠한 정책을 따르는지 알려준다
- 이 패턴을 도입하면서 얻을 수 있는 이점을 정리하면 패턴을 이해하는 데에 도움을 줄 수 있다
디자인 패턴을 도입했을 때의 장점
- 한 로직을 설계하기 위해 머리를 싸매기보단, 이미 있는 패턴을 적절히 변형 및 차용해서 원하는 구조를 잡을 수 있다
- ‘하나의 객체를 다른 객체들이 참조하는 구조를 짜고 싶다’ ‘추상적인 클래스 하나를 여러 자식 클래스들이 상속받는 구조를 잡는 게 좋겠다' 같은 고민이 들 때, 적합한 설계를 위해 멀리 돌아갈 필요 없이 적합한 디자인 패턴 (싱글톤 패턴, 템플릿 메소드 패턴 등) 을 차용하면 효율이 올라간다
- 이미 다양한 디자인 패턴이 나와 있기 때문에, 이에 대해 공부해 두면 어떤 작업에서 어떤 디자인 패턴을 차용할지 눈에 들어오므로 미리 조금씩 알아두면 좋겠다
- 협업 시에 적합한 디자인 패턴을 도입하면, 구조에 관해 설명할 필요가 줄어든다
- 팀원간 어떤 디자인 패턴을 도입하여 작업했는지 공유만 된다면, 굳이 이 구조가 어떤 이점을 갖고 어떤 방식으로 동작하는지 자세하게 기술할 필요가 없다
- 마치 클라이언트와 서버가 약속된 형식을 따라 데이터를 주고받듯이, 팀원 간에도 약속된 디자인 패턴을 사용하여 의사 소통을 원활하게 할 수 있다
주의
디자인 패턴 그 자체에 너무 매몰되어 패턴에 정확히 들어맞는 구조를 짜기 보다는, 프로젝트에 맞게 적합하게 변형하여 사용하는 것이 좋다
함수형 프로그램과 같은 프로그래밍 패러다임과 같은 이치이다… ‘함수형 프로그래밍 방식을 도입했으니까 무조건 모든 함수를 순수함수로 짜야 해…! 그리고 값은 무조건 불변해야 하지!!’ 라며 그 특성에 너무 얽매이다 보면 빠르게 갈 수 있는 지름길을 놔 두고 과도하게 돌아가는 경우가 생길 수 있다
디자인 패턴은 ‘이런 상황에 놓였을 땐 이걸 사용해 보는 게 어때?’ 라는 일종의 제안일 뿐이며, 너무 패턴대로 짜겠다고 골머리를 썩지 말자
또한 어떠한 패턴을 도입했다면, 이 패턴을 도입한 이유와 그로 인해 개선된 문제점들을 명확히 알고 있는 것이 좋다
‘남들이 쓰길래 좋아보여서 저도 썼는데용 ㅇㅅㅇaa;;’ 은 별로 좋지 못한 변명인 것 같다… (요즘 많이 느낌)
하나의 디자인 패턴을 차용해서 모든 코드를 그 패턴대로만 작성하기 보다는, 각 패턴별 장점을 명확히 파악하고 적절한 로직에만 적용하는 것이 적합하다
싱글톤 패턴을 과하게 사용하다 보면 객체지향에 전면으로 위배된다는 멘토님의 코멘트가 있었다
싱글톤 패턴을 남용하여 모든 인스턴스를 단 한 개만 만들게 되면 이게 과연 객체지향이라 할 수 있을까…? 싶은 것이다…
디자인 패턴 종류 - GoF
객체지향 디자인 패턴으로 GoF 디자인 패턴 시리즈가 있으며, 어떤 로직을 작성하기 위한 패턴인지에 따라 3개의 카테고리로 분류된다
GoF는 이 패턴들을 구체화하고 체계화한 사람들의 팀 이름으로, Gang of Four
이라는 뜻이다
에리히 감마, 리처드 헬름, 랄프 존슨, 존 블리시데스가 공동집필했으며 논문이 먼저 나오고, 이를 책으로 엮어 발매하였다
책은 여기서 구매가능하다 (홍보아님)
생성 패턴
객체 인스턴스를 생성할 때, 그 절차를 추상화한 패턴이다
어떤 방식으로 레고 블럭을 만들 지 고민하는 부분이라고 할 수 있다
- 싱글톤 패턴 (Singleton)
전에 정리했던 싱글톤 패턴 또한 객체지향 디자인 패턴 중 하나이다
특정 클래스의 인스턴스를 단 한 개만 존재하게 함으로써 메모리를 아끼고, 여러 다른 객체들이 하나의 인스턴스만을 참조하게 함으로써 데이터 공유를 용이하게 하는 이점이 있다
- 추상 팩토리 패턴 (Abstract Factory)
클래스를 자세하게 정의하는 대신, 매우 추상적인 클래스를 이용하여 비슷한 객체들의 군 (群) 을 만들어내는 패턴이다
노란색 4칸짜리 레고 블럭을 만든다고 해 보자
굳이 노란색 4칸 레고 블럭만을 만드는 공장 대신, 우리는 ‘노란 블럭을 만드는 공장’ 또는 ‘4칸짜리 정사각형 블럭을 만드는 공장’ 을 통해 노란 블럭의 집합 또는 4칸짜리 정사각형 블럭의 집합을 만들어낼 수 있다
이렇게 관련성이 있는 객체들의 특성을 모아 클래스를 만든 후, 이를 통해 세부적인 인스턴스를 만들어내는 것이 추상 팩토리 패턴이다
- 팩토리 패턴 (Factory)
객체를 생성하기 위한 인터페이스 (추상 클래스) 를 정의하긴 하지만, 그 인터페이스대로 인스턴스를 생성하는 것은 하위 클래스 (서브클래스) 가 결정하는 방식의 패턴이다
레고 블럭을 만드는 설계도와, 그 설계도대로 블럭을 만드는 공장을 별개로 두고, 어떤 모양의 블럭이 만들어질지는 해당 공장에서 결정한다
하나의 공장에서 한 종류의 설계도를 가지고 블럭을 만들게 한다면, 다른 종류의 블럭을 만들고 싶을 땐 공장을 새로 짓거나 구조를 뜯어고쳐야 했지만, 팩토리 패턴은 인스턴스를 만드는 클래스들이 병렬로 동작하기 때문에 새로운 종류의 인스턴스를 만들고자 할 때 구조를 수정할 필요 없이 추가와 삭제가 유연하다
결과적으로 소프트웨어가 코드에 종속되지 않게 하는 이점이 있다
- 빌더 패턴 (Builder)
여러 작은 객체를 조합하여 큰 객체를 만들 때, 작은 객체들을 어떻게 생성할지와 그 객체들을 어떻게 조립할지에 대한 로직을 분리하는 패턴이다
한 공장에서 블럭을 제작하여 해적선을 만드는 과정을 모두 담당하기보단, 블럭을 만드는 공정과 해적선을 조립하는 공정을 분리하면 만들어진 블럭으로 비단 해적선 뿐만 아니라 상어나 아파트 등의 다른 블럭 작품들도 만들 수 있게 된다
자바에서 사용하는 빌더 패턴은 정의가 약간 다른데, 생성자에 프로퍼티 초기화를 위한 인자를 전달하지 않고 별도의 메서드들로 프로퍼티를 초기화해주는 패턴을 뜻한다
아파트를 짓는다고 할 때, 방의 개수와 창문의 개수 등은 아파트마다 유동적이고, 드레스룸 같은 경우는 필수적인 요소가 아니므로 굳이 빼 버려도 상관이 없다
그렇다면 아파트를 만들 때 처음부터 이러한 요소들 (방 개수, 창문 개수 등…) 을 다 설계하는 것이 아니라, 일단 아파트 기본 틀을 만들어 놓고 상세한 요소들을 따로 추가해주는 방식이 여러 채의 서로 다른 아파트를 지을 때 효율적일 것이다
이처럼 빌더 패턴은 생성자는 기본 틀만 정의하고, 내부 프로퍼티는 별도의 함수를 호출하여 초기화함으로써 코드의 가독성을 높이고 불필요한 중복 작업을 제거하는 방식이다
- 원형 패턴 (Prototype)
예시가 되는 원형 (circular 아님) 인스턴스를 하나 준비하고, 다른 객체들을 생성할 때 이 원형 인스턴스를 복사해서 만드는 패턴이다
블럭 견본 하나를 3D 프린터로 깎고, 이 블럭을 실리콘 틀 등을 이용해서 본을 뜬 후 계속 복제하는 방식이다
원형 복제 시 얕은 복사가 이루어지면 모든 인스턴스가 같은 요소를 공유하게 된다는 치명적인 문제가 발생할 수 있으므로, 깊은 복사를 하는 것이 중요하다
구조 패턴
클래스 또는 객체 등 객체지향에서의 요소들을 조합하는 방식을 정의한 패턴이다
특정 로직을 구현하고자 할 때 레고 블럭을 어떻게 쌓아야 적합할 지 결정하는 패턴이라고 생각하면 쉽겠다
- 브리지 패턴 (Bridge)
객체의 동작을 처리하는 ‘구현부’ 와 객체의 확장을 용이하게 하는 ‘추상부' 를 분리하는 패턴이다
구현부와 추상부란 말이 좀 어려운데, 구현부는 실제 기능이 동작하는 로직 부분, 추상부는 이 클래스의 객체들이 갖는 공통적인 특성을 모아둔 부분이라고 할 수 있다
로봇을 만들 때, 우선 추상부에서 “이동한다", “충전한다", “사물을 인식한다" 등 추상적인 행동들을 정의한다
그리고 구현부에서는 어떤 방식으로 이동하고, 충전하고, 사물을 인식할 지 정의하는 것이다
단순히 바퀴를 이용해서 전진하는 방법도 있겠지만, 사람이나 동물과 비슷한 로봇이라면 다리를 이용해서 걸을 것이고 비행형 로봇이라면 제트 엔진이나 프로펠러를 이용하는 방법도 있을 것이다
이렇게 구현부와 추상부를 분리함으로써 두 부분을 독립적으로 변형시켜 클래스를 발전시켜 나갈 수 있다
- 데코레이터 패턴 (Decorator)
기능 확장을 할 때, 새로운 서브클래스를 추가하기보단 데코레이터 객체를 이용하여 기존 객체에 추가 속성과 책임을 부여하는 방식이다
처음 젓가락질을 시도하는 어린이들은 조작을 많이 어려워하는데, 이를 위한 어린이용 젓가락을 새로 만들어내기보단 젓가락 보조 도구를 기존의 젓가락에 끼워 어린이들이 손쉽게 젓가락질을 연습할 수 있도록 하는 것과 비슷하다
- 적응자 패턴 (Adaptor)
이미 있는 클래스 인터페이스를 사용하고는 싶은데 클래스를 적용시키고 싶은 객체와 해당 클래스가 호환이 되지 않을 때, 중간에 적응자 (어답터) 를 추가하여 기존 클래스 인터페이스를 호환되는 인터페이스로 변환하는 패턴이다
승용차에 반려견을 태우고 싶지만 의자와 안전띠의 형태가 사람의 체형에 맞춰져 나와 반려견에게 그대로 적용시키기엔 무리가 있으므로, 반려견 전용 의자를 부착하거나 케이지에 넣어 이동하는 것과 비슷하다
- 컴포지트 패턴 (Composite)
객체를 트리 구조로 구성하고, 어떤 형식의 인터페이스이던간에 별도의 구별 없이 똑같이 사용할 수 있도록 하는 패턴이다
하나의 디렉토리는 하위 디렉토리와 파일로 구성되어 있고 근본적으로 디렉토리는 또 다른 여러 개의 파일과 디렉토리로 이루어진 복합 객체이지만, 클라이언트에서는 하나의 디렉토리 안에 존재한다면 전부 단일 객체처럼 평등하게 취급하는 것이다
- 파사드 패턴 (Facade)
작은 프로그램 구현 시에는 느끼기 어렵지만, 규모가 큰 프로젝트에서는 클래스들의 종류가 매우 많고 그 역할도 천차만별이다
이 클래스들을 직접 사용하기보단 상위에 창구 인터페이스를 하나 정의하여 내부 클래스에 접근할 때마다 창구에 행동을 요구하는 방식으로, 하위 인터페이스 접근이 용이하다는 장점이 있다
TV 리모컨을 생각하면 매우 쉽다
- 플라이웨이트 패턴 (Flyweignt)
유사한 객체들 사이에서 가능한 많은 정보를 공유하여 사용하도록 하는 디자인 패턴으로, 메모리 용량을 최대한 줄일 수 있다
각 객체들의 고유 프로퍼티 또한 외부 자료 구조에 저장하여 해당 자료 구조를 공유하는 방식으로 설계한다
프로젝트를 진행할 때 팀원 한명 한명마다 5TB의 저장소를 지급하고 각자 따로 프로젝트 진행 상황을 저장하게 하는 것보다, 5TB짜리 공유 저장소를 두고 적절한 폴더구조로 자료나 코드 등을 저장한 뒤 모든 팀원이 해당 저장소에 접근하여 작업하게 하는 것이 메모리 절약이 되는 것과 같은 이치이다
- 프록시 패턴 (Proxy)
파사드 패턴과 비슷하게, 객체를 직접 참조하기보단 대리인 (프록시) 을 두어 대신 참조하게 한다
참조하고자 하는 객체가 메모리 상에 존재하지 않을 경우에도 프록시 객체를 통해 기본 동작을 수행하거나, 시간이 매우 오래 걸리는 작업을 프록시에게 위임하는 방식으로 사용가능하다
높으신 분 (사장님 등…) 에게 말을 전하고 싶을 때, 그 사람이 부재중일 가능성이 있으므로 비서를 통해 이야기를 전달해 놓는 것과 같다
높으신 분이 업무를 처리하는 속도가 매우 느릴 경우에도 그 시간 내내 마냥 기다리는 것보단 비서에게 업무를 위임한 후 나는 다른 작업을 하고 있어도 된다
행동 패턴
객체들의 상호작용 및 행동, 역할과 책임 분배에 관한 패턴이다
이 블럭은 로직에서 무엇을 담당하고, 이 블럭은 무엇을 담당하고… 를 정의하는 패턴이다
- 인터프리터 패턴 (Interpreter)
언어 / 문법적 규칙을 해석하는 번역자 (인터프리터) 를 두고, 특정 형식의 로직이나 문자열 등을 해석하여 적합한 행동을 취하는 형태로 기능을 구현한다
정규표현식, SQL 문법 등이 대표적인 예시인데, SELECT FROM
이나 INSERT INTO
등 사전에 합의된 문법을 조합하여 명령을 만들고 입력하면 문법에 알맞는 적절한 동작을 취하여 결과물을 내보낸다
- 템플릿 메서드 패턴 (Template Method)
몇몇 로직을 서브 클래스로 캡슐화하여 중복 로직을 최소화하면서 클래스를 구조화하는 디자인 패턴
자동차와 오토바이는 ‘전진', ‘후진', ‘방향 이동' 등의 공통적인 기능을 갖고 있으므로, 전진 / 후진 등에 대한 공통 기능을 담은 추상적인 설계도를 만들고 이를 재정의하여 자동차와 오토바이를 만들 수 있다
이처럼 템플릿 메서드 패턴은 기본 골격은 동일하게 잡되 내부 요소를 추가하거나 제거하는 식으로 인터페이스를 확장하는 것을 목표로 한다
- 책임 연쇄 패턴 (Chain of Responsibility)
명령 객체와 여러 개의 처리 객체를 가지며, 각각의 처리 객체는 명령 객체에서의 특정 로직을 처리한다
앞의 처리 객체에서 처리할 수 없는 로직은 뒤의 객체로 넘겨지며, 마치 사슬과 같은 형태로 처리 객체가 연쇄적으로 호출되어 명령을 해결한다
빵 하나를 구울 때 재료 섞는 담당, 발효 및 휴지 담당, 모양 잡기 담당, 오븐에 굽는 담당을 각각 배정하면 처음에 재료 담당이 재료들을 한데 모아 섞고, 발효는 본인의 담당이 아니므로 발효 담당에게 위임하고… 하는 식으로 네 명의 담당자가 반죽을 이어받아 각자 맡은 업무를 수행하여 빵을 만들어 낸다
하나의 명령을 여러 처리 객체가 처리하므로 결합이 느슨하고 확장성이 높다
- 커맨드 패턴 (Command)
요청을 캡슐화하여 인자로 넘겨주는 형식의 패턴이다
클래스에 기능을 많이 담을 수록 관련 메서드가 기하급수적으로 늘어나고, 덩달아 나눠야 할 분기 또한 많아져 결과적으로 코드의 복잡함을 낳는다
따라서 클래스에 직접 기능을 담지 않고, 하나의 메서드를 두어 그 메서드에 명령을 인자 형태로 넘겨주면 명령의 객체가 누구이던지간에 유연하게 명령을 수행할 수 있도록 하는 것이다
예를 들어 고깃집에 돼지고기, 소고기, 닭고기를 판다고 할 때, 돼지고기를 굽는 사람과 소고기를 굽는 사람, 닭고기를 굽는 사람을 다 따로 두면 고깃집이 상당히 복잡해진다
고깃집에 고기 굽는 사람 한 명을 두고, 그 사람에게 돼지고기를 인자로 넘겨주면 돼지고기를 굽고, 소고기를 넘겨주면 소고기를 굽도록 해주는 것이 커맨드 패턴이다
- 반복자 패턴 (Iterator)
컨테이너 (저장 공간 = 자료구조) 와 그 컨테이너를 동작시키는 알고리즘을 분리하고, 반복자 객체를 통해 컨테이너 내부에 접근하여 각각의 자료들을 조회하고, 알고리즘을 적용하는 방식의 패턴이다
알고리즘을 아예 별도로 분리함으로써 관심사 분리를 통해 클래스가 간결해지고, 컨테이너 내부의 구현체를 노출시키지 않고도 내부 요소 각각에 접근할 수 있다
서점에서 책을 살 때는 책의 주제나 출판사, 저자 등을 알고 있어야 하고, CD/DVD를 사고 싶다면 가수나 장르를 알고 있어야 하는 것처럼 내부의 구현체를 알아야 검색을 하거나 리뷰를 적을 텐데, 요즘은 제목 하나만 알아도 검색을 통해 위치를 파악하는 것이 가능한 것과 비슷(?) 하다
- 중재자 패턴 (Mediator)
모든 객체의 행동을 수행하기 전에는 중재자를 거쳐야 하고, 중재자가 로직을 수행할 지 말지 결정한다
각 객체들은 중재자만을 바라보며, 중재자를 통해서만 다른 객체의 메서드를 호출하거나 호출을 받아 특정 행동을 수행한다
관찰자, 파사드 패턴과 꽤나 비슷한데, 중재자 패턴은 모든 객체가 중재자와만 연결되어 중재자가 요청받은 업무를 적절한 객체를 선택하여 호출하는 방식으로 동작한다
감옥의 수감자 전원은 서로를 알지 못하고, 서로 마주칠 수 없으며, 모든 행동은 간수의 판단과 동행 하에만 이루어지는 것을 생각하면 될 듯 하다
- 관찰자 패턴 (Observer)
관찰자 (옵저버) 가 모든 객체들을 감시하고 있다가, 어떤 객체의 상태가 변할 때 그 객체와 관련된 모든 객체에게 “상태가 변했다!” 라고 통지하고, 객체들은 알림을 받으면 그에 따른 처리를 수행하는 형태의 패턴이다
발행-구독 패턴도 옵저버 패턴 중 하나이다
감옥에서 감시 담당 간수가 특정 영역의 죄수들을 감시하고 있다가, 이상행동을 하는 죄수가 나타날 경우 다른 간수들에게 연락을 보내 적절한 조치를 취하도록 하는 것과 같다
- 상태 패턴 (State)
객체의 내부 상태가 어떤지에 따라 스스로 내부 동작을 변경할 수 있도록 하는 패턴이다
객체 각각이 사람이라고 생각하면, 기분이 좋을 때 표정도 밝으며 더 적극적으로 행동하고, 기분이 좋지 않을 때 꿍한 표정으로 소극적으로 행동하는 것이다
- 전략 패턴 (Strategy)
객체가 취할 수 있는 모든 행동을 각각 전략 클래스로 캡슐화하고, 객체가 할 수 있는 행동을 유동적으로 추가 및 삭제, 수정할 수 있는 패턴이다
어떠한 로봇에게 ‘사진 찍기', ‘춤추기', ‘쪼그려 앉기' 라는 기능이 있고, 특정 휴대전화도 같은 로직의 ‘사진 찍기' 와 ‘인터넷 접속하기' 기능이 있다고 하자
이때 우리는 ‘사진 찍기' 기능을 ‘동영상 찍기’ 로 업그레이드하고자 한다
로봇을 분해해서 내부 소프트웨어를 업그레이드하는 것보단 ‘사진 찍기' 기능만 패치해주는 식으로 효율적으로 대응하면 휴대전화 패치도 같이 대응할 수 있으므로 로직의 중복이 줄어들고 확장이 용이하다
- 메멘토 패턴 (Memento)
메멘토는 기억이라는 뜻이며, 메멘토 패턴은 각 객체의 상태 정보를 저장하고 원할 때 특정 시점의 상태로 복원하는 매커니즘을 갖는다
메멘토 패턴은 오리지네이터 객체, 케어테이커 객체, 메멘토 객체의 3개로 구성된다
오리지네이터 객체가 상태를 갖는 객체이고, 케어테이커는 메멘토 객체를 관리하며 저장 또는 복원을 요구받았을 때 그에 따른 행동을 취하며, 메멘토 객체는 상태 정보를 갖고 있는 인스턴스이다
오리지네이터는 자신의 상태 정보를 저장하는 메멘토를 추가할 수 있고, 추가된 메멘토는 케어테이커에 저장되며, 오리지네이터 외의 객체는 케어테이커에 접근할 수 없다
또한 케어테이커는 메멘토를 저장하고 오리지네이터에게 전달하는 역할만 하며, 내부 메멘토를 조작할 수 없다
오리지네이터는 특정 시점의 데이터를 불러오기 위해 케어테이커에서 메멘토를 꺼내 사용한다
오리지네이터는 관리자, 메멘토는 비디오테이프, 케어테이커는 비디오테이프를 꽂아두는 진열장이라고 생각하면 된다
- 방문자 패턴 (Visitor)
객체 구조에서 알고리즘 로직을 분리시키는 패턴으로, 개방 - 폐쇄 원칙을 충실히 따른다 (객체에 새로운 기능을 추가 (확장) 하기는 쉽지만, 객체의 기능을 수정하기는 힘들어야 한다)
이렇게 분리된 알고리즘 로직은 방문자 객체가 소유하며, 방문자 객체가 다른 객체를 ‘방문' 할 때 로직이 수행되는 방식이다
새로운 기능을 추가하고 싶다면 간단하게 방문자 객체를 늘리기만 하면 되기 때문에 개방 - 폐쇄 원칙에 부합한다
컴퓨터는 단지 CD를 읽어들여 내부 로직을 수행하는 기능만 가지고 있다고 할 때, 모든 별도 기능들은 CD에 저장해놓은 뒤 CD를 컴퓨터에 꽂으면 (방문하면) CD에 저장된 로직이 컴퓨터에 의해 수행되는 패턴이라고 할 수 있다
디자인 패턴 종류 - 소프트웨어 설계 방법론 (아키텍쳐)
소프트웨어 아키텍쳐는 소프트웨어를 구성하는 요소 간의 관계를 정의한다
소프트웨어 아키텍쳐는 조금 더 넓은 범위를 바라보며, 요소들을 어떻게 정리하고, 책임을 분리하여 연결해야 개발이 용이할지를 다룬다
마찬가지로 소프트웨어 전반을 설계할 때의 노하우를 재사용하기 좋은 패턴의 형태로 정의했다는 점에서 디자인 패턴 중 하나라고 볼 수 있겠다
위키피디아에서는 GoF와 아키텍쳐 패턴을 뭉뚱그려 ‘소프트웨어 디자인 패턴' 이라고 이야기하고 있다… 만 검색해보면 대부분의 자료에서 사실상 ‘디자인 패턴' 이 GoF 패턴을 가리키는 말로 사용되고 있는 것 같다
계층화 패턴
Layered Pattern
서로 다른 업무를 수행하는 n개의 계층으로 모듈을 분리하는 방식이다
주로 사용되는 4개의 계층은
- UI 계층
- 프리젠테이션 계층이라고도 불린다
- 사용자에게 보여지는 부분을 담당한다
- 어플리케이션 계층
- 서비스 계층이라고도 불린다
- 사용자와의 상호작용을 담당하고, UI 계층과 비즈니스 로직 계층을 연결한다
- 비즈니스 로직 계층
- 도메인 계층이라고도 불린다
- 시스템 전반의 핵심 로직을 담당한다
- 데이터 접근 계층
- 영속 계층이라고도 불린다
- 데이터베이스에 접근하고 데이터를 다룬다
대표적인 예시로 OSI 7계층이 있다
클라이언트 - 서버 패턴
Client - Server
하나의 서버와 다수의 클라이언트가 통신하는 형태의 패턴으로, 서로 독립적으로 동작하다가 요청과 응답을 통해 통신한다
웹 서비스 덕에 비교적 익숙한 형태의 패턴으로, 메일도 같은 방식이라고 한다
파이프 - 필터 패턴
Pipe - Filter
데이터가 파이프를 통해 흘러가면서 필터에서 정제 및 처리되는 방식이다
쉘이 파이프 - 필터 패턴으로 데이터를 처리한다
브로커 패턴
Broker
브로커 컴포넌트가 나머지 컴포넌트들간 통신을 조정하는 형태의 패턴으로, 사용자와의 상호작용 또한 브로커를 통해 이루어진다
나머지 컴포넌트들은 브로커를 통해서만 통신한다는 점에서 GoF의 중재자나 관찰자 패턴과 쫌 비슷하다
마스터 - 슬레이브 패턴
Master - Slave
마스터 부분과 슬레이브 부분으로 구성되며, 마스터 컴포넌트는 슬레이브 컴포넌트에게 업무를 분담시키고 그 결과값을 취합하는 방식으로 동작한다
병렬 컴퓨팅 시스템이 이 방식으로 동작한다고 한다
피어 투 피어 패턴
Peer - to - Peer
하나의 컴포넌트가 하나의 피어가 되며, 피어 각각은 클라이언트의 역할을 맡아 서버에 데이터를 요청할 수도 있고 서버의 역할을 맡아 각각의 클라이언트에게 데이터와 서비스를 제공할 수도 있다
각자의 역할이 유동적으로 변할 수 있다는 특이점이 있다
파일 공유 서비스가 P2P 방식으로 이루어진다
이벤트 버스 패턴
이벤트가 발생할 때마다 그 이벤트를 구독하는 서비스들이 사전 정의된 행동을 취하는 방식의 패턴이다
이벤트 소스, 이벤트 리스너, 채널, 이벤트 버스 4개의 구성요소로 이루어진다
- 이벤트 소스는 이벤트 버스를 통해 채널에 이벤트를 발행한다
- 이벤트 리스너는 채널을 구독하며, 이벤트가 발행될 때마다 알림을 받아 적합한 행동을 취한다
- 이벤트 채널은 이벤트 소스가 발행되는 위치이다
- 이벤트 버스는 이벤트 채널들을 관리한다
알림 서비스가 보통 이벤트 버스 패턴으로 동작한다 (특정 앱에서 알림이 발생 = 이벤트의 발행)
MVW
Model - View - Whatever
모델 - 뷰 - 뭐시기 라는 이름에서 알 수 있듯이 모델, 뷰는 공통으로 갖고 그 외에는 어떤 것이든 올 수 있는 패턴이다
MVC, MVP, MVVM 모두 MVW에 해당한다
- MVC
Model - View - Controller
모델 (데이터 처리), 뷰 (사용자에게 보여지는 UI), 컨트롤러 (사용자의 입력에 대한 적절한 처리) 세 개의 파트로 코드를 분 할하는 방법이다
- MVP
Model - View - Presenter
모델과 뷰는 MVC 패턴과 동일하고, 프리젠터는 모델을 통해 데이터를 가공하여 뷰에 넘겨주는, 일종의 전달자 역할을 한다
- MVVM
Model - View - View Model
모델과 뷰 말고도 뷰 모델이라는 부분을 별도로 가지며, 모델과 뷰는 MVC 패턴과 동일하고, 뷰 모델은 뷰를 표현하기 위한 데이터들을 처리하는 모델이다
블랙보드 패턴
해결 전략이 알려지지 않은 문제를 대응하기 위한 패턴으로, 명확한 해결 방법을 모르므로 부분적인 해결 방법을 서브시스템으로 모아 지식을 조합하는 방식이다
블랙보드, 지식 소스, 제어 컴포넌트로 구성되어 있다
- 블랙보드는 데이터 저장소로, 모든 컴포넌트가 접근하고 데이터를 추가할 수 있다
- 지식 소스는 특수한 문제를 해결하는 서브시스템이다
- 제어 컴포넌트는 블랙보드 내의 데이터 변경을 감지하고, 적합한 모듈을 선택하여 다음 동작을 결정한다
인공지능 구현이 블랙보드 패턴을 통해 이루어진다
인터프리터 패턴
특정한 언어 또는 특정 형식 (표현식) 으로 작성된 프로그램을 해석하고, 적절한 동작을 수행한다
SQL 등의 데이터베이스 쿼리와 정규표현식 등이 여기 포함된다
참고자료
[Design Pattern] 디자인 패턴 정의와 종류에 대하여
[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류
[Design Pattern] 컴퍼지트 패턴이란 - Heee's Development Blog
[디자인 패턴] 템플릿 메소드(Template Method) 패턴이란?
책임 연쇄 패턴(chain-of-responsibility pattern)
[디자인패턴] 커맨드 패턴 ( Command Pattern )
[디자인패턴] 반복자 패턴 (Iterator Pattern)
[디자인패턴] 옵저버 패턴 (Observer Pattern) 아주 간단하게 정리해보기
[디자인패턴] 전략 패턴 ( Strategy Pattern )
'이론적인 부분들 > 기타' 카테고리의 다른 글
도메인 주도 설계와 아키텍처 (0) | 2023.11.10 |
---|---|
블로킹, 논블로킹, 동기, 비동기 (0) | 2023.09.18 |