Head Fist Design Pattern 책에 있는 내용들을 정리해서 올립니다.
1. Strategy Pattern (전략 패턴)
1.1 Design Pattern 이란?
우리는 개발을 하며 어떠한 문제에 부딪힌 적이 있을것입니다.
예를 들어 내가 만들어 놓은 객체를 가져다 사용하는 클래스를 설계했습니다. 그런데 그 객체에 수정사항이 생기는 경우는 정말 많죠.
"뭐 수정사항이 생겼으면 수정하면 되지 그게 뭐?" 라고 생각하실 수도 있습니다. 하지만 객체를 수정하면 해당 객체를 가져다 사용하는 클래스 또한 수정해야 하는 문제가 발생했을 것입니다. 이러한 문제는 두 클래스 사이의 결합도 때문에 발생하는 문제죠. 하나의 클래스를 고쳤다고 다른 모든 클래스들을 고쳐야 한다면 정말 암담하겠죠? 이렇듯 설계는 개발하는데 있어 상당히 중요한 요소입니다.
우리가 겪었고 또 앞으로도 겪게될 이러한 클래스의 설계적인 문제를 해결해 놓은 방법을 디자인 패턴이라고 합니다.
1.1.2 Strategy Pattern이란?
1)알로리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든 패턴
- 기능의 클래스 집합을 만들어 사용
2)클라이언트와 알고리즘을 구성을 이용해서 합치기 때문에 두 클래스는 독립적이다. 따라서 클라이언트와는 독립적이고 안정적으로 알고리즘의 수정이나 확장에 어려움이 없다. 실행시에 setter method를 이용해 연결할 알고리즘 구성을 변경할 수 있다.
- 기능을 사용할 클래스와 기능역할을 하는 클래스는를 구성을 이용해 결합시킨다.
1.2 오리게임 만들기
오리 게임을 만들려고 합니다. 오리의 종류와 기능을 설계해 보겠습니다.
1.2.1 보통 초보 개발자들의 설계
오리의 종류
- MallardDuck (청둥 오리)
- RedHeadDuck (붉은 머리 오리)
오리가 할 수 있는 행동
- 날 수 있다. (공통)
- 헤엄 칠 수 있다. (공통)
- 오리들의 모습은 다르다 (공통 x)
개발경험이 부족하신 분들은 디자인 패턴이란 말을 처음 들어본 사람도 있을것입니다. 일단 만드는게 우선이고 동작하기만 하면 되니까요!
그런분들은 위 요구사항들을 보고 이렇게 생각할 겁니다.
- 어? 오리 종류가 두 마리니까 오리 클래스를 만들고 상속받아서 하면 되겠네.
- 모습은 다르니까 추상 메소드로해서 재정의해야지.
1.2.1.1 Duck (Super class)
- 모든 오리들이 공통적으로 가진 기능을 상속으로 물려주기 위한 Super class 입니다.
1 2 3 4 5 6 7 8 9 10 | abstract class Duck { void quack() { System.out.println("꽥꽥"); } void swim() { System.out.println("첨벙 첨벙"); } abstract void display(); } | cs |
1.2.1.2 MallardDuck (Sub class)
- Duck super class를 상속받아 공통된 부분을 상속받아 코드의 중복을 줄였습니다. (코드의 재사용)
1 2 3 4 5 6 | class MallardDuck extends Duck { @Override void display() { System.out.println("MallardDuck은 청둥 오리."); } } | cs |
1.2.1.2 RedHeadDuck (Sub class)
- Duck super class를 상속받아 공통된 부분을 상속받아 코드의 중복을 줄였습니다. (코드의 재사용)
1 2 3 4 5 6 | class RedHeadDuck extends Duck { @Override void display() { System.out.println("RedHeadDuck은 붉은 머리 오리. "); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 | abstract class Duck { void quack() { System.out.println("꽥꽥"); } void swim() { System.out.println("첨벙 첨벙"); } void fly() { System.out.println("훨훨"); } abstract void display(); } | cs |
1 2 3 4 5 6 | class RubberDuck extends Duck { @Override void display() { System.out.println("RubberDuck은 고무오리."); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class RubberDuck extends Duck { @Override void display() { System.out.println("RubberDuck은 고무오리."); } @Override void quack() { System.out.println("삑삑"); } @Override void fly() {} } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | abstract class Duck { // 모든 sub class의 공통 기능 void swim() { System.out.println("첨벙 첨벙"); } /* 모든 sub class가 가지는 공통된 기능 X void quack() { System.out.println("꽥꽥"); } void fly() { System.out.println("훨훨"); } */ abstract void display(); } | cs |
1.2.3.2 모든 sub class가 갖는것은 아니거나 기능을 가지긴 하되 약간씩의 차이가 있는 경우는 interface로 만든다.
모든 sub class가 기능을 가지긴 하되 약간씩의 차이가 있는 경우
1 2 3 4 | interface Quackable { void quack(); } | cs |
모든 sub class가 갖는것은 아닌경우
1 2 3 | interface Flyable { void fly(); } | cs |
1.2.3.3 기능이 필요한 경우 interface를 구현한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class MallardDuck extends Duck implements Quackable,Flyable { @Override void display() { System.out.println("MallardDuck은 청둥 오리."); } @Override public void fly() { System.out.println("훨훨"); } @Override public void quack() { System.out.println("꽥꽥"); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class RedHeadDuck extends Duck implements Quackable,Flyable { @Override void display() { System.out.println("RedHeadDuck은 붉은 머리 오리. "); } @Override public void fly() { System.out.println("훨훨"); } @Override public void quack() { System.out.println("꽥꽥"); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class RubberDuck extends Duck implements Quackable { @Override void display() { System.out.println("RubberDuck은 고무오리."); } @Override public void quack() { System.out.println("삑삑 (바람 빠지는 소리)"); } } | cs |
fly()와 quack()은 Duck super class에서 sub class마다 달리지는 부분이다.
Duck super class로 부터 분리해서 각 기능을 나타낼 클래스 집합을 만든다.
1.2.4.1 울음소리의 클래스 집합
울음소리 기능 클래스들을 묶을 상위 인터페이스
1 2 3 4 5 | package QuackBehavior; public interface QuackBehavior { void quack(); } | cs |
1 2 3 4 5 6 7 8 | package QuackBehavior; public class OriginQuack implements QuackBehavior { @Override public void quack() { System.out.println("꽥꽥"); } } | cs |
울음소리 기능2
1 2 3 4 5 6 7 8 | package QuackBehavior; public class Squeak implements QuackBehavior { @Override public void quack() { System.out.println("삑삑"); } } | cs |
울음소리 기능3
1 2 3 4 5 6 7 8 | package QuackBehavior; public class MuteQuack implements QuackBehavior { @Override public void quack() { // 소리 안냄. } } | cs |
1.2.4.2 날기 기능의 클래스 집합
날기 기능 클래스들을 묶을 상위 인터페이스
1 2 3 4 5 | package FlyBehavior; public interface FlyBehavior { void fly(); } | cs |
1 2 3 4 5 6 7 8 | package FlyBehavior; public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("훨훨"); } } | cs |
날기기능2
1 2 3 4 5 6 7 8 | package FlyBehavior; public class FlyNoWay implements FlyBehavior { @Override public void fly() { // 날지 못함. } } | cs |
1.2.4.3 기능 클래스들을 사용할 Client 클래스들
Duck Super class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import FlyBehavior.FlyBehavior; import QuackBehavior.QuackBehavior; abstract class Duck { // field QuackBehavior quackBehavior; FlyBehavior flyBehavior; // method void swim() { System.out.println("첨벙 첨벙"); } void performQuack() { quackBehavior.quack(); } void performFly() { flyBehavior.fly(); } abstract void display(); } | cs |
MallardDuck class
1 2 3 4 5 6 7 8 9 10 11 12 13 | import FlyBehavior.FlyWithWings; import QuackBehavior.OriginQuack; class MallardDuck extends Duck { MallardDuck(){ quackBehavior = new OriginQuack(); flyBehavior = new FlyWithWings(); } @Override void display() { System.out.println("MallardDuck은 청둥 오리."); } } | cs |
RubberDuck class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import FlyBehavior.FlyNoWay; import QuackBehavior.MuteQuack; class RubberDuck extends Duck { public RubberDuck() { quackBehavior = new MuteQuack(); flyBehavior = new FlyNoWay(); } @Override void display() { System.out.println("RubberDuck은 고무오리."); } } | cs |
1.2.4.4 main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class DuckSimulator { public static void main(String args[]) { Duck mallard = new MallardDuck(); mallard.performQuack(); mallard.performFly(); Duck RedHeadDuck = new RedHeadDuck(); RedHeadDuck.performQuack(); RedHeadDuck.performFly(); Duck RubberDuck = new RubberDuck(); RubberDuck.performQuack(); RubberDuck.performFly(); } } | cs |
1.3 디자인 원칙1
Application에서 달라지는 부분을 찾아서 달라지지 않는 부분으로부터 분리 시킨다.
- 코드에 업데이트 마다 변경될 수 있는 부분을 따로 뽑아내 캡슐화 시킨다.
이렇게 하면 나중에 바뀌지 않는 부분에는 영향을 주지않고 고치거나 확장할 수 있다.
1.3.1 디자인 원칙1을 적용시킨 Strategy Pattern
변경되는 기능의 클래스 집합을 만든다.
각 클래스 집합에는 각각의 기능을 구현한 것을 전부 집어 넣는다.
1.4 디자인 원칙 2
구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
1.4.1 디자인 원칙2을 적용시킨 Strategy Pattern
각 기능을 interface로 설정하고 그 interface를 구현하여 class 집합을 만든다.
이러한 방법은 기능을 super class에서 구현하거나 sub class에서 구현하는 방식과는 상반된 방법이다.(구현 위주)
sub class에서는 interface로 표현되는 기능을 사용하게 된다.
여기서 interface란 상위 형식을 말하는 것이기 때문에 abstract super class를 사용해도 무관하다. *
엄연히 말하면 Point는 상위형식에 맞춰서 프로그래밍 해야 한다는 것이다. ***
변수 선언시 상위 형식으로 선언하면 상위 형식을 구현한 어떠한 객체라도 집어 넣을 수 있기 때문이다. (다형성)
이렇게 하면 변수를 선언하는 클래스에서는 실제 변수에 할당되는 객체의 형식을 몰라도 된다.
Ex)
------------------------------------------------------------------------
구현 클래스 형식으로 선언하면
Dog d = new Dog();
d.bark(); <- 어떤 구체적인 구현에 맞춰서 코딩해야 한다.
------------------------------------------------------------------------
상위 형식(interface or abstract super class)에 맞춰서 프로그래밍 한다면
Animal animal = getAnimal();
animal.makeSound();
Animal의 하위 형식중 어떤 형식인지 알 필요없이 코딩할 수 있다.
------------------------------------------------------------------------
1.5 디자인 원칙3
상속 보다는 구성을 활용한다.
(is a) (has a)
'Design Pattern > Head FIrst Design Pattern' 카테고리의 다른 글
7. Adaptor Pattern (0) | 2019.05.23 |
---|---|
6. Command Pattern (0) | 2019.05.20 |
4. Factory Pattern - (1) SImple Factory (0) | 2019.04.21 |
3. Decorator Pattern (0) | 2019.04.21 |
2. Observer Pattern (0) | 2019.04.21 |