1. Strategy Pattern
Created: Oct 08, 2019 10:27 AM
Tags: strategy pattern,전략 패턴
[ Strategy Pattern ]
: 자주 변동이 생기거나 기존 구조에 새로 추가될 경우 해당 알고리즘을 "캡슐화"해 주입함으로써
클라이언트와는 독립적으로 알고리즘을 교환할 수 있게되는 디자인 패턴
public abstract class Animal {
// 각 객체에서 공통으로 사용될 이름 변수 String name; public Animal(String name) { this.name = name; } public void doBehavior() { // Dog, Eagle, Penguin 각 객체 모두 공통으로 동일한 내용으로 호출되는 메서드 doEat(); // 각 동물 객체별로 재구현이 필요한 이동관련 메서드 doMove(); // 각 동물 객체별로 재구현이 필요한 울음소리 메서드 doCry(); if(canFly()) { // hook method로 각 객체에서 재구현해 실행여부를 결정(optional) doFly(); } } public void doEat() { System.out.println(name + "(은) 얌얌 밥을 먹습니다."); } public abstract void doMove(); public abstract void doCry(); public boolean canFly() { return false; } public abstract void doFly();
}
public class Dog extends Animal{
public Dog(String name) { super(name); } @Override public void doMove() { System.out.println(name + "(은) 네발로 기어갑니다."); } @Override public void doCry() { System.out.println(name + "(은) 왈왈 짓습니다."); } @Override public void doFly() { // TODO Auto-generated method stub }
}
public Eagle(String name) {
super(name); } @Override public void doMove() { System.out.println(name + "(은) 날아갑니다."); } @Override public void doCry() { System.out.println(name + "(은) 쒱~ 하고 웁니다."); } @Override public void doFly() { System.out.println(name +"(은) 날게가 있어 날 수 있습니다."); } // hook method를 true로 바꿔 doFly()가 동작할 수 있도록 한다. @Override public boolean canFly() { return true; }
}
public class Penguin extends Animal{
public Penguin(String name) { super(name); } @Override public void doMove() { System.out.println(name + "(은) 뒤뚱뒤뚱 걷습니다."); } @Override public void doCry() { System.out.println(name + "(은) 펭귄~하고 웁니다."); } @Override public void doFly() { System.out.println(name + "(은) 날개가 없어 날 수는 없습니다."); } @Override public boolean canFly() { return true; }
}
public class CommonModelTest {
private static final Logger logger = LoggerFactory.getLogger(CommonModelTest.class);
@Test
public void test1() {
Dog dog = new Dog("강아지");
dog.doBehavior();
Eagle eagle = new Eagle("독수리");
eagle.doBehavior();
Penguin penguin = new Penguin("펭귄");
penguin.doBehavior();
/*
강아지(은) 얌얌 밥을 먹습니다.
강아지(은) 네발로 기어갑니다.
강아지(은) 왈왈 짓습니다.
독수리(은) 얌얌 밥을 먹습니다.
독수리(은) 날아갑니다.
독수리(은) 쒱~ 하고 웁니다.
독수리(은) 날게가 있어 날 수 있습니다.
펭귄(은) 얌얌 밥을 먹습니다.
펭귄(은) 뒤뚱뒤뚱 걷습니다.
펭귄(은) 펭귄~하고 웁니다.
펭귄(은) 날개가 없어 날 수는 없습니다.
* */
}
}
위의 코드는 Dog, Eagle, Penguin 동물 객체의 행동을 구현하였는데, 동물의 공통 동작을 정하기 위해서 Template Method Pattern을 이용해서 구성하였다.
- 먼저, Dog, Eagle, Penguin은 공통적으로 doEat 밥을 먹는 메서드를 Animal에 구현하였고
- 울음소리를 나타내는 doCry는 Animal을 상속받는 각 동물객체에서 재정의하도록 하였다.
- 마지막으로 doFly는 강아지는 날 수 없음으로 Optional하게 선택할 수 있도록 Hook Method인 canFly()를 넣어 각 동물객체에서 재정의하여 doFly를 수행할지 여부를 지정할 수 있도록 하여 큰 틀을 구성하였다.
→ 이때, doFly 메서드의 구현방식을 변경하고(날 수 있는 녀석, 없는 녀석)
→ doLive라는 어디에 서식하는지에 대한 메서드를 추가한다고 했을 때
위의 코드의 문제점은 무엇일까?
—> 1. 먼저, doFly 메서드는 Dog한테는 해당사항이 없음에도 override하고 있는 문제점
doFly의 상세 구현 내용을 변경하기 위해 각 객체별로 접근해서 구현 내용을 일일이 바까주어
야 하는 점.(지금은 객체가 3개뿐이지만, doFly메서드를 사용하는게 20,30개가 넘는다면?)
→ 따라서, 이럴 경우에는 doFly메서드 알고리즘을 별도로 "캡슐화"해서 주입하도록 해 언제든지 알고리즘을 교환하는 방식을 통해 클라이언트 실행단과의 종속성을 제거할 수 있다.
수정 방향)
- doFly메서드에 대해 "날수 있는", "날수 없는"의 알고리즘을 분리한다.
- 각 객체에 주입한다.
공통사항을 관리하는 Animal 추상 클래스 : 아까와 동일하다.
public abstract class Animal {
// 각 객체에서 공통으로 사용될 이름 변수
String name;
public Animal(String name) {
this.name = name;
}
public void doBehavior() {
// Dog, Eagle, Penguin 각 객체 모두 공통으로 동일한 내용으로 호출되는 메서드
doEat();
// 각 동물 객체별로 재구현이 필요한 이동관련 메서드
doMove();
// 각 동물 객체별로 재구현이 필요한 울음소리 메서드
doCry();
if(canFly()) { // hook method로 각 객체에서 재구현해 실행여부를 결정(optional)
doFly();
}
}
public void doEat() {
System.out.println(name + "(은) 얌얌 밥을 먹습니다.");
}
public abstract void doMove();
public abstract void doCry();
public boolean canFly() {
return false;
}
public abstract void doFly();
}
DoFly 인터페이스
: 날수 있는 알고리즘, 날수없는 알고리즘을 관리할 DoFly 인터페이스
public interface DoFly {
// 나는 것과 관련된 메서드
public void fly();
}
CanFly 클래스
: DoFly 인터페이스를 구현해 날 수 있는 알고리즘을 구현한 클래스로 ⇒ 알고리즘을 "캡슐화"했다.
public class CanFly implements DoFly{
private String name;
public CanFly(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println("날 수 있습니다.");
}
}
CanNotFly 클래스
: 위와 마찬가지로 날 수 없는 알고리즘을 캡슐화
public class CanNotFly implements DoFly{
private String name;
public CanNotFly(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println("날개는 있지만 날 수 없습니다.");
}
}
public class CanNotFly implements DoFly{
private String name;
public CanNotFly(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println("날개는 있지만 날 수 없습니다.");
}
}
Dog 클래스
: 강아지 객체로 Animal을 상속했으며 강아지는 날수없음으로 Animal의 canFly() hook method를 구현하지 않았다.(default false)
public class Dog extends Animal{
public Dog(String name) {
super(name);
}
@Override
public void doMove() {
System.out.println(name + "(은) 네발로 기어갑니다.");
}
@Override
public void doCry() {
System.out.println(name + "(은) 왈왈 짓습니다.");
}
@Override
public void doFly() {
// TODO Auto-generated method stub
}
}
Eagle 클래스
: 아까는 doFly() 내부에 직접 상세구현했다면 이번엔 DoFly fly 를 선언해 생성자에서 CanFly 알고리즘을 주입하는 방식으로 바깠다.
펭귄의 경우 날 수 없음으로 CanNotFly가 주입될 것(즉, 알고리즘 교환이 용이하다.)
public class Eagle extends Animal {
DoFly fly;
public Eagle(String name) {
super(name);
fly = new CanFly(name); // 각 객체에서 구현한 것을 주입한다.(날 수 있는 알고리즘 객체를 넣음)
}
@Override
public void doMove() {
System.out.println(name + "(은) 날아갑니다.");
}
@Override
public void doCry() {
System.out.println(name + "(은) 쒱~ 하고 웁니다.");
}
@Override
public void doFly() {
fly.fly();
}
// hook method를 true로 바꿔 doFly()가 동작할 수 있도록 한다.
@Override
public boolean canFly() {
return true;
}
}
Penguin 클래스
public class Penguin extends Animal{
DoFly fly;
public Penguin(String name) {
super(name);
this.fly = new CanNotFly(name); // 펭귄은 날수 없는 알고리즘을 주입함
}
@Override
public void doMove() {
System.out.println(name + "(은) 뒤뚱뒤뚱 걷습니다.");
}
@Override
public void doCry() {
System.out.println(name + "(은) 펭귄~하고 웁니다.");
}
@Override
public void doFly() {
fly.fly();
}
@Override
public boolean canFly() {
return true;
}
}
실행 main 클래스
public class CommonModelTest {
private static final Logger logger = LoggerFactory.getLogger(CommonModelTest.class);
@Test
public void test1() {
Dog dog = new Dog("강아지");
dog.doBehavior();
Eagle eagle = new Eagle("독수리");
eagle.doBehavior();
Penguin penguin = new Penguin("펭귄");
penguin.doBehavior();
/*
* 강아지(은) 얌얌 밥을 먹습니다.
강아지(은) 네발로 기어갑니다.
강아지(은) 왈왈 짓습니다.
독수리(은) 얌얌 밥을 먹습니다.
독수리(은) 날아갑니다.
독수리(은) 쒱~ 하고 웁니다.
날 수 있습니다.
펭귄(은) 얌얌 밥을 먹습니다.
펭귄(은) 뒤뚱뒤뚱 걷습니다.
펭귄(은) 펭귄~하고 웁니다.
날개는 있지만 날 수 없습니다.
* */
}
}
'스터디 > 디자인 패턴(design pattern)' 카테고리의 다른 글
팩토리 메서드 패턴(Factory Method Pattern) (0) | 2019.10.29 |
---|---|
스테이트 패턴(State Pattern) (0) | 2019.10.16 |
템플릿 메서드 패턴(Template Method Pattern) (0) | 2019.10.16 |