Strategy, State Pattern 의 예
Strategy Pattern
클라이언트 객체가 컨텍스트 객체에게 다른 특정 객체를 지정해주고 실행하게 한다.
프로그램 실행시 컨텍스트 객체가 실행할 객체를 외부(클라이언트 객체)에서 유연하게 지정할 수 있는 장점이 있다.
컨텍스트 객체 내에 참조 멤버변수를 사용하여 하위클래스의 객체의 참조를 전달하면 컨텍스트 객체의 코드 변경이 없이도 컨텍스트의 행동을 변경할 수 있다. 즉, 다형성과 가상메소드 호출을 이용하여 컨텍스트 클래스의 코드를 간략하게 할 수 있는 방법이다.
State Pattern도 가상메소드 호출을 이용한다는 점에서 Strategy Pattern과 유사하다.
그러나 State Pattern은 외부(클라인트)객체의 개입이 없이 상태객체 내부에서 현재 상태에 따라 컨텍스트 객체의 멤버인 상태객체를 변경하여 사용한다는 점에서 차이가 있다. 그러므로 State Pattern은 프로그램의 각 상태를 객체화하여 사용한다는 점이 요구된다.
다음은 Strategy Pattern의 한 예이다.
Vehicle.java
package pattern; public interface Vehicle { double getCost(); } |
package pattern; public class Car implements Vehicle {
private double timeOnGround; private static final double costPerHourGnd = 5.0;
public Car(double timeOnGround) { this.timeOnGround = timeOnGround; } @Override public double getCost() { return timeOnGround * costPerHourGnd; } } |
Ship.java
package pattern; public class Ship implements Vehicle { private double timeOnPort; private double timeOnSea; private static final double costPerHourPort = 3.5; private static final double costPerHourSea = 4.2;
public Ship(double timeOnPort, double timeOnSea ) { this.timeOnPort = timeOnPort; this.timeOnSea = timeOnSea; } @Override public double getCost() { return timeOnPort*costPerHourPort + timeOnSea*costPerHourSea; } } |
Airplane.java
package pattern; public class Airplane implements Vehicle { private double timeOnGround; private double timeOnTakeoff; private double timeOnRoute; private double timeOnLanding; private static final double costPerHourGnd = 6.3; private static final double costPerHourTakeoff = 8.4; private static final double costPerHourRoute = 4.3; private static final double costPerHourLanding = 3.5;
public Airplane(double timeOnGround, double timeOnTakeoff, double timeOnRoute, double timeOnLanding ) { this.timeOnGround = timeOnGround; this.timeOnTakeoff = timeOnTakeoff; this.timeOnRoute = timeOnRoute; this.timeOnLanding = timeOnLanding; } @Override public double getCost() { return timeOnGround*costPerHourGnd + timeOnTakeoff*costPerHourTakeoff + timeOnRoute*costPerHourRoute + timeOnLanding*costPerHourLanding; } } |
Context.java
package pattern; public class Context { private Vehicle vehicle;
public void setVehicle(Vehicle vehicle) { this.vehicle = vehicle; } public void printCost(){ System.out.println(vehicle.getCost()); } } |
Client.java
클라이언트 객체에서 컨텍스트 객체가 실행할 오브젝트를 변경하는 예
package pattern; public class Client {
public static void main(String[] args) {
Context m = new Context();
m.setVehicle(new Car(4)); // 컨텍스트 객체가 실행할 오브젝트를 클라이언트가 설정함 System.out.print("차 운용비: "); m.printCost();
m.setVehicle(new Ship(2.1, 8)); // 컨텍스트에 현재 설정되어 있는 오브젝트를 클라이언트가 변경함 System.out.print("선박 운용비: "); m.printCost();
m.setVehicle(new Airplane(0.5, 0.5, 7.2, 0.9)); // 컨텍스트에 현재 설정되어 있는 오브젝트를 클라이언트가 변경함 System.out.print("비행기 운용비: "); m.printCost();
} } |
State Pattern의 예 (http://en.wikipedia.org/wiki/State_pattern)
State Pattern에서는 컨텍스트의 상태를 각 상태객체 내에서 변경하며 외부(클라이언트객체)에서 상태를 변경하지 않는다.
interface State { void writeName(StateContext stateContext, String name); } class StateA implements State { public void writeName(StateContext stateContext, String name) { System.out.println(name.toLowerCase()); /* 각 StateA에서 StateB로 상태를 변경한다 */ stateContext.setState(new StateB()); // 각 상태객체가 스스로 상태를 변경한다 } } class StateB implements State { private int count=0; public void writeName(StateContext stateContext, String name){ System.out.println(name.toUpperCase()); // change state after StateB's writeName() gets invoked twice if(++count>1) { stateContext.setState(new StateA()); // StateB -> StateA로 변경함 } } } |
public class StateContext { private State myState; public StateContext() { setState(new StateA()); } // normally only called by classes implementing the State interface public void setState(State newState) { this.myState = newState; } public void writeName(String name) { this.myState.writeName(this, name); } } |
public class TestClientState { public static void main(String[] args) { StateContext sc = new StateContext(); sc.writeName("Monday"); sc.writeName("Tuesday"); sc.writeName("Wednesday"); sc.writeName("Thursday"); sc.writeName("Saturday"); sc.writeName("Sunday"); } } |