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

Adapter Pattern (어댑터 패턴)

Adapters Pattern

 

정의 : class interface client에서 필요로 하는 다른 interface 변환한다. Adapter 이용하면 interface 호환성문제로 없는 class들을 연결해서 있다.




그림과 같이 Adapter 소스만 변경하여 Existing System Vender Class 연결해 준다.

 


1.    Class Adapter Pattern


A.    장점 : Adapter 전체를 다시 구현할 필요가 없다.


B.     단점 : 상속을 활용하기 때문에 유연성이 떨어진다.
(java
다중상속 -지원 X)


C.    Class Adapter에서는 다중 상속을 이용하기 때문에 JAVA에서는 사용할 없다 (간접적 다중상속 지원 à Interface 이용)

 

2.    Object Adapter Pattern


A.    장점 : Composition 사용하기 때문에 유연성이 뛰어나다.


B.     단점 : Adapter Class 대부분의 코드를 구현해야 하기 때문에 효율성이 떨어진다.

 


사용방법


1.    Client에서 Target Interface 사용하여 method 호출함으로써 adapter 요청을 한다.


2.    Adapter에서는 adapter interface 사용하여 요청을 adapter 대한 하나 이상의 메소드를 호출로 변환한다.


3.    Client에서는 호출 결과를 받긴 하지만 중간에 Adapter 역할이 있었는지 전혀 없다.



기존의 Duck interface

public interface Duck {

         public void quack();

         public void fly();

}

 

Duck interface implement하는 MallardDuck

public class MallardDuck implements Duck{
    @Override
    public void fly() {
     // TODO Auto-generated method stub
     System.out.println("Quack");
    }
 
    @Override
    public void quack() {
     // TODO Auto-generated method stub
     System.out.println("I'm flying");
    }
}

 

하지만 turkey 오고싶다. 그래서 interface 만들어준다.

public interface Turkey {
    public void gobble();
    public void fly();
}

 

, Duck interface 사용하기 위해 Adapter 사용한다.

public class TurkeyAdapter implements Duck{
    Turkey turkey;
     public TurkeyAdapter(Turkey turkey){
     this.turkey=turkey;
    }
     @Override
    public void quack() {
     turkey.gobble();
    }         
    @Override
    public void fly() {
     turkey.fly();
    }
}


 

Turkey interface implements 하는 WildTurkey

public class WildTurkey implements Turkey{
    @Override
    public void fly() {
     // TODO Auto-generated method stub
     System.out.println("Gobble gobble");
    }
    @Override
    public void gobble() {
     // TODO Auto-generated method stub
     System.out.println("I'm flying a short distance");
    }
}

 

Main 소스

public class DuckTestDrive {
    public static void main(String[] args) {
     MallardDuck duck = new MallardDuck();
     WildTurkey turkey = new WildTurkey();
     Duck turkeyAdapter = new TurkeyAdapter(turkey);
     
     System.out.println("The turkey says...");
     turkey.gobble();
     turkey.fly();
     System.out.println("The Duck says...");
     testDuck(duck);
     System.out.println("The TurkeyAdapter says...");
     testDuck(turkeyAdapter);
    }
    
    public static void testDuck(Duck duck){
     duck.quack();
     duck.fly();
    }
}

 

결과 화면

The turkey says...

I'm flying a short distance

Gobble gobble

The Duck says...

I'm flying

Quack

The TurkeyAdapter says...

I'm flying a short distance

Gobble gobble



UML



'패턴'에 해당되는 글 4건

1 →