티스토리 뷰

📌 목차

1️⃣ 디자인 패턴

2️⃣ 어댑터 패턴

3️⃣ 퍼사드 패턴

4️⃣ 전략 패턴

 


 

객체지향 개념들이 실제로 어떤 형태로 사용되는지 알고 싶다면?

1️⃣ 디자인 패턴

  • 디자인 → 설계
  • 패턴 → 자주 반복되는 것(패턴)
    ➡️
    설계 과정에서 자주 반복되는 코드의 형태를 패턴으로 정의해 놓은 것 (효과적이라고 생각되는 것만)

💁🏻‍♀️ 디자인 패턴 왜(Why) 쓰는데?

✔️ 실무에서 자주 겪는 문제들을 해결할 수 있는 검증된 문제 해결 방법! → 바퀴를 재발명하지 마라!!

✔️ 효율적인 커뮤니케이션 → 디자인 패턴의 이름만 말하면 개발자들은 같은 코드를 떠올릴 수 있음

❗ but 모든 코드에 디자인 패턴을 적용하려 하지 마라!! (필요할 때만 잘 사용하기)

 

🔎 디자인 패턴을 나누는 분류

  • 생성을 위한 패턴
    : 팩토리 메서드, 추상 팩토리, 빌더, 프로토타입, 싱글턴
  • 구조를 위한 패턴
    : 어댑터, 브릿지, 컴포지트, 데코레이터, 퍼사드, 플라이웨이트, 프록시
  • 행동을 위한 패턴
    : 인터프리터, 템플릿 메서드, 책임 연쇄, 커맨드, 이터레이터, 중재자, 메멘토, 옵저버, 상태, 전략, 비지터

💡 (tip) 자주 접하는 디자인 패턴부터 공부하기
    → 직접 구현해서 쓰는 패턴 / 이미 구현된 것을 사용하는 패턴 (by 라이브러리, 프레임워크)

 

💁🏻‍♀️  이미 구현된 것을 사용하는 패턴은 왜 알아야하지?

💡 라이브러리 / 프레임워크가 제공해주는 디자인 패턴을 모르는 경우, 해당 라이브러리 / 프레임워크를 온전히 사용할 수 없음 or 예상치 못한 버그가 발생할 확률↑

 

🖐🏻 안티패턴?

 : 어떤 상황에 자주 등장하긴 하지만, 비효율적이고 비생산적인 형태의 코드를 의미 (가독성↓, 버그 발생 가능성↑)

 


 

먼저 우리가 직접 구현하게 될 확률이 높은 패턴들을 알아보자🤓

2️⃣ 어댑터 패턴

 : 호환되지 않는 인터페이스(ex. 외부 라이브러리)를 가진 객체들이 협업할 수 있도록 하는 구조적 디자인 패턴

 

문제상황💦

우리는 낙상방지 프로그램 개발을 진행 중이다!
Mediapipe라는 라이브러리의 PoseLandmarker.detectAsync(Image image) 메서드를 사용해서 이미지를 넣으면 이미지에 있는 사람의 랜드마크를 리턴해 주는 메서드를 사용하고 있다.
우리는 코드 이곳저곳(Image, Video, LiveStream)에서 PoseLandmarker에 의존해 랜드마크를 감지하고 있다!
이는 DIP를 위반할 뿐더러 우리는 Mediapipe가 제공해주는 메서드의 성능이 마음에 들지 않으면 OpenCV 라이브러리로 랜드마킹 기능을 바꿀지도 모른다. → 추후 변경에 유연해질 수 있도록 어댑터 패턴을 도입하자!

  • 더이상 고수준 컴포넌트(ex. LiveStream)이 저수준 컴포넌트(PoseLandmarker)에 의존X (DIP ⭕)
  • Landmerker 인터페이스 덕분에 간결해진 인터페이스 (서비스 가독성↑)
  • Landmarker 구현체를 확장해 나갈 수 있음! (ex. OpenCV의 포즈 감지 알고리즘)

 


 

3️⃣ 퍼사드 패턴

 : 라이브러리 or 프레임워크 or 클래스들의 복잡한 집합에 대한 단순화된 인터페이스를 제공하는 구조적 디자인 패턴

  • 퍼사드는 건물의 정면을 의미 (건물 내부의 복잡함은 감추고 오직 건물의 정면만이 노출)
  • 건물의 정면이 인터페이스 (클라이언트는 인터페이스만을 사용!)

 

문제상황💦

우리는 Controller - Service - Repository 레이어드 아키텍처를 가진 웹 서비스를 개발 중이다.
우리의 UserService는 getProcessedUser()라는 메서드를 제공해주고, PostService는 getProcessedPost()라는 메서드를 제공해준다. 그런데, UserService의 어떤 메서드에서 getProcessedPost()가 해주는 기능을 사용해야 된다!
그리고 PostService의 어떤 메서드에서도 getProcessedPost()에서 제공해주는 기능을 사용해야 한다!
서로의 메서드를 사용하기 위해 UserService에서 PostService를 멤버변수로 갖고, PostService에서도 UserService를 멤버변수로 갖는다면? → 순환참조 발생! 퍼사드 패턴으로 해결하자!

 

💁🏻‍♀️ 그냥 같은 Service를 멤버변수로 갖지 못하게 하고 해당 기능을 Repository로 구현하면 안되나?

💡 이렇게 해결 가능하지만 이것은 해당 기능이 아주 구현하기 쉬울 경우에 가능!

     만약 해당 기능이 1,000줄을 넘어가는 복잡한 로직이라면?? 같은 기능을 또 만드는 것은 낭비!

 

@Service
public class UserService {
    private final PostService postService;
    
    public ProcessedUser getProcessedUser() {
        // 100줄이 넘는 복잡한 로직
    }
    
    public void someMethod() {
        postService.getProcessedPost();
        // ...
    }
    
    // ...
}
@Service
public class PostService {
    private final UserService userService;

    public ProcessedPost getProcessedPost() {
        // 100줄이 넘는 엄청난 로직
    }
    
    public void someMethod() {
        userService.getProcessedUser();
        // ...
    }
    
    //...
}

  • 복잡한 로직(코드)를 캡슐화하여 간단한 인터페이스만으로 동일한 동작을 하도록 함
  • 로직이 변경되더라도 Facade까지만 영향 (UserService와 PostService가 보호 받음)
  • 중복된 코드를 함수록 묶어내는 것과 비슷

 


 

4️⃣ 전략 패턴

 : 알고리즘(문제해결 방법)들의 패밀리를 정의하고, 각 패밀리를 별도의 클래스(인터페이스)에 넣은 후 그들의 객체들을 상호교환할 수 있도록 하는 행동 디자인 패턴

스프링 프로젝트에서 어떤 전략을 사용할지는 스프링 프레임워크가 결정

  • 상황에 따라 런타임에 어떤 동작을 수행할지(어떤 Repository 구현체를 사용할지) 인터페이스 뒤에 추상화 되어 있기 때문에 서비스는 자신이 주입받은대로 실행만 하면 됨
  • Service가 모르게 새로운 전략의 추가 가능 (Service를 보호)

 

🖐🏻 상속과 구성(멤버변수)

다른 클래스의 메서드에 접근(코드를 재사용)하고 싶다면 상속이 아닌 구성(클래스를 멤버변수로 갖는 것)을 사용해야 함

그렇지 않으면 Controller가 Repository의 메서드를 알게되는 것처럼 클래스들이 강하게 결합(의존성↑)하게 됨...

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함