본문 바로가기
Computer Science

[CS] 객체지향 디자인패턴

by DuncanKim 2022. 7. 7.
728x90

[CS] 객체지향 디자인패턴

 

 

Java의 객체 지향을 공부하다 보면, 어떻게 객체 지향 프로그램을 잘 '설계'하고 개발해나갈 것인가를 고민하게 된다.

이 때 우리에게 답을 주는 것이 "객체 지향 디자인패턴"이다.

 

그림을 그리는 것 같은 디자인도 사람의 영감을 받아 어떤 디자인이나 기호, 캐릭터를 만들어내는 것 같지만, 그림을 그리는 디자인에도 일정한 패턴이 존재한다. 다른 말로 하면, '성공의 공식' 같은 것....!

 

마찬가지로 소프트웨어 설계 부분에서도 일정한 성공의 공식 패턴이 있다. 프로그램을 설계할 때 자주 발생하는 문제들을 피하기 위해 사용되는 패턴을 의미한다. 디자인 패턴을 참고하여 개발을 할 경우 개발의 효율성과 유지보수성, 운용성 등의 품질이 높아지며, 프로그램의 최적화에 도움이 된다. 

 

 

구성요소

디자인 패턴은 패턴의 이름, 문제 및 배경, 솔루션, 사례, 결과, 샘플 코드로 구성이 되어 있다.

 

패턴의 이름 : 디자인 패턴을 부를 때 사용하는 이름과 디자인 패턴의 유형

문제 및 배경 : 디자인 패턴이 사용되는 분야 또는 배경, 해결하는 문제

솔루션 : 디자인 패턴을 이루는 요소들, 관계, 협동 과정

사례 : 디자인 패턴의 간단한 적용 사례

결과 : 디자인 패턴을 사용하면 얻게 되는 이점이나 영향

샘플 코드 : 디자인 패턴이 적용된 원시 코드

 

 

디자인 패턴의 유형

목적과 범위에 따라 구분된다.


목적

 

생성 : 객체 인스턴스 생성에 관여, 클래스 정의와 객체 생성 방식을 구조화, 캡슐화를 수행하는 패턴

구조 : 더 큰 구조 형성 목적으로 클래스나 객체의 조합을 다루는 패턴

행위 : 클래스나 객체들이 상호 작용하는 방법과 역할 분담을 다루는 패턴


범위

 

클래스 : 클래스 간 관련성, 상속 관계를 다루는 패턴. 컴파일 타임에 정적으로 결정된다.

객체 : 객체 간 관련성을 다루는 패턴으로 런타임에 동적으로 결정된다.


 

 

디자인 패턴의 종류

 

1) 목적 - 생성 패턴

 

(1) Builder Pattern

 

복잡한 인스턴스를 조립하여 만드는 구조로, 복합 객체를 생성할 때 객체를 생성하는 방법과 객체를 구현하는 방법을 분리함으로써 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있다. '생성과 표기를 분리'하는 점이 특징이다.

 

 

(2) Singleton Pattern

 

어떤 클래스에서 객체가, 해당 프로세스에서 딱 하나만 만들어져 있어야 할 때가 있다. 

사용자가 앱을 사용하는데, 세팅에서 다크모드를 설정해놓으면 다른 페이지로 이동하더라도 다크모드가 그대로 유지되어 있어야 한다. 어떤 페이지에 있든 이 세팅을 관리하는 객체는 반드시 같은 것을 사용해야 한다. 이 객체는 하나만 만들어져있도록 해야 한다. 이런 경우 싱글톤 패턴을 사용한다.

 

static 정적 변수를 활용해서 하나의 클래스에서 구현이 되면, 다른 클래스에서 객체를 생성하지 않아도 그 값이 자동으로 적용되어 있게끔 하는 기능을 구현할 수 있다. 

 

static만 사용하면 될 것 같기도 한데 싱글톤을 쓰는 이유는 인터페이스의 사용이나 lazy loading 등 할 수 있는 일이 더 많기 때문에 이 패턴을 사용한다. 

 

싱글톤으로 작성하면 멀티쓰레드 환경 등에서 오류가 발생할 소지가 있다. 안전하게 싱글톤을 사용할 수 있는 방법 들을 언어마다 검색을 해보는 것이 좋다. 

 

 

(3) Factory-method Pattern

 

아파서 약국에 왔는데, 약사가 여러 가지 약을 놓고 설명을 하고 있다면? 그냥 아픈 것을 알려주면 적절하게 약을 주는 약사가 좋을 것이다.

 

이처럼, 기존에 만들어진 라이브러리 클래스들을 활용할 때, 다른 클래스를 고치는 것 없이 새로운 클래스를 개발하는 것이 좋을 때가 있다. 특정 종류의 기능들에 사용될 수 있는 클래스들의 종류가 많고 복잡할 때, 개발자측에서는 이를 다 알 필요 없이 약국에서 약사에게 증상을 이야기하듯 사용할 객체의 조건들만 인자로 넘겨주면 이에 적절한 클래스를 찾아 객체로 생성해 주는 ‘중간’ 단계의 클래스(팩토리)를 구현해놓는 것이다. 

 

 

(4) abstract-factory Pattern

 

추상 팩토리 메소드 패턴은 팩토리 메소드 패턴에 한 단계 더 추상화가 입혀진 패턴이다. 특정 객체들을 찍어낼 팩토리도 여러 종류로 건설할 수 있도록 하는 것이다. 팩토리도 상황에 따라 더 만들 수 있도록 허용해주는 것이다. 

 

 

 

2) 목적 - 구조 패턴

 

 

(1) Facade 

 

객체지향 언어를 사용하는 프로그래밍을 하는 개발자면 사용해본 적이 있는 가장 기본적인 패턴.

 

하나의 클래스 안에 메소드를 전부 구현해놓으면 복잡해지는 경우가 있다. 그럴 때, 여러 클래스로 나누어서 객체들을 복합적으로 사용하는 것이다. 객체 지향에 입문할 때 배우는, 게임 캐릭터 생성의 과정을 클래스로 구현하는 것이 대표적이다. 생성의 과정을 구현한 클래스는 모두 숨기고, 메인 클래스 ‘생성’하는 클래스를 한 번 불러오면 되게끔 만드는 구조를 Facade Pattern이라고 한다.

 

 

(2) Decorator Pattern

 

특정 클래스의 객체들이 할 수 있는 일을 여러 가지 두고 각 객체마다 사용자가 원하는대로 골라 시키거나 기능들을 필요에 따라 정착할 수 있도록 할 때, 데코레이터 패턴이 사용된다. 객체가 생성자 변수로 다른 객체 안에 들어감으로써 그 실행하는 메소드의 행동이 추가되도록 하는 것이 데코레이터 패턴이다. 

 

 

(3) Composite Pattern

 

컴퓨터의 폴더 시스템과 비슷하다. 폴더 안에는 파일들도 들어갈 수 있지만, 또 다른 폴더들도 들어갈 수 있고, 해당 폴더도 다른 폴더 안에 포함될 수 있다. 폴더와 파일은 엄연히 다른 종류지만, 둘 다 이름 바꾸기, 용량 구하기 삭제하기 등의 명령을 받을 수 있다. 

 

포함하는 것들과 포함되는 것들이 같은 방식으로 다뤄질 수 있도록 할 때, 컴포지트 패턴이 사용된다. 

 

 

(4) Adapter Pattern

 

인터페이스가 서로 다른 객체들이 같은 형식 아래 작동할 수 있도록 하는 역할을 한다. 어떤 방식이 다른 인터페이스를 도입하였을 때, 그 인터페이스에게 기존의 일의 방식을 알아들을 수 있도록 클래스, 메소드를 생성해서 연결시켜주는 것이다.

 

 

(5) Proxy Pattern

 

대리인 패턴. 객체를 여러 개 생성되기에는 부담되는 클래스들이 있다. 그럴 때 그 클래스를 대리할 수 있는 다른 클래스를 따로 두어서 가벼운 일은 대리 클래스가 수행하고, 무거운 작업을 할 때, 본 클래스가 작동하는 방법이다. 

 

프리뷰를 보여주는 것을 동영상을 보여주는 클래스가 직접하지는 않아도 될 것이다. 이럴 때 프리뷰를 보여주는 클래스를 본 클래스의 기능 중 일부를 이전하여 구현해서 프리뷰를 보여달라는 요청이 왔을 때, 본 클래스를 부르지 않고 대리 클래스를 불러서 프리뷰를 보여주는 것이다. 

 

 

 

3) 목적 - 행위 패턴

 

(1) Mediator Pattern

 

거래처들에 서비스를 제공하는 회사에서 새로 제품을 출시하거나 약관을 변경하거나 할 때, 거래처들에 하나하나 연락을 돌릴 것이다. 회사 규모가 작을 때는 대표가 일일이 하지만, 커지면 아래의 직원들이 대신할 것이다. 어떤 클래스의 객체에서 특정 이벤트가 발생할 때마다 연결된 다른 클래스들에 알려야 하는 경우가 있다. 

 

특정 이벤트에 반응해서 관련된 다른 클래스들에 알려주는 일을 Mediator 역할을 하는 클래스에 전담을 시키는 것이다. 거래처에 전화를 돌릴 외주업체를 따로 두는 것이다. 

 

N대 N 관계가 형성되는데, 여러 클래스들의 관계가 특정 이벤트들을 중심으로 복잡하게 얽힌 설계에서 유용하게 사용될 수 있다. 

 

 

(2) Template-method Pattern

 

어떤 같은 형식을 지닌 특정 작업들의 세부 방식을 다양화하고자 할 때 사용하는 패턴이다.

 

대대로 전통 약과를 만드는 가문이 있다고 하자. 약과를 만드는데에는 반죽을 만드는 과정 반죽을 기름에 튀겨내는 과정 시럽을 바르는 과정이 있다. 이 세 과정을 어떻게 하느냐에 따라 다양한 결과물들이 만들어질 수 있다.

 

전략 패턴에서는 이 방식들을 각각 갈아끼워넣을 수 있는 모듈화된 형식으로 따로 만들었다. 이와 달리 템플릿 메소드 패턴은 다양화된 방식을 각각 자식 클래스들에서 오버라이딩하는 방식으로 구현하는 것이다.

 

객체지향의 단순한 상속일 뿐인데, 왜 패턴으로 따로 구분한 것인지 의문이 들 수 있다. 

 

템플릿 메소드에서 상속은 일정한 형식이 있다. 부모 클래스에 전반 과정을 수행하는 메인 메소드가 있고, 그 과정 가운데 세부 메소드가 있다. 메인 메소드를 호출하면 실행 중에 세부 메소드들이 호출되는 형태이다. 자식 과정에서는 그 세부 메소드를 오버라이딩 하는 것이다. 

 

약과 가문의 부모가 약과를 만드는 과정을 반죽, 튀기기, 시럽바르기로 정해놓았다. 전체 과정을 자식들이 바꿀 수는 없다. 이것이 달라지면 약과가 아니게 되는 것이다. 하지만 세부 과정 하나하나는 자식들이 자기 스타일로 개발해서 다양한 결과물 들을 만들어낼 수 있는 것이다. 

 

어떤 일을 수행하는 몇 가지 방법이 있는데, 전반 과정에 공통된 절차가 있을 때 코드를 효율적으로 짜기 위해 만들어진 패턴인 것이다. 

 

 

(3) State Pattern

 

전략패턴과 비슷한 패턴이다. 세부적으로 다른 점은 전략 패턴이 어떤 동일한 틀 안에 있는 특정 작업의 방식, 모드를 바꿔준다고 하면, state 패턴은 TV가 꺼져있을 때 누르면 켜지고 켜진 상태에서 다시 누르면 꺼지는 버튼처럼 특정 상태마다 다르게 할 일을 나아가서 그 상태들 자체를 그 상태마다 실행시 할 일과 함께 하나하나 모듈화해서 지정해둘 때 쓰인다.

 

전략패턴이 지정된 특정 메소드가 모듈화된 모드에 따라 다르게 실행되도록 하는 것이라면, state 패턴은 그 메소드가 실행될 때 모드도 전환되도록 하는 것이라고 볼 수 있다. 

 

 

(4) Strategy Pattern

 

전략 패턴. 검색을 할 수 있는 창에 어떤 버튼들이 있다고 하자. 그 버튼들은 선택된 모드를 지정할 수 있고, 선택된 버튼에 따라서 그 모드에 맞게 검색이 되도록 만드는 것을 구현하고 싶다면?

 

프로그램 실행 중 모드가 바뀔 때마다 검색이 이루어지는 방식, 즉 전략이 수정되는  것이다. 클릭하는 것에 따라 메소드가 어떤 것이 구현되는 지를 연결시키는 방법도 있겠지만, 프로젝트의 사이즈가 커지면 유지보수가 어려워지고 개발이 힘들어질 것이다. 그래서 모듈로 따로 분리해서 이 버튼들을 누를 때마다 검색버튼을 누를 때 실행될 검색 ‘모듈’을 갈아끼워주는 방식으로 코드를 짜는 것이다. 

 

옵션들마다의 행동들을 모듈화해서 독립적이고 상호 교체 가능하게 만드는 것이 전략패턴이다. 

 

 

(5) Command Pattern

 

커맨드 패턴도 전략 패턴과 일정부분 비슷하다. 근본적인 차이는 전략 패턴이 같은 일을 하되 그 알고리즘이나 방식이 갈아끼워지는 것이라면, 커맨드 패턴은 하는 일 ‘자체’가 다른 것이다. 

 

 

 

 

 

마치며

 

패턴의 설계구조만 머릿속으로 외우는 것이 아니라, 왜 만들어졌는지, 어떻게 활용이 가능한 지, 다른 패턴들과는 어떻게 어우러질 수 있는 지를 고민해야 한다. 

 

그렇게 되면 어떤 ‘패턴’을 사용한다는 의식 없이도 자연스럽게 패턴을 ‘활용’하고 있을 것이다. 

 

 

 

 

 

<참고>

정보처리기사 문제집(수제비)

얄팍한 코딩 사전

728x90

'Computer Science' 카테고리의 다른 글

[CS] 결합도와 응집도  (0) 2022.07.12
[CS] 테스트와 TDD  (0) 2022.07.09
[CS] 프로세스와 스레드  (0) 2022.07.06
[CS] 동기와 비동기의 개념 차이  (0) 2022.07.05
[C언어] call-by-reference vs call-by-value  (0) 2022.07.04

댓글