전략패턴

핫다리·2023년 9월 5일
0

디자인패턴

목록 보기
1/3

전략패턴

알고리즘군을 정의하고 캡슐화 해서 각각의 알고리즘군을 수정해 사용할 수 있게 해줍니다.
클라이언트로부터 알고리즘을 분리해서 독립적으로 변경할 수 있습니다.

이것만 들어서는 잘 감이 안올겁니다. 좀 더 자세한 예제를 들어봅시다.

오리 시물레이션 코드

Duck 슈퍼클래스 
quack() 모든 오리가 가능하다
swim() 모든 오리가 가능하다
display() 모든 오리 모양이 다르니 추상메소드 이다.
Mallard Duck 클래스
display() //적당한 모양 표시
Redhead Duck 
display()//적당한 모양 표시

오리를 만드는 코드 입니다.

여기서 오리를 날게 하려면 Duck 슈퍼클래스에 fly()메소드를 추가하면 됩니다.

Duck 슈퍼클래스 
quack() 모든 오리가 가능하다
swim() 모든 오리가 가능하다
display() 모든 오리 모양이 다르니 추상메소드 이다.
fly() <-- 추가

근데 하필 이상태에서 고무오리 클래스를 추가하게 되었습니다.

Rubber Duck 
quack()// 소리 안내게 오버라이드
display() //적당한 모양 표시

곤란하죠? 고무오리는 날면 안됩니다. 그리고 quack소리도 다릅니다.
그래서 fly()와 quack()을 오버라이드를 해주겠습니다

Rubber Duck 
quack()// 소리 안내게 오버라이드
fly() //날지 못하게 코드 작성
display() //적당한 모양 표시

이제 문제가 생겼습니다. 앞으로 오리가 추가될때마다 일일히 오버라이드 할건가요?
유지보수가 힘들지 않을까요??

이 문제를 어떤방식으로 해결할까요.

  1. 여기서 상속으로 문제를 처리하면,앞으로 다른 오리를 추가할때 마다 계속 오버라이드 해야합니다.
  2. 실행시 특징을 바꾸기 힘들고 모든 오리의 행동을 알기 힙듭니다.
  3. 그리고 코드 변경 시 다른 오리에게 원치 않는 영향을 끼칠 수 있습니다.
  4. fly()와 quack()등 필요한 동작을 슈퍼클래스에서 빼서 interface로 만들
    수 있지만 코드중복이 우려되고, 코드의 재사용이 불가능합니다.

해결하기 위한 원칙을 알아봅시다.

디자인 원칙 : 애플리케이션이 달라지는 부분을 찾고 달라지지 않는 부분과 분리한다.

바뀌는 부분만 찾아서 캡슐화 한다

fly()와 quack()은 Duck 클래스에 있는 오리 종류에 따라 달라집니다.

fly()와 quack()을 Duck 클래스로부터 분리하려면, 2개의 메소드 모두 Duck 클래스에서 끄집어내서 각 행동을 나타낼 클래스 집합을 새로 만들어야 합니다.

Duck안에 fly()와 quack()을 슈퍼클래스에서 빼서 interface로 만들겠습니다.

디자인 원칙 : 구현보다는 인터페이스(상위형식)에 맟춰 프로그래밍 한다.

각 행동을 인터페이스로 표현하고, 이 인터페이스를 사용해서 행동을 구현합니다.
꼭 인터페이스가 아니여도 된다.상위 형식에 맞춰서 프로그래밍 하라는 소리입니다.

이러면 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용 할 수 있습니다.
그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck 클래스를 건드리지 않고도 새로운 행동을 추가할 수 있습니다.

오리 행동 통합하기

포인트는 나는 행동과 꽥꽥거리는 행동을 슈퍼클래스 에서 정의한 메소드를 써서 구현하지 않고 다른 클래스에 위임한다는 것 입니다.

행동변수는 행동 인터페이스 형식으로 선언합니다.
인스턴트 변수 실행시, 특정 행동의 레퍼런스가 실행됩니다.

perform Quack를 구현해봅시다.

public abstract class Duck {
	QuackBehavior quackBehavior;
    //기타코드
   
   public void performQuack(){
   	quackBehavior.quack();
   }
}

이제 꽥꽥 거리고 싶을 땐 객체의 종류에는 전혀 신경 쓸 필요 없이 quack()을 실행할 줄 만 알면 됩니다.

public class 청둥오리 extends Duck {

	public 청둥오리() {
    	quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
     }
     
     
     public void display() {
     	System.out.println("저는 물오리입니다")
      }
      
  }

물론 setter를 이용해 행동을 동적으로 지정할 수 있습니다.

두 클래스를 합치는 방법

A에는 B가 있다" 관계를 생각해 봅시다. 각 오리에는 FlyBehavior와 QuackBehavior가 있으며, 각각 나는 행동과 꽥꽥거리는 행동을 위임받습니다. 이런식으로 두 클래스를 합치는 것을 '구성(composition)을 이용한다'라고 부릅니다

오리코드 테스트

Duck 슈퍼 클래스

public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public Duck() {}
    
    public abstract void display();
    
    public void performQuack() {
        quackBehavior.quack();
    }
    
    public void swim() {
        System.out.println("모든 오리는 물에 뜹니다. 가짜 오리도 뜨죠");
    }
}

FlyBehavior 인터페이스

public interface FlyBehavior {
    public void fly();
}

-------------------------------------------------

public class FlyWithWings implements FlyBehavior {
    public void fly() {
    	System.out.println("날고 있어요!!!");
    };
}

-------------------------------------------------

public class FlyNoWay implements FlyBehavior {
    public void fly() {
    	System.out.println("저는 못 날아요!!!");
    };
}

QuackBehavior 인터페이스

public interface QuackBehavior {
    public void fly();
}

-------------------------------------------------

public class Quack implements QuackBehavior {
    public void quack() {
    	System.out.println("꽥!");
    };
}

-------------------------------------------------

public class MuteQuack implements QuackBehavior {
    public void quack() {
    	System.out.println("<< 조용 ~ >> ");
    };
}

-------------------------------------------------

public class Squeak implements QuackBehavior {
    public void quack() {
    	System.out.println("삑");
    };
}

실행코드

public class MiniDuckSimulator {
  public static void main(String[] args) {
  	Duck mallard = new mallardDuck();
   	mallard.performQuack();
    mallard.performFly();
  }
}

디자인 원칙3 : 상속보다는 구성을 사용하자.

profile
일단 만들고 본다

0개의 댓글