디자인 패턴 | 전략 패턴

Jihun Kim·2022년 6월 16일
0

디자인 패턴

목록 보기
1/4
post-thumbnail

이 글은 헤드퍼스트 디자인 패턴(한빛 미디어, 에릭 프리먼 외)를 읽고 정리한 것입니다.


전략 패턴이란?

알고리즘군을 정의하고 캡슐화 하여, 각각의 알고리즘군을 수정해서 쓸 수 있도록 만드는 전략이다. 이를 통해 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경하는 것이 가능하다.


전략 패턴이 필요한 상황 생각하기

디자인 패턴 책에는 전략 패턴을 사용하는 예시로 Duck 클래스를 생성하는 경우를 보여준다.
가령, Duck 추상 클래스가 있고 오리의 종류에 따라 해당 부모 클래스를 상속 받는 구상 클래스가 있다고 해보자.

  1. 추상 클래스
  • Duck
    - quack(): 울음 소리를 내는 메소드
    - fly(): 나는 데 사용하는 메소드
    - display(): 형태를 보여주기 위한 메소드
  1. 구상 클래스(추상 클래스를 상속 받음)
  • MallardDuck
    - quack, fly, display 모두 가능하다.
  • RedheadDuck
    - quack, fly, display 모두 가능하다.
  • RubberDuck
    - quack, fly가 불가능하다.

위와 같은 상황에서 만약 나무로 된 가짜 오리를 추가해야 한다면 어떻게 될까? quack과 display 메소드를 오버라이딩 해서 새로운 특징을 추가해야 할 것이다. 그러나 이렇게 하면 아래와 같은 문제점이 있다.

  • 서브클래스에서 코드가 중복 된다.
  • 실행 시에 특징을 바꾸기가 어렵다.
  • 코드를 변경했을 때 다른 오리들에게 원치 않은 영향을 끼칠 수 있다.
  • 모든 오리의 행동을 알기 어렵기 때문에 계속해서 메소드가 늘어나게 된다.

전략 패턴 적용하기

문제 파악

애플리케이션에서 달라지는 부분은 나머지 코드에 영향을 주지 않도록 캡슐화 해야 한다. 이를 통해 나중에 바뀌지 않는 부분에는 영향을 주지 않는 선에서 바뀌는 부분만 고치거나 확장하는 것이 가능하다.

따라서, 구성을 이용하는 방법을 통해 위의 예시에서 오리의 행동은 상속 받지 않고 오리의 행동 인터페이스를 통해 구성한 행동 객체를 부여 받도록 해야 한다.
-> 즉, "A는 B이다"가 아니라 "A에 B가 있다"가 되도록 하는 것이다.


유의할 점

  • 실제 실행 시에 쓰이는 객체가 코드에 고정되지 않도록 상위 형식에 맞추어 프로그래밍 하여 다형성을 활용해야 한다.
  • 객체를 변수에 대입할 때 상위 형식을 구현한 형식은 어떤 객체든 넣을 수 있다는 장점이 있다.

오리의 행동 구현하기

생성할 인터페이스

아래의 인터페이스 각각에 대해 구체적인 행동을 구현하기 위한 클래스를 생성할 수 있다.

FlyBehavior

  • 메소드: fly()
  • 구상 클래스: FlyWithWings, FlyNoWay

QuackBehavior

  • 메소드: quack()
  • 구상 클래스: Quack, Squeak, MuteQuack

위의 방법과 같이 메소드를 사용하지 않고 다른 클래스에 위임하는 방법을 통해 다른 형식의 객체에서도 나는 행동과 꽥꽥 거리는 행동을 재사용할 수 있다.

통합하기

오리의 특징을 정의할 Duck 클래스(abstract)를 생성한다.

Duck

  • 인스턴스 변수 선언
    - FlyBehavior flyBehavior
    - QuackBehavior quackBehavior
  • 메소드
    - performQuack()
    public abstract class Duck {
    	QuackBehavior quackBehavior;
        
       	public void performQuack() {
        	quackBehavior.quack();
        }  // performQuack 함수는 quackBehavior 인터페이스에 선언된 quack() 함수를 실행한다.
    }
    - swim()
    - display()
    - performFly()

위에서 정의한 Duck 추상 클래스를 이용해 물오리의 특징을 정의할 MallardDuck 클래스를 생성할 수 있다.

public class MallardDuck extends Duck {
	public MallardDuck() {
    	quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();  // 물오리는 날 수 있기 때문에 FlyWithWings 객체를 생성한다.
    }
    public void display() {
    	System.out.println("물오리입니다");
    }
}

전략 패턴 정리

먼저, 전략 패턴을 살펴보면서 아래와 같은 객체 지향 원칙이 그 안에 녹아 있음을 확인할 수 있었다.

객체 지향 원칙

  • 바뀌는 부분을 캡슐화 한다.
  • 상속 보다는 구성을 활용한다.
  • 구현 보다는 인터페이스에 맞춰서 프로그래밍 한다.

위에서 살펴본 오리의 나는 행동/ 꽥꽥 거리는 행동은 각 행동 집합에 대한 알고리즘군으로 생각할 수 있다. 각 행동은 또한 바뀔 수 있기 때문에 캡슐화 되어 있는 구조이다.

디자인 패턴 책에 따르면, 이처럼 알고리즘군을 이용하는 또 다른 방식으로는 세금 계산 방식을 구현하는 클래스가 있다고 한다. 이밖에도 우리 앱에서 이 패턴을 적용해 보기 위해서는 가령 장학금을 주는 방식이 여러가지라 가정했을 때 사용해 볼 수 있을 것 같다. 예를 들어 장학금을 현금으로 주는 방식, 상품권을 주는 방식, 특정 상품을 주는 방식 등으로 다양하다고 한다면 이를 알고리즘군으로 생각하고 전략 패턴을 적용해 볼 수도 있겠다.

profile
쿄쿄

0개의 댓글