디자인 패턴에 대한 고찰

dev_qorh·2022년 10월 2일
0

CatchCatch

목록 보기
13/18
post-thumbnail

나흘 정도 장염에 걸리는 바람에 아무것도 할 수 없었다. 어제야되서 겨우 응급실을 가 진통제를 처방받으니 급진적으로 호전되고 있다. 덕분에 작업을 좀 다시할 수 있을 것 같다!

디자인 패턴

학부생부터 코드를 이리저리 작성하였는데, 돌이켜 본 결과 다른 것들은 차츰차츰 실력이 좋아졌지만 하나 개선되지 않는 것이 있었다.

왜 코드를 이렇게 분리하고 있는가? 에 대한 이유를 모르고 있었다.

레이어드 아키텍쳐라는 것은 정말 보편적으로 많이 사용되었기에 (github의 많은 실전 예제들이 그러하듯) 나도 무작정 따라 한 것이고, 그것의 이유는 충분히 느낄 수 있었다. 하지만 원칙적으로 그것이 설계된 것은 내가 느낀정도의 구분이 편하기 때문이라는 이유보다는 조금 더 논리적으로 설명가능한 이유가 바탕되어 있을 것 같았다.

여태껏 구현에 급급했기에 나중에 알아봐야지, 알아봐야지 했던 부분을 오늘 해소해보고자 한다.

디자인 패턴 용도에 따라 나누기

무언가 정리 글을 적고자 하였지만 너무 많은 선배님들께서 잘 정리해주신 내용이 수두룩했다. 그래서 프로젝트를 빗대어 이를 이해하고자 한다.

가장 유명한 분류 방법은, 패턴 카탈로그에서 제시한 생성, 행동, 구조라는 3가지 범주로 용도에 따라 나누기라고 한다.

  • 생성 : 객체 인스턴스를 생성하는 패턴, 클라이언트와 그 클라이언트가 생성해야 하는 객체 인스턴스 사이의 연결을 끊어주는 패턴
  • 행동 : 클래스와 객체들이 상호작용하는 방법과 역할을 분담하는 방법을 다루는 패턴
  • 구조 : 클래스와 객체를 더 큰 구조로 만들 수 있게 구성을 사용하는 패턴

생성

  1. 키보드 리더와 같은 클래스는 정적 메소드를 활용하면, 객체 인스턴스를 어디서든지 액세스 할 수 있게 만들 수 있다. 이처럼 특정 클래스의 객체 인스턴스가 하나만 만들어지도록 해주는 것. (싱글턴 패턴) 이는 의존성 주입에 의해 레이어 단계에서의 현 프로젝트는 괜찮을 것으로 보인다. + 생성자를 접근제한자로 막아두면 좋다

  2. 추상 팩토리 패턴은 OCP 원칙과 꽤나 밀접한 관계가 있는 듯 하다. 변경에는 닫혀 있지만 확장에 열려 있다는 것 자체가, 추상 팩토리를 사용해서 구성하였을 경우 일관된 구성을 가질 때 조건을 만족하게 되니까 말이다. (ex, 같은 제조사의 스펙을 갖는 다른 제품, 서로 다른 DB의 save를 override 해서 사용하는 클래스 등)

  3. 일반 팩토리 패턴은 객체를 만들어 반환하는 함수를 제공하는 방법. 아래 예시는 이해에 꽤 많은 도움을 준다. 해결을 위해서는 UnitFactory.create 라는 static 함수를 만들어 사용할 수 있다.

  4. 인스턴스를 생성할 때, 생성자를 통하지 않고 빌더라는 내부 클래스를 통해 간접적으로 생성하게 한다. 인수를 추가 할당할 때 모든 생성자를 찾아가 단서를 다는 것 보다 builder에 setParams 하나만 추가하고 기본값을 설정한다면, 코드들을 일일이 찾아다니지 않아도 기능 추가가 가능하다.

  5. 프로토타입을 통해, 기존 인스턴스를 복제하는 것으로 새로운 인스턴스를 만드는 방법. 기존의 인스턴스를 만드는 것이 오래 걸릴 때 사용한다. 본 프로젝트에서 문제를 여러개 생성할 때 적용 가능할 것 같다. 문제 저장하기 전 DTO와 같은 형식에서 clone 함수를 구현하는 방식 말이다.

행동

  1. 알고리즘 골격을 정의하고, 템플릿 메소드를 사용하여 알고리즘 일부 단계를 서브클래스에서 구현하다... 구조는 유지한다... 로그인 서비스와 같은 service impl 단계에서 활용하는 내용을 생각하면 될 것 같았다. 서비스의 impl은 각 목적에 맞게끔 상속한 인터페이스의 알고리즘을 재정의 하여 사용하고, 인터페이스의 알고리즘을 호출해 생성된 서비스 impl의 재정의된 구현물을 사용할 수 있다.

  2. state 클래스를 두고 이를 구현한 많은 상태 A,B,C...를 만든다. 모든 구현된 상태는 handle이라는 전역 변경 함수가 각각 다르게 구현되어 있으며 이를 호출하는 것은 상태를 가진 클래스에 의해 진행된다. 본 프로젝트는 아니지만, 결제와 같은 시스템에서 해당 플로우를 사용할 것 같다.

  3. 반복자 패턴으로는 컬렉션의 구현 방법을 노출하지 않으면서 집합체 내 모든 항목에 접근하는 방법을 제공한다고 한다. forEach와 같은 형태로 사용하는 형태가 이걸 뜻하는 듯 하다.. 나아간다면, 문제 셋을 구성하거나 풀 때 DTO로 변환하는 과정을 커스텀 iterator를 만들어 사용할 수도 있겠다.

  4. 알고리즘군을 정의하고 캡슐화하여 각각을 수정해서 쓸 수 있게 해주는 전략 패턴도 있다.

  5. 옵저버 패턴으로는 객체의 상태가 바뀔 때 그 객체에 의존 하는 다른 객체에 연락을 취하고 자동으로 내용이 갱신되는 방식을 사용할 수 있다. 일대다 의존성을 정의하는 것이다.

구조

  1. 객체의 결합을 통해 기능을 동적으로 유연하게 확장시킬 수 있다. 그림으로만 봤을 땐 이해가 잘 되지 않았는데, 예시 코드를 너무 잘 적어주셔서 이해가 수월했다. 아무튼 코딩 언어에서 사용되는 데코레이터의 원초격이 맞아보였고, 그 원리는 데코레이터라는 객체가 하나의 인터페이스를 상속받고, 서로는 생성자에 인터페이스 형태를 인자로 받아 내부에서 사용하도록 하는 방식이었다. 즉, 이미 생성된 특정 기능A 데코레이터를 기능B 데코레이터의 생성 인자로 주는 게 가능하게 된다는 것이다. 이를 위해서는 기능의 완전한 독립이 되어 있어야 할 것이다..

  2. 원래 객체의 대리인을 두어 로딩이 필요한 경우, 메소드를 숨길 경우, 원격 객체를 사용할 경우, 사전처리를 하는 경우에 활용이 가능한 프록시 패턴

  3. 파일 폴더와 같은 구조를 코드화 하는 예시로 적합한 컴포지트 패턴. 이 예제 또한 정말 잘 소개시켜 주셨다. 프로젝트에서 자료를 다루는 것에 있어서 정적이었을 수 있다는 생각이 들었다. 트리 구조로 코드베이스에서 활용이 가능하다니...

  4. 어탭터 패턴은 비슷하지만 분리된 두 클래스를 말그대로 이어주는 역할을 할 수 있다. 이 블로그 포스트 에서는 신기하게도 오리와 칠면조로 예를 들었는데, 나는 개인적으로 bird라는 클래스를 더 위에 설계를 했을 것 같다. 예시를 들었다면 음. 현악기와 강아지를 두고 바이올린과 포메라니안의 소리 내는 것을 어댑터로 이어붙이기 위해 어댑터를 쓴다고... 할 것 같다 물론 그런 사례는 잘 없겠지만. 하지만 덕분에 이해하게 되었으니 훌륭한 글임에는 틀림없다.

  5. 그리고... 이것저것 때려박은걸 정리해 놓은 함수를 밖으로 빼주는게 멋진 말로 퍼사드 패턴이라는 것을 깨달았다 하하.

참고

https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823
https://appleg1226.tistory.com/49
https://gmlwjd9405.github.io/2018/07/06/design-pattern.html
https://namu.wiki/w/%EB%94%94%EC%9E%90%EC%9D%B8%20%ED%8C%A8%ED%84%B4
https://victorydntmd.tistory.com/300
https://kingchan223.tistory.com/295#:~:text=%3A%20%EA%B8%B0%EC%A1%B4%20%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%EB%A5%BC%20%EB%B3%B5%EC%A0%9C%ED%95%98%EC%97%AC,%EC%8B%9C%EA%B0%84%EC%9D%B4%20%EC%98%A4%EB%9E%98%EA%B1%B8%EB%A6%AC%EB%8A%94%20%EC%9E%91%EC%97%85.
https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html
https://coding-factory.tistory.com/711
https://mygumi.tistory.com/343
https://jusungpark.tistory.com/22

profile
기술로써 가치를 만들고 싶은 사람입니다.

0개의 댓글