객체지향 디자인 패턴 13가지

Ko Seoyoung·2021년 7월 24일
6
post-thumbnail

왜 공부할까?

요즘 프로젝트 구조에 대한 고민에 또 부딪히게 되었다. 어떤 네이밍을 가진 컴포넌트와 함수, 클래스를 만들지, 각 클래스들의 책임과 역할을 어떻게 분배해야 확장성 있는 라이브러리가 될지 등을 고민한다.

이때 객체지향은 꼭 자바와 같은 객체지향 프로그래밍 언어에서만 사용되는 개념이 아니다. 객체지향은 프로그래밍 방법론이다. 이는 프로그램을 ‘객체’라는 단위로 나누고, 각 객체의 역할에 대한 추상화, 캡슐화를 통해 더욱 쉽고 효율적인 프로그래밍을 가능하게 해준다.

앞으로의 프로젝트에서도 끊임없이 고민하게 될 구조 설계를 위해서 평소 이름만 들어봤던 여러 객체지향 디자인 패턴을 좀 더 정확히 알고 적용해 보면 좋을 것 같다는 생각이 들었고, 유튜버 얄팍한 코딩사전의 영상을 통해 다양한 객체지향 패턴에 대해 알게되었다. (* 본 포스팅은 영상 내용을 요약 작성함)


1. Singleton(싱글톤) 패턴

언제 사용하면 좋을까?

어떤 클래스의 객체가 프로세스 내에서 단 한 개만 만들어져야할 때

예: Setting을 관리하는 클래스 (다크모드를 설정하면, 모든 페이지에 다크모드가 적용되어야 함)

어떻게?

  • 생성자를 private으로 변경 -> 다른 클래스에서 new로 생성할 수 없게 된다

  • 클래스 안에 자신의 타입을 갖는 객체를 선언한다

  • static 아닌 변수는 객체가 생성될 때마다 동적공간에 메모리가 할당되지만, static 변수는 메모리가 지정된 정적 공간에 딱 하나만 존재한다 (컴파일 할 때부터)

  • static 메소드는 이미 메모리의 정적 공간에 자리를 차지하고 있으므로 해당 객체를 new로 생성하지 않아도 바로 불러 사용할 수 있다

그냥 정적 변수를 쓰지 왜 싱글톤을 쓸까?

인터페이스의 사용이나 lazy loading 등 싱글톤으로 할수 있는 것들이 더 많기 때문에

2. Strategy(전략) 패턴

언제 사용하면 좋을까?

프로그램 실행 중 선택된 모드에 따라 실행되는 방식, 즉 전략이 결정될 때

예: 전체, 이미지, 뉴스, 지도 선택/검색 클릭 -> 검색할 때 선택한 검색 모드에 따라 각각 다른 검색 방식이 실행됨

어떻게?

- 위 예시를 전략패턴을 적용하지 않고 구현한다면?

SearchButton을 눌렀을 때 onClick함수에서 조건문을 사용해, 검색 모드에 따라 동작을 다르게 실행한다. 수정사항이 생기거나 모드가 추가되면 onClick은 한없이 길어짐 -> 소프트웨어가 커지고 복잡해 질수록 코드 분석,관리가 어려워지는 우아하지 않은 설계 && 클래스마다 역할지정을 뚜렷히 하여 모듈화 된 소프트웨어를 구축하는 객체지향 철학에 어긋남

- 전략패턴을 사용하면?

모드마다의 동작 하나하나를 모듈로 따로 분리하여 검색버튼을 누를 때 실행될 검색 모듈을 갈아끼워주는 방식 사용

SearchStrategy 인터페이스를 만들고 onClick에서 searchStrategy의 search() 함수만 호출한다. setter를 이용해 searchStrategy 를 SearchStategy 인터페이스를 상속 받은 다른 검색 전략으로 갈아끼울 수 있다.

3. State 패턴

언제 사용하면 좋을까?

State 패턴의 설계 구조는 strategy와 얼핏 비슷하다

그럼 차이점은?

  • Strategy 패턴: 어떤 동일한 틀 안에 있는 특정 작업의 방식, 모드를 바꿔줄 떄

  • State 패턴: 특정 상태마다 다르게 할 일을 하나하나 모듈화해서 지정해 둘 때 쓰임

예: TV 전원 On/Off 스위치

정리: Strategy 패턴이, 지정된 특정 메소드가 모듈화된 모드에 따라 다르게 실행되도록 하는 거라면 State 패턴은 메소드가 실행될 때 그 모드도 전환 된다고 이해하면 된다

4. Command 패턴

전략 패턴은 같은 일을 하되 (ex) 검색), 알고리즘이나 방식이 갈아끼워짐

커맨드 패턴은 그 하는 일 자체가 다른 것!

-> 작성하는 방식이 다양함

5. Adapter 패턴

형식이 다른 둘 사이를 연결 -> 이 둘이 호환될 수 있게 해줌

객체 지향에서는 어답터는 보통 인터페이스가 서로 다른 객체들이 같은 형식 아래 작동할 수 있도록 하는 역할을 함

예: Strategy 패턴 예제 처럼 SearchButton 모드에 동영상 모드가 추가되었는데, 완전 다른 구조의 다른 라이브러리 코드라면? (다른 전략들 처럼 단순히 search()하는 것이아니라, FindAlgorithm에서 find(true)로 호출해야함)

-> SearchFindAdapter라는 클래스를 지정하고 FindAlgorithm 객체를 생성자로 넘겨줌, 어댑터 안의 search 메소드를 실행하면 find 메소드가 실행되도록 한다

6. Proxy 패턴 (대리인 패턴)

언제 사용하면 좋을까?

회사 대표가 몸소 등장해야하는 일은 그만큼 중요도가 있고 권한이 필요한 일 나머지 잔일은 비서가 처리한다.

프로그래밍에서 사용하는 일 중 중대한 일(인터넷에서 받아와야 해서 시간이 걸리거나, 메모리를 많이 차지하는 일 등)들은 객체로 여럿 생성해서 사용하기 부담이 될 수 있다. 이 때, 클래스의 Proxy를 만들어 비서 같은 이 클래스가 가벼운 역할들을 하도록 만들고, 중요한 일이 있을 때만 실제 클래스를 만들어 무거운 일을 돌린다.

어떻게?

예: 유튜브 영상 목록에서 썸네일에 마우스 오버시 프리뷰가 재생되는 기능 - 영상을 받아와야하므로 무거운 작업

이때 목록에서 제목도 보여줘야하는데, 이런 가벼운 작업은 ProxyThumbnail 클래스에서 하게 하고 ProxyThumbnail에서 showPreviw함수를 호출할 때 그제서야 RealThumbnail 객체를 생성하여 무거운 작업을 진행한다.

7. Facade 패턴

언제 사용하면 좋을까?

facade는 프랑스어로 외벽을 뜻하는 말.

한 작업에서 여러 클래스를 생성하여 복잡한 작업을 해야할 때, 이 과정들을 외벽 뒤로 숨겨 추상화 할 때 사용된다. (이미 자주 사용해본 적이 있을 패턴)

8. Template-method 패턴

언제 사용하면 좋을까?

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

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

어떻게?

부모 클래스에 전반 과정을 수행하는 메인 메소드가 있고, 그 과정 가운데 세부 메소드가 있음

자식 클래스는 과정을 수행하는 세부 메소드를 오버라이딩한다.

(예(비유): 약과 가문의 약과 만드는 방법은 1. 반죽 2. 튀기기 3. 즙청 인데, 이 큰틀을 자식들이 변경할 수는 없다. 그럼 약과가 아니게 됨. 대신 세부과정을 변경 가능)

예: 지도 클래스(MapView)에서 지도 init 방식 은 1. 맵서버 연결 2. 스크린에 맵 띄우기 3. 현재 위치로 옮기기 인데, MapView를 상속받은 자식 클래스인 네이버 지도와 카카오 지도에서는 이 각각의 세부 과정들이 다르게 동작 할 수 있다.

9. Decorator 패턴

언제 사용하면 좋을까?

특정 클래스의 객체들이 할 수 있는 일을 여러가지 두고 각 객체마다 사용자가 원하는 대로 골라 시키거나 기능들을 필요에 따라 장착할 수 있도록 할 때 사용된다.

예: 게임에서 전투기가 아이템을 먹을 때마다 공격시 발사하는 무기들이 추가됨

어떻게?

객체가 생성자 변수로 다른 객체 안에 들어감으로써 그 실행하는 메소드의 행동이 추가되도록 한다.

10. Factory-method 패턴

언제 사용하면 좋을까?

팩토리 메소드 패턴은 클래스의 인스턴스를 만드는 일을 서브 클래스에게 맡기는 것이다. (생성자를 호출하는 클래스)

(비유: 약국에 약 사러 갔는데, 약사가 알약 열몇개 들고와서 a약은 ㅇㅇ제약이고 성분으 뭘로 만들어졌고, b약은 어떻고, c약은 어떻고,, 알약에 대해 일일이 설명하면 머리아픔. 그냥 감기약 주세요 - a+b, 두통약 주세요 - c 이렇게 줘야지)

어떻게?

예: Component class를 상속하는 Button, Switch, Dropdown 클래스가 있을 때, new Button(), new Switch()와 같은 객체 생성 코드를 CompFactory class에 구현하여 CompFactory.getComp(컴포넌트 종류) 이런식으로 객체를 생성한다.

장점1: 팩토리 클래스를 사용하면 객체 생성부분(Button에 prop이 추가된다거나)이 변경되었을 때, 코드 여기저기에서 해당 객체의 생성코드를 변경하지 않고도 팩토리 클래서만 수정하면 된다!

장점2: 조건에 따라 객체를 생성해 가져오는 일을 팩토리 클래스에 위임해버림으로써, 개발자들은 각 Button, Switch 등 세부 클래스에 대해 알 필요가 없게 한다.

11. Abstract-factory 패턴

팩토리 메소드 패턴에 한단계 더 추상화가 입혀진 패턴

특정 객체들을 찍어낼 공장인 팩토리도 여러 종류로 건설할 수 있도록 함

예: 버튼, 스위치, 드랍다운이 여러 테마(light, dark)가 있다고 할 때, 테마마다 CompFactory 인터페이스를 상속한 LigthCompFactory와 DarkCompFactory 테마를 만든다

12. Mediator 패턴

언제 사용하면 좋을까?

특정 이벤트에 반응해서 관련된 다른 클래스들에 알려주는 일을 mediator 중재자 역할을 하는 클래스에 전담시키는것

비유: 거래처에 전화를 돌릴 외주업체를 따로 두는 것
회사(N) - 중재자 (외주업체 1) - 거래처(N) : 중재자를 중심으로 회사와 거래처가 N:N관계가 될 수 있다

여러 클래스들의 관계가 특정 이벤트들을 중심으로 복잡하게 얽힌 설계에서 유용하게 사용

13. Composite 패턴

언제 사용하면 좋을까?

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

예: 컴퓨터의 폴더 시스템과 유사함. 폴더와 파일은 다른 종류이지만, 둘다 이름 바꾸기, 용량구하기, 삭제하기 등의 명령을 받을 수 있음


출처영상

얄팍한 코딩사전: 객체지향 디자인패턴1
얄팍한 코딩사전: 객체지향 디자인패턴2
https://velog.io/@namezin/GoF-design-pattern

더 공부하면 좋을 자료

https://www.tutorialspoint.com/design_pattern/index.htm

profile
Web Frontend Developer 👩🏻‍💻 #React #Nextjs #ApolloClient

0개의 댓글