요즘 프로젝트 구조에 대한 고민에 또 부딪히게 되었다. 어떤 네이밍을 가진 컴포넌트와 함수, 클래스를 만들지, 각 클래스들의 책임과 역할을 어떻게 분배해야 확장성 있는 라이브러리가 될지 등을 고민한다.
이때 객체지향은 꼭 자바와 같은 객체지향 프로그래밍 언어에서만 사용되는 개념이 아니다. 객체지향은 프로그래밍 방법론이다. 이는 프로그램을 ‘객체’라는 단위로 나누고, 각 객체의 역할에 대한 추상화, 캡슐화를 통해 더욱 쉽고 효율적인 프로그래밍을 가능하게 해준다.
앞으로의 프로젝트에서도 끊임없이 고민하게 될 구조 설계를 위해서 평소 이름만 들어봤던 여러 객체지향 디자인 패턴을 좀 더 정확히 알고 적용해 보면 좋을 것 같다는 생각이 들었고, 유튜버 얄팍한 코딩사전의 영상을 통해 다양한 객체지향 패턴에 대해 알게되었다. (* 본 포스팅은 영상 내용을 요약 작성함)
어떤 클래스의 객체가 프로세스 내에서 단 한 개만 만들어져야할 때
예: Setting을 관리하는 클래스 (다크모드를 설정하면, 모든 페이지에 다크모드가 적용되어야 함)
생성자를 private으로 변경 -> 다른 클래스에서 new로 생성할 수 없게 된다
클래스 안에 자신의 타입을 갖는 객체를 선언한다
static 아닌 변수는 객체가 생성될 때마다 동적공간에 메모리가 할당되지만, static 변수는 메모리가 지정된 정적 공간에 딱 하나만 존재한다 (컴파일 할 때부터)
static 메소드는 이미 메모리의 정적 공간에 자리를 차지하고 있으므로 해당 객체를 new로 생성하지 않아도 바로 불러 사용할 수 있다
인터페이스의 사용이나 lazy loading 등 싱글톤으로 할수 있는 것들이 더 많기 때문에
프로그램 실행 중 선택된 모드에 따라 실행되는 방식, 즉 전략이 결정될 때
예: 전체, 이미지, 뉴스, 지도 선택/검색 클릭 -> 검색할 때 선택한 검색 모드에 따라 각각 다른 검색 방식이 실행됨
SearchButton을 눌렀을 때 onClick함수에서 조건문을 사용해, 검색 모드에 따라 동작을 다르게 실행한다. 수정사항이 생기거나 모드가 추가되면 onClick은 한없이 길어짐 -> 소프트웨어가 커지고 복잡해 질수록 코드 분석,관리가 어려워지는 우아하지 않은 설계 && 클래스마다 역할지정을 뚜렷히 하여 모듈화 된 소프트웨어를 구축하는 객체지향 철학에 어긋남
모드마다의 동작 하나하나를 모듈로 따로 분리하여 검색버튼을 누를 때 실행될 검색 모듈을 갈아끼워주는 방식 사용
SearchStrategy 인터페이스를 만들고 onClick에서 searchStrategy의 search() 함수만 호출한다. setter를 이용해 searchStrategy 를 SearchStategy 인터페이스를 상속 받은 다른 검색 전략으로 갈아끼울 수 있다.
State 패턴의 설계 구조는 strategy와 얼핏 비슷하다
그럼 차이점은?
Strategy 패턴: 어떤 동일한 틀 안에 있는 특정 작업의 방식, 모드를 바꿔줄 떄
State 패턴: 특정 상태마다 다르게 할 일을 하나하나 모듈화해서 지정해 둘 때 쓰임
예: TV 전원 On/Off 스위치
정리: Strategy 패턴이, 지정된 특정 메소드가 모듈화된 모드에 따라 다르게 실행되도록 하는 거라면 State 패턴은 메소드가 실행될 때 그 모드도 전환 된다고 이해하면 된다
전략 패턴은 같은 일을 하되 (ex) 검색), 알고리즘이나 방식이 갈아끼워짐
커맨드 패턴은 그 하는 일 자체가 다른 것!
-> 작성하는 방식이 다양함
형식이 다른 둘 사이를 연결 -> 이 둘이 호환될 수 있게 해줌
객체 지향에서는 어답터는 보통 인터페이스가 서로 다른 객체들이 같은 형식 아래 작동할 수 있도록 하는 역할을 함
예: Strategy 패턴 예제 처럼 SearchButton 모드에 동영상 모드가 추가되었는데, 완전 다른 구조의 다른 라이브러리 코드라면? (다른 전략들 처럼 단순히 search()하는 것이아니라, FindAlgorithm에서 find(true)로 호출해야함)
-> SearchFindAdapter라는 클래스를 지정하고 FindAlgorithm 객체를 생성자로 넘겨줌, 어댑터 안의 search 메소드를 실행하면 find 메소드가 실행되도록 한다
회사 대표가 몸소 등장해야하는 일은 그만큼 중요도가 있고 권한이 필요한 일 나머지 잔일은 비서가 처리한다.
프로그래밍에서 사용하는 일 중 중대한 일(인터넷에서 받아와야 해서 시간이 걸리거나, 메모리를 많이 차지하는 일 등)들은 객체로 여럿 생성해서 사용하기 부담이 될 수 있다. 이 때, 클래스의 Proxy를 만들어 비서 같은 이 클래스가 가벼운 역할들을 하도록 만들고, 중요한 일이 있을 때만 실제 클래스를 만들어 무거운 일을 돌린다.
예: 유튜브 영상 목록에서 썸네일에 마우스 오버시 프리뷰가 재생되는 기능 - 영상을 받아와야하므로 무거운 작업
이때 목록에서 제목도 보여줘야하는데, 이런 가벼운 작업은 ProxyThumbnail 클래스에서 하게 하고 ProxyThumbnail에서 showPreviw함수를 호출할 때 그제서야 RealThumbnail 객체를 생성하여 무거운 작업을 진행한다.
facade는 프랑스어로 외벽을 뜻하는 말.
한 작업에서 여러 클래스를 생성하여 복잡한 작업을 해야할 때, 이 과정들을 외벽 뒤로 숨겨 추상화 할 때 사용된다. (이미 자주 사용해본 적이 있을 패턴)
어떤 같은 형식을 지닌 특정 작업들의 세부 방식을 다양화 하고자 할 때 사용된다.
어떤 일을 수행하는 몇가지 방법이 있는데 그 전반적 과정에 공통된 절차가 있을 때 코드를 효율적으로 짜기 위해 만들어진 패턴!
부모 클래스에 전반 과정을 수행하는 메인 메소드가 있고, 그 과정 가운데 세부 메소드가 있음
자식 클래스는 과정을 수행하는 세부 메소드를 오버라이딩한다.
(예(비유): 약과 가문의 약과 만드는 방법은 1. 반죽 2. 튀기기 3. 즙청 인데, 이 큰틀을 자식들이 변경할 수는 없다. 그럼 약과가 아니게 됨. 대신 세부과정을 변경 가능)
예: 지도 클래스(MapView)에서 지도 init 방식 은 1. 맵서버 연결 2. 스크린에 맵 띄우기 3. 현재 위치로 옮기기 인데, MapView를 상속받은 자식 클래스인 네이버 지도와 카카오 지도에서는 이 각각의 세부 과정들이 다르게 동작 할 수 있다.
특정 클래스의 객체들이 할 수 있는 일을 여러가지 두고 각 객체마다 사용자가 원하는 대로 골라 시키거나 기능들을 필요에 따라 장착할 수 있도록 할 때 사용된다.
예: 게임에서 전투기가 아이템을 먹을 때마다 공격시 발사하는 무기들이 추가됨
객체가 생성자 변수로 다른 객체 안에 들어감으로써 그 실행하는 메소드의 행동이 추가되도록 한다.
팩토리 메소드 패턴은 클래스의 인스턴스를 만드는 일을 서브 클래스에게 맡기는 것이다. (생성자를 호출하는 클래스)
(비유: 약국에 약 사러 갔는데, 약사가 알약 열몇개 들고와서 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 등 세부 클래스에 대해 알 필요가 없게 한다.
팩토리 메소드 패턴에 한단계 더 추상화가 입혀진 패턴
특정 객체들을 찍어낼 공장인 팩토리도 여러 종류로 건설할 수 있도록 함
예: 버튼, 스위치, 드랍다운이 여러 테마(light, dark)가 있다고 할 때, 테마마다 CompFactory 인터페이스를 상속한 LigthCompFactory와 DarkCompFactory 테마를 만든다
특정 이벤트에 반응해서 관련된 다른 클래스들에 알려주는 일을 mediator 중재자 역할을 하는 클래스에 전담시키는것
비유: 거래처에 전화를 돌릴 외주업체를 따로 두는 것
회사(N) - 중재자 (외주업체 1) - 거래처(N) : 중재자를 중심으로 회사와 거래처가 N:N관계가 될 수 있다
여러 클래스들의 관계가 특정 이벤트들을 중심으로 복잡하게 얽힌 설계에서 유용하게 사용
포함하는 것과 포함되는 것들이 같은 방식으로 다뤄질 수 있도록 할 때 컴포지트 패턴 사용
예: 컴퓨터의 폴더 시스템과 유사함. 폴더와 파일은 다른 종류이지만, 둘다 이름 바꾸기, 용량구하기, 삭제하기 등의 명령을 받을 수 있음
얄팍한 코딩사전: 객체지향 디자인패턴1
얄팍한 코딩사전: 객체지향 디자인패턴2
https://velog.io/@namezin/GoF-design-pattern