State Pattern (스테이트 패턴)

State Pattern

 


정의


Object의 내부 상태가 바뀜에 따라서 object의 행동을 바꿀 수 있다.


마치 object class가 바뀌는 것과 같은 결과를 얻을 수 있다.


State patternstateclass로 표현한다


그 이유는, class의 교체를 통해서 state의 변화를 나타낼 수 있고, 새로운 state를 추가해야 할 때, 편리하기 때문이다.

 


적용 영역


1.    Object의 행위가 object state에 의존하고 그 object의 행위가 실행 시점에서 object state에 따라 변화해야 할 경우


2.    Operation이 크고, objectstate에 따라 다수의 조건문을 포함하는 경우


 

State vs Strategy (pattern)


State pattern을 사용할 때는 state object의 행동이 캡슐화 된다


State에 따라 context object에서 여러 state object 중 한 object에게 모든 행동을 위임한다.


해당 object의 내부 state에 따라 현재 state를 나타내는 object가 바뀌고


그 결과 context object의 행동도 바뀐다. Clientstate object에 대해서 몰라도 된다.


하지만, strategy patternclient에서 context object한테 어떤 object를 사용할지 정한다


Strategy pattern은 주로 실행시에 전략 object를 변경할 수 있는 유연성을 제공하기 위한 용도로 쓰이며, 가장 적합한 전략 object를 선택해서 사용한다.


, strategy pattern은 외부에서 상황에 맞게 변화를 줄 수 있고, state pattern은 자신이 직접 상태변화에 관여한다는 것이다



구성요소


1.    State를 나타내는 state


2.    state간의 전의를 표현하는 action


3.    state 전이에 나타나는 onEntry, onExit 함수


 

소스코드


Action(enum)

public enum Action {
   
EAT, DIGEST, GOTOBED;
}

 

State(enum)

public enum State {
   
HUNGRY{
       
public State act(Action action){
           
switch (action){
                
case EAT: return FULL;
               
default: return null;
            }
        }
    },
   
FULL{
       
public State act(Action action){
           
switch (action){
               
case EAT: return ANGRY;
               
case DIGEST: return HUNGRY;
               
case GOTOBED: return SLEEPING;
               
default: return null;
            }
        }
    },
   
ANGRY{
       
public State act(Action action){
           
switch (action){
               
case DIGEST: return FULL;
                
default: return null;
            }
        }
    },
   
SLEEPING{
       
public void onEntry(){
           
System.out.println("go bed");
        }
       
public State act(Action action){
           
return null;
        }
    };
   
abstract State act(Action action);
   
public static State getInitState(){
       
return HUNGRY;
    }
   
public static boolean isFinalState(State state){
       
return state==SLEEPING;
    }
   
public void onEntry(){}
   
public void onExit(){}
}

 

StateContext(class)

public class StateContext {
   
private State currentState;
   
public StateContext(){
       
currentState = State.getInitState();
    }
   
public void processEvent(Action action){
       
State next= currentState.act(action);
       
if(next != null){
            
currentState.onExit();
           
System.out.println(action +"에 의해 State " +
                   
currentState+"에서 "+next+"로 바뀜");
           
currentState=next;
           
currentState.onEntry();
           
if(State.isFinalState(currentState)){
               
System.out.println("i'm final State");
            }
        }
else{
           
System.out.println(action+" state " +currentState
           
+"에서는 의미 없는 짓");
        }
    }
}

 

Main

public class Main {
   
public static void main(String[] args){
       
StateContext context = new StateContext();
       
context.processEvent(Action.EAT);
       
context.processEvent(Action.EAT);
       
context.processEvent(Action.GOTOBED);
       
context.processEvent(Action.DIGEST);
       
context.processEvent(Action.EAT);
       
context.processEvent(Action.GOTOBED);
       
context.processEvent(Action.DIGEST);
       
context.processEvent(Action.GOTOBED);
    }
}



결과




UML



Composite Pattern (컴포지트 패턴)

Composite Pattern

 


정의


Object group이 단일 object와 동일하게 다루는 것이다.


client들이 단일 object와 복합체(composition)들을 동일하게 다룬다.


 

용도


Clientobject들의 composition과 단일 object 사이 차이를 무시해야 할 때 사용한다


여러 개의 object들을 같은 방법으로 사용하거나


object들에게 비슷한 코드를 사용한다면 composite pattern을 생각해 볼 수 있다.


 

구조


1.    Composite


자식들을 다루는 메소드로 일반적으로 자식들에게 기능을 위임하여 component의 모든 method를 구현한다.


2.    Component


composite 하나를 포함하는, 자신을 포함한 모든 component들의 추상화로 composite에서 object들을 위한 interface를 제공한다


또한 선택적으로 component의 부모에 접근하기 위해 recursive structure interface를 구현하기도 한다.


3.    Leaf


composition에서 leaf 객체들을 나타내며, component의 모든 method들을 구현한다.


 

소스코드


Component

package CompositePattern;

public interface Component {
   
public void show();
}

 

composite

package CompositePattern;

import java.util.ArrayList;
import java.util.List;

public class Composite implements Component {

   
private List<Component> childComponents = new ArrayList<Component>();

   
public void add(Component component){
       
childComponents.add(component);
    }

   
public void remove(Component component){
       
childComponents.remove(component);
    }

   
@Override
    public void
show() {
       
childComponents.forEach(Component::show);
    }
}

 


 

Leaf

package CompositePattern;

public class Leaf implements Component {
   
private String name;
   
public Leaf(String name){
       
this.name=name;
    }
    
@Override
    public void
show() {
       
System.out.println(name);
    }
}

 

main

package CompositePattern;

public class Main {
   
public static void main(String[] args){
       
Leaf leaf1 = new Leaf("leaf 1");
       
Leaf leaf2 = new Leaf("leaf 2");
        
Leaf leaf3 = new Leaf("leaf 3");
       
Composite composite1 = new Composite();
       
composite1.add(leaf1);

       
Composite composite2 = new Composite();
       
composite2.add(leaf2);
       
composite2.add(leaf3);

       
composite1.add(composite2);
       
composite1.show();

       
System.out.println("remove 1");
       
composite1.remove(leaf1);
       
composite1.show();

       
System.out.println("remove composite 2");
       
composite1.remove(composite2);
       
composite1.show();

       
System.out.println("print composite 2");
       
composite2.show();
    }
}


결과화면




UML






Template Method Pattern (템플릿메소드패턴)

Template Method Pattern

 


정의


상위 class에서 처리의 흐름을 제어하고, 하위class에서 처리의 내용을 구체화 한다


여러 class에서 공통되는 사항은 상위 추상 class에서 구현하고, 각각의 세부내용은 하위 class에서 구현한다


그 결과 코드의 중복을 줄이고, Refactoring에 유리한 패턴으로 상속을 통한 확장 개발 방법으로 Strategy Pattern과 함께 많이 사용되는 pattern이다.


 

Hook method


필수적이지 않은 부분으로 필요에 따라 선택적으로 sub-class에서 구현할 경우 사용한다


Abstract class에서 정의된 어떠한 행도 하지 않을 수 있고, sub-class의 공통된 동작을 할 수도 있다


또한, sub-class에서 hook method를 재정의 하여 사용하는 방법도 있다


Sub-class에 따라 algorithm에 원하는 내용을 추가할 수 있도록 하는 것이 hook-method의 존재 이유다.


 

요구사항


1.    멤버 함수들의 접근 범위 지정을 명확히 한다.


2.    가상함수, 일반함수로 선언 하는 것 에 대한 기준이 필요


3.    Virtual 함수의 수를 최소화한다.



l  Hollywood Principle

Sub-classupper-class에 구성요소로써 활용될 수 있지만, sub-class upper-class를 호출하여 자신을 호출하게 하면 안된다는 것이다


Template Method Pattern에서도 이러한 원칙을 따르고 있는데


설명을 더하면, abstract class에서는 sub-class에 있는 기능을 호출할 수 있지만


sub-class에서는 abstract-class template method를 호출하거나 수정할 수 없다는 것이다.

 


장점


상위위 class template method에서 알고리즘이 기술되어 있으므로,


하위 class에서는 알고리즘을 일일이 기술할 필요가 없다.


 

소스코드


상위클래스

public abstract class CaffeineBeverageWithHook {

         void prepareRecipe(){

                 boilWater();

                 brew();

                 pourInCup();

                 if(customerWantsCondiments()){

                          addCondiments();

                 }

         }

 

         abstract void brew();

         abstract void addCondiments();

 

         void boilWater(){

                 System.out.println("Boiling water");

         }

 

         void pourInCup(){

                 System.out.println("Pouring into cup");

         }

 

         boolean customerWantsCondiments(){

                 return true;

         }

}

 

하위클래스(coffee)

public class CoffeeWithHook extends CaffeineBeverageWithHook{
         @Override
         void brew() {
                 // TODO Auto-generated method stub
                 System.out.println("Dripping Coffee through filter");
         }
 
         @Override
         void addCondiments() {
                 // TODO Auto-generated method stub
                 System.out.println("Adding Sugar and Milk");
         }
         @Override
         public boolean customerWantsCondiments() {
                 // TODO Auto-generated method stub
                 String answer = getUserInput();
 
                 if(answer.toLowerCase().startsWith("y")){
                          return true;
                 }else{
                          return false;
                 }
         }
 
         private String getUserInput(){
                 String answer = null;
 
                 System.out.println("would you like milk&sugar with your coffee (y/n) ?");
                 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
 
                 try {
                          answer = in.readLine();
                 } catch (Exception e) {
                          // TODO: handle exception1
                          System.err.println("IO error trying to read your answer");
                 }
 
                 if(answer==null){
                          return "no";
                 }
                 return answer;
         }
}

 

하위클래스(tea)

public class TeaWithHook extends CaffeineBeverageWithHook{
 
         @Override
         void brew() {
                 // TODO Auto-generated method stub
                 System.out.println("Steeping the tea");
         }
 
         @Override
         void addCondiments() {
                 // TODO Auto-generated method stub
                 System.out.println("Adding Lemon");
         }
         @Override
         public boolean customerWantsCondiments() {
                 // TODO Auto-generated method stub
                 String answer = getUserInput();
 
                 if(answer.toLowerCase().startsWith("y")){
                          return true;
                 }else{
                          return false;
                 }
         }
 
         private String getUserInput(){
                 String answer = null;
 
                 System.out.println("would you like lemon with your tea (y/n) ?");
                 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
 
                 try {
                          answer = in.readLine();
                 } catch (Exception e) {
                          // TODO: handle exception1
                          System.err.println("IO error trying to read your answer");
                 }
 
                 if(answer==null){
                          return "no";
                 }
                 return answer;
         }
 
}

 

Main

public class BeverageTestDrive {
         public static void main(String[] args) {
                 TeaWithHook teaWithHook = new TeaWithHook();
                 CoffeeWithHook coffeeWithHook = new CoffeeWithHook();
                 
                 System.out.println("\n Making tea");
                 teaWithHook.prepareRecipe();
                 
                 System.out.println("\nMaking coffee");
                 coffeeWithHook.prepareRecipe();
         }
}

 

 

결과

Making tea

Boiling water

Steeping the tea

Pouring into cup

would you like lemon with your tea (y/n) ?

y

Adding Lemon

 

Making coffee

Boiling water

Dripping Coffee through filter

Pouring into cup

would you like milk &sugar with your coffee (y/n) ?

y

Adding Sugar and Milk



UML


'SW > DesignPattern' 카테고리의 다른 글

Composite Pattern (컴포지트 패턴)  (0) 2017.09.15
Iterator Pattern (반복자 패턴)  (0) 2017.09.14
Facade Pattern (퍼사드 패턴)  (0) 2017.09.07
Adapter Pattern (어댑터 패턴)  (0) 2017.09.07
Command Pattern (커맨드 패턴)  (0) 2017.09.07

'디자인'에 해당되는 글 3건

1 →