일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- HTTP
- 백준 장학금
- 최대 힙
- 프로세스
- 완전이진트리
- 엔티티 그래프
- heapq
- 힙트리
- 최소힙
- 백준장학금
- jpa n+1 문제
- 이분탐색이란
- MSA
- 운영체제
- JPA
- 연결리스트
- SpringSecurity
- Kruskal
- python
- 자료구조
- 점근적 표기법
- 멀티프로세서
- JVM
- 연결리스트 종류
- posix
- 알고리즘
- 스케줄링
- spring
- 강화학습
- AVL트리
- Today
- Total
KKanging
[디자인 패턴] 오리 예제로 알아보는 Strategy Pattern 본문
오리 문제

가정
- 모든 오리는 소리를 낼 수 있다(quack)
- 모든 오리는 수영을 할 수 있다(swim)
- 오리의 종류마다 외형만 다르다.(display)
설명
모든 오리는 quack 과 swim operation을 가지고 있기 때문에 추상 클래스로 quack 와 swim 을 가지게 하고 추상 메서드로 display 를 구현하게 하였다.
추가 요구 사항(fly)

추가 요구 사항
- 오리는 날 수 있다.
설명
오리는 날 수 있기 때문에 fly 라는 operation 을 추가해야한다.
다행이도 현재 MallardDuck 과 RedheadDuck은 fly 동작을 하므로 추상 클래스인 Duck 에 fly를 impl 하였다.
에러 상황


설명
- RubberDuck 추가 : 소리를 다른 오리와 다르게 냄, 날지 못함
- DecoyDuck 추가 : 소리를 못 냄 , 날지 못함
해결?

설명
RubberDuck , DecoyDuck 전부 fly 와 squack operation 이 다르기 때문에 재정의를 해준다.
그리고 동작이 없는 메서드들은 재정의를 하지만 로직을 넣지 않는다.
public class RubberDuck extends Duck {
@Override
public void display() {
System.out.println("I'm a Rubber Duck!");
}
@Override
public void quack() {
System.out.println("Squeak!");
}
@Override
public void fly() {
return;
}
}
public class DecoyDuck extends Duck {
@Override
public void display() {
System.out.println("I'm a Decoy Duck!");
}
@Override
public void quack() {
return;
}
@Override
public void fly() {
return;
}
}
문제점
- 재사용 목적으로 사용한 상속이, 유지보수 시점이 되자 잘 되지 않았다(fly,squack)
- boiler plate 코드를 계속 생성해야함(날지 못하는 행동을 날지 못하는 클래스마다 적어야함)
- 지역의 수정이 지역적이지 않은 영향을 야기한다.
행동을 인터페이스 추상화로 해결?

설명
문제는 fly 와 quack operation 이 문제점이 되었다.
문제는 fly 와 quack 은 구현 클래스마다 다르게 구현되었고 그렇다고 추상 메서드로 놔두기에는 operation으로 가지지 않는 구현 클래스도 존재하였다.
따라서 fly 와 quack을 인터페이스화 시켜서 fly 와 quack 을 operation으로 가지는 구현 클래스만 구현하도록 하였다.
문제점
public class RedheadDuck extends Duck implements Flyable, Quackable {
@Override
public void display() {
System.out.println("I'm a Redhead Duck!");
}
@Override
public void fly() {
System.out.println("Flying!");
}
@Override
public void quack() {
System.out.println("Quacking!");
}
}
public class MallardDuck extends Duck implements Flyable, Quackable {
@Override
public void display() {
System.out.println("I'm a Mallard Duck!");
}
@Override
public void fly() {
System.out.println("Flying!");
}
@Override
public void quack() {
System.out.println("Quacking!");
}
}
- 위 코드는 코드 재사용을 무시했다.
- 궁극적으로 RubberDuck 과 DecoyDuck 의 fly 와 quack 에 대한 유지보수 지점을 제거할 목적이었지만 또다른 유지보수를 만들었다.
- 만약 이상태에서 더 다양한 오리와 더 다양한 fly 나 quack 에 대한 구현체들이 생긴다면 유지보수 악몽에 빠질 것
Strategy Pattern
디자인 원칙
Strategy Pattern 을 설명하기 전에 Strategy Pattern에 근간이 되는 디자인 원칙을 설명한다.
위 예제의 문제점은 Duck 에 대해 변하는 behaviors(행동)을 구분은 했지만 추상화된 클래스로 묶지 않았다.
따라서 구현 클래스에서 구현에 맞춰 프로그래밍 하였다.
이를 해결하기 위한 디자인 원칙은 다음과 같다.
- 구현이 아닌 인터페이스에 맞춰 프로그래밍 한다.
- 상위 타입으로 프로그래밍 한다.
- 변수의 선언된 타입은 상위 타입이어야 한다.
인터페이스에 맞춰 프로그램 한다는 것은?

---X
Dog d = new Dog();
d.bark();
---인터페이스에 맞춘 예제
Animal a = new Dog();
a.makeSound();
변수 설정은 무조건 super type 으로 구현하고 메서드는 다형성을 이용한다.
그리고 구현 객체는 런타임에 알 수 있게 프로그래밍 하는 것이다.
개선
변경되는 행동과 변경되지 않는 행동 분리

변경 되는 행동인 fly 와 quack 을 따로 클래스 집합으로 분리한다.

public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void swim() {
System.out.println("Swimming!");
}
public abstract void display();
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
public class RedheadDuck extends Duck {
public RedheadDuck() {
flyBehavior = new Flywithwings();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a Redhead Duck!");
}
}
public class MallardDuck extends Duck {
public MallardDuck(){
flyBehavior = new Flywithwings();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a Mallard Duck!");
}
}
public class RubberDuck extends Duck {
public RubberDuck(){
quackBehavior = new Squeak();
flyBehavior = new FlyNoway();
}
@Override
public void display() {
System.out.println("I'm a Rubber Duck!");
}
}
public class DecoyDuck extends Duck {
public DecoyDuck() {
quackBehavior = new MuteQuack();
flyBehavior = new FlyNoway();
}
@Override
public void display() {
System.out.println("I'm a Decoy Duck!");
}
}
Duck 추상 클래스에서 fly 와 quack 의 behavior 의 interface를 composition 관계로 가진다.
그리고 Duck 클래스의 메서드는 인터페이스의 다형성을 이용하여 호출하였다.
따라서 런타임시에 Duck 구현 클래스 마다 behavior의 구현 객체를 할당하였다.
앞으로 Duck의 다른 behavior 가 추가되면 모든 Duck에 적용된다면 Duck 클래스에 아니면 interface로 추상 클래스 집합을 만들어서 composition 관계를 통해 유지보수 할 수 있게 되었다.
따라서 지역적인 변경이 이제 다른 클래스의 변경을 초래하지 않게 된다.
composition? inheritance?
위의 예시는 fly 와 quack을 유연하게 프로그래밍 하기 위해 inheritance가 아닌 composition 관계를 이용했다
왜 inheritance가 아닌 composition 관계를 사용했을까?
이유는 composition이 더 유연하기 때문이다.
재사용 관점에서 composition 과 inheritance의 차이
- inheritace (white box 재사용)
- composition(black box 재사용)
Strategy Pattern은 무엇인가

Strategy 패턴은 알고리즘의 패밀리를 정의하고
각각을 캡슐화하여 상호 교환 가능하게 만드는 것이다.
캡슐화가 필요한 행동(Behavior)들을 Strategy라는 패밀리로 정의하고 이를 사용하는 Context(client) 에서 이를 런타임시에 알고리즘을 변화시킬 수 있게 하는 패턴이다.
Strategy Pattern의 사용 목적은?
Strategy Pattern의 목적은 변경 가능성이 높은 행동을 캡슐화하여, 클라이언트가 런타임 시에 이러한 행동을 유연하게 변경하고 재사용할 수 있도록 하는 것이다. 이를 통해 코드의 구조를 보다 간결하게 유지하고, 새로운 행동을 추가하거나 기존 행동을 수정할 때 발생할 수 있는 위험을 줄일 수 있게한다.
내가 생각하는 Strategy Pattern의 사용 시점은?
행동의 변경 가능성이 높을 때: 추상화된 클래스 그룹에 대한 행동(또는 작업)이 변경되거나 추가될 가능성이 높을 경우, Strategy Pattern을 적용하면 각 클래스의 구현을 변경하지 않고도 행동을 쉽게 수정할 수 있다.
다양한 행동이 필요할 때: 특정 구현체에서 여러 가지 다양한 행동이 필요할 때, Strategy Pattern을 사용하여 각 행동을 별도의 클래스에 캡슐화함으로써 코드의 복잡성을 줄이고, 관리와 확장이 용이해진다.
지역적인 행동의 변경이 다른 곳에 영향을 주고 싶지 않을 때: 특정 행동의 변경이 다른 구현체에 영향을 미치지 않도록 하고 싶을 경우, Strategy Pattern을 적용하여 행동을 독립적으로 유지함으로써 다른 부분과의 의존성을 최소화할 수 있다.
재사용 가능한 코드 작성이 어려울 때: 행동 구현에 대해 재사용 가능한 코드를 작성하기 어려운 경우, 각 행동을 별도의 클래스로 분리하여 Strategy Pattern을 적용함으로써 코드의 재사용성을 높이고, 유지보수를 용이하게 할 수 있다.
'백엔드 > 아키텍처 & 패러다임 & 디자인 패턴' 카테고리의 다른 글
설계 원칙 , 전지적 2년차 뉴비 시점 (0) | 2025.04.07 |
---|---|
[디자인패턴] 음료시스템 예제로 배우는 Decorator Pattern (0) | 2024.10.24 |
[디자인 패턴] 센서 디스플레이 예제로 배우는 Observer Pattern (4) | 2024.10.12 |
자바 개발자가 AOP를 공부 해야하는 이유 (1) | 2024.09.26 |