1. '디자인 패턴'이란?
디자인 패턴
디자인 패턴은 짧게 ‘객체지향 패러다임에서 더 좋은 코드란 무엇인가에 대한 고민의 결과’ 이다. 처음 이 개념은 Gang of Four 라는 4명의 프로그래머가 작성한 Design Patterns 책을 통해 세상밖으로 나오게 되었고, 이는 꼭 객체지향 프로그램이이 아니더라도 어느 언어에서도 통용될 수 있는 개발 방식들을 담고 있다.
다양한 패턴들을 찾아보기에 앞서, 모든 패턴들을 가로지는 원칙이자 패턴들이 생겨나게된 이유인 원칙 3가지를 알아보자면 다음과 같다.
- 중복의 최소화: 한 곳의 수정이 중복된 다른 코드에서의 수정을 동반해선 안된다.
- 코드 변경의 용이성: 코드는 항상 완벽하지 않고, 요구사항은 상시 바뀔 수 있다.
- 재사용성: 정돈된 코드는 전혀 다른 요구사항 및 비슷한 경우에도 그대로 사용이 가능해야한다.
디자인 패턴 3 카테고리
- Creational Patterns: 객체를 생성하는 방식
- 예) Singleton Pattern
- Structural Patterns: 객체 간 관계/구성하는 방식
- 예) Facade Pattern
- Behavioral Patterns: 객체 간 통신하는 방식
- 예) Observer Pattern
디자인 패턴은 위와 같이 3가지로 구분되어 각각 하위에 정말 다양한 개수의 패턴들이 위치한다. Refacotring Guru 페이지를 꼭 참조하길 바란다. 실제로 각 카테고리면 어떤 패턴이 존재하는지 볼 수 있고, 각 패턴별로 왜 생겨났고 어떤 개념인지 예제 코드들과 함께 앞으로 학습에 큰 도움이 될것이다.
주니어에겐 너무나도 먼 디자인 패턴
어떻게보면 큰 프로젝트를 진행해보지 않은 한 디자인 패턴을 모르는건 당연하다.
코드 한 줄의 변경이 다른 줄의 드라마틱한 변경을 일으킬만한 큰 프로젝트를 만들어본적이 없다.
교내 팀 프로젝트로 유저 폰으로부터 주변 AP 정보를 받아 라즈베리 파이에게 해당 고객 위치를 전달해주는 재실표시기를 만들었을때도, 2개의 서버를 만들었음에도 코드가 복잡할것이 없었기에 패턴을 적용하고 말고를 고민할 것이 없었다. 학생분들이라면 알테지만, 계획 및 구축 60%, 개발 20% 를 사용한 뒤 남은 20% 은 술마시기에 여념이 없다.
또한, 팀 프로젝트 코드는 다신 볼일이 없다. 요구사항 변경을 요청할 교수도 다른 프로젝트에 활용할 일도 없다.
‘디자인 패턴’은 면접을 준비할때면 언제든 반복해서 접하는 주제지만 결국엔 코드로써 직접 프로젝트를 개발해보며 직접 겪어야한다. 본 블로그에 정리하는 디자인 패턴 글들은 이해에 주안점을 두고, 까먹지 않도록 복습하기 쉬운 형태로 짧게 게시하려한다. 각 패턴들은 글로 명료하게 설명하려 노력하겠지만 클래스 다이어그램에 대한 이해가 있어야, 더 구현에 맞는 이해를 할 수 있다.
클래스 다이어그램 - 화살표
클래스 다이어그램은 이름 자체가 내포하고있듯 Class 간의 관계를 표로 간단하게 설명한 것으로 Class 개념이 존재하는 객체지향 프로그래밍(OOP)에서 프로젝트 구현을 위한 설계도라고 보면 된다. 클래스 다이어그램은 Class, Interface 이 2개의 관계를 설명할 Implements, Extends, Composition 딱 이 3개의 핵심적인 화살표만 살펴보면 끝이다. 코드는 java-like psudo code 기반으로 설명한다.
implements (구현)

앞선 객제지향 프로그래밍 글을 잘 읽었다면, 객체지향 코드는 Class 보다 Interface 를 단위로 구성된다는것을 이미 알고있을것이다. ‘어떤 작업을 수행할지’는 Interface 로 명시한 채, ‘어떻게 작업할지’는 원하는 구현 클래스(Concrete Class) 를 적재적소에 주입하여 실제 작업을 수행한다.
Class 를 처음 배운 뒤엔 아래와 같이 정의하여 사용한다.
ConcreteClass clazz = new ConcreteClass();
그 다음 Interface 와 다형성을 배우면 Interface 에 구현 클래스(Concrete Class)를 대입할 수 있다.
Interface clazz = new ConcreteClass();
실전에선 유연성을 위해서 구현 클래스가 무엇인지 바로 명시하지 않고, 원하는 구현 클래스를 동적으로 Interface 에 외부에서 주입해주는 방식을 사용한다. 구현 클래스를 주입하여 수행한다는 의미에서 Dependency Injection 혹은 코드 자신이 ‘구현 결정권’을 갖지 않는다는 이유로 이를 Inversion of Control 라 부르기도 한다. 이건 매우 깊게, Spring 글에서 따로 다루겠다.
private Interface clazz;
public void setClass(Interface clazz) {
this.clazz = clazz;
}
setClass(new ConcreteClass();
extends (상속)

상속은 Class 의 몇몇 함수에 대해 추가적인 기능을 확장하거나, 다른 로직의 함수로 대체할때 사용한다. 라떼는 상속을 Animal ‘부모 클래스’에 Cat, Dog 과 같은 ‘자식 클래스’로 배웠었는데, 이와 같은 상위-하위 개념은 앞서 배운 Implements 가 적절하고, 상속은 단순 기능/정의 확장에만 사용한다. 즉, 잘못배웠던 셈.
Cat 중에서 줄무늬가 있는 Cat 을 StripeCat 으로 정의할때, Cat 의 줄무늬를 제외한 모든 함수와 변수는 동일하다.
composite (구성, 합성)

Class 의 기능/정의 확장에 상속만 있는것은 아니다. 구성도 있다.
- 상속: 본래의 Class 를 확장하여 -> 새 Class 정의
- 구성: 본래의 Class 를 가져다가 변수로 사용하는 -> 새 Class 정의
왜 객제지향 프로그래밍에선 상속(extends)보다 구성(composite)을 사용하라고 할까? 다시 한번 복습하자면
- 상속(extends)은 ‘경직된 확장성’을 갖지만,
- interface 구현(implements)과 구성(composite)의 조합은 ‘개방적 확장성’을 갖기 때문이다.
위 구성 예시 그림을 아래와 같이 interface(implements)를 추가했더니, Cat 종을 더 잘 표현할 수 있게 되었다.

상위 개념인 ‘종’ 하위에 ‘줄무늬 종’, ‘검은 종’ 등을 다 처리할 수 있는 확장성을 갖게되었다. 바로 직전 상속(extends) 예시와 비교해보면, 자식 클래스였던 SpriteCatClass 가 갖고있던 Sprite 정보가 Cat 클래스 밖인 SpriteClass 로 이동되었다. 왜 Inversion of Control 라고 부르는지 알 수 있는 대목이다.

Cat 이 ‘줄무늬 종’뿐만 아니라 ‘오드아이’ 눈을 갖는다 가정해보자. 이 또한 interface(implements) 와 composite 을 통해 EyesClass 에 OddEyesClass 를 명시함으로써 Cat 은 이제 ‘종’과 ‘눈’에 확장성을 갖게 되었다.
이로써 앞서 배운 객제지향 프로그래밍의 제 1 원칙 “‘클래스-상속’보다 ‘인터페이스-구성’을 사용하라”와 제 2 원칙 “‘구현 클래스’보다 ‘인터페이스’로 구성하라”와 을 다시 한번 되새김질 할 수 있었다.
이제부턴 2 개의 원칙과 3 개의 화살표(구현, 상속, 구성)로 디자인 패턴들을 살펴보자.