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

Facade Pattern (퍼사드 패턴)

Facade Pattern


 

정의


어떤 sub-systeminterface에 대한 통합된 interface를 제공하고 facade에서 고수준 interface를 정의하기 때문에 sub-system을 쉽게 사용할 수 있다.



 

장점


더욱 더 간단한 interface를 만들 수 있다는 것이다


또한 client부와 sub-system을 분리할 수도 있다


만약 clientfacade로 만들었다고 하면 interface가 달라졌을 때 client는 변경할 필요 없이 facade만 변경하면 된다


그리고 표면에 드러나 있는 interface를 사용하고 그 내부는 알 필요가 없다.


 

l  최소지식원칙(=Law of Demeter)


system design에 있어서 어떤 object든 그 object와 상호작용하는 class의 수에 주의해야 하며, object들과 어떤 식으로 상호작용하는지도 주의해야 한다는 원칙이다.


위 원칙을 지키기 위해서는 4 종류의 object method만 호출하면 되는데, 다음과 같다.


1.    객체 자체


2.    Methodparameter로 전달된 object


3.    method에서 생성하거나 instance를 만든 object


4.    object에 속하는 구성요소 (A has B)




Adapter Pattern, Facade Pattern, Decorator Pattern의 차이점


l  Adapter Pattern : interface를 다른 interface로 변환하는 것이다. Interface를 변경해서 client에서 필요로 하는 interface로 적응시키는 위한 용도로 호환성을 위한다.


l  Facade Pattern : interface를 간단하게 바꾸는 것으로, 어떤 sub-system에 대한 간단한 interface를 제공하기 위한 용도로 사용되며 간편할 때 사용한다.


l  Decorator Pattern : interface를 바꾸지 않고 기능만 추가하는 것으로 Object를 감싸서 새로운 기능을 추가하기 위해서 사용한다.



 

정리


1.    기존 class를 사용하려고 하는데 interface가 맞지 않으면 adapter pattern을 사용한다.


2.    interface 또는 여러 interface를 단순화 시키거나 통합시켜야 한다면 facade pattern을 사용한다.


3.    Adapter Patterninterfaceclient가 원하는 interface로 바꾸는 역할을 하고 Facade Pattern은 복잡한 sub-system과 분리하는 역할을 한다.


4.    Adapter Pattern은 다중 Adapter로 만들 수 있고 Façade Pattern은 한 sub-system에 여러 개 만들 수 있다.

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



Command Pattern (커맨드 패턴)

Command Pattern

 

정의

command pattern을 이용하면 요구사항을 객체로 캡슐화 하여, 요구사항을 나중에 이용할 수 있도록 메소드 이름과 매개변수 등 요구사항에 필요한 정보를 집어넣을 수 있다. 요청내역을 큐에 저장하거나 로그를 기록할 수 있으며, 작업취소 기능도 있다.

 

구성

1.    Client : Client ConcreteCommand를 생성하고 Receiver를 설정한다.

2.    Receiver : 요구사항을 수행하기 위해 어떤 일을 처리하는 객체

3.    Invoker : 명령이 있으며 execute() 메소드를 호출하여 커맨드 객체에게 특정 작업을 수행하도록 요청

4.    Command : 모든 커맨드 객체가 구현할 Interface. Receiver에게 시킬 모든 명령은 execute()메소드를 호출함으로써 수행되며, Receiver에게 특정 작업을 처리하라는 지시를 내린다.

5.    ConcreteCommand : 특정 행동과 Receiverbind한다. Invoker에서 execute()메소드 호출을 통해 요청하고 ConcreteCommand 객체에서는 Receiver에 있는 메소드를 호출함으로써 작업을 처리한다.


 

구현방법

1.    기능Class(Receiver)들을 캡슐화 한다.

2.    기능Class를 외부에 작동할 수 있는 Command (interface or class)를 추상화 및 구현한다.

3.    기능실행class(Invoker) command 타입 객체로 구현한다.

4.    기능실행class(Invoker) setter method를 구현하여 Command 타입 object를 가져오도록 한다.

5.    Invoker Object, Receiver Object, Command 타입의 Receiver Objectparameter로 하는 Object들을 생성한다.

6.    Invoker Object에서 Setter method를 호출하여 해당 commandAction Object를 등록하고, invoker에서 Command execute할 수 있는 method를 호출한다.

 

특징

request부와 execute부를 분리하고, undo, 보관, log생성이 가능하다.

장점

Receiver Command만 추가하면 dynamic하게 object 호출이 가능하다.

단점

Object 구성부가 추가되면 abstract부분부터 수정해야 한다.


 

Client 소스

public class RemoteLoader {

         public static void main(String[] args) {

                 RemoteControl remoteControl = new RemoteControl();

                 CeilingFan ceilingFan = new CeilingFan("Living Room");

                 CeilingFanHighCommand ceilingFanHigh = new CeilingFanHighCommand(ceilingFan);

                 CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan);

                 remoteControl.setCommand(2, ceilingFanHigh, ceilingFanOff);

                 System.out.println(remoteControl);

                 remoteControl.onButtonWasPushed(0);

                 remoteControl.offButtonWasPushed(0);

                 remoteControl.undoButtonWasPushed();

                 remoteControl.onButtonWasPushed(2);

                 remoteControl.offButtonWasPushed(2);

                 remoteControl.undoButtonWasPushed();

         }

}


Receiver 소스

/**
 * Receiver (천장의 FAN 작동시킨다)
 */
public class CeilingFan {
         public static final int HIGH=3;
         public static final int MEDIUM=2;
         public static final int LOW=1;
         public static final int OFF=0;
         String location;
         int speed;
         public CeilingFan(String location) {
                 // TODO Auto-generated constructor stub
                 this.location=location;
                 speed=OFF;
         }
         public void high(){
                 speed = HIGH;
         }
         public void medium(){
                 speed = MEDIUM;
         }
         public void low(){
                 speed = LOW;
         }
         public void off(){
                 speed = OFF;
         }
         public int getSpeed(){
                 return speed;
         }
}


 

Invoker

/**
 * Invoker
 */
public class RemoteControl {
         Command[] onCommands;
         Command[] offCommands;
         Command undoCommand;
         public RemoteControl() {
                 // TODO Auto-generated constructor stub
                 onCommands = new Command[7];
                 offCommands = new Command[7];
 
                 Command noCommand = new NoCommand();
                 for(int i=0; i<7; i++){
                          onCommands[i] = noCommand;
                          offCommands[i] = noCommand;
                 }
                 undoCommand = noCommand;
         }
         /**
          * @param slot
          * @param onCommand
          * @param offCommand
          * 리모컨의  slot command 넣는다.
          */
         public void setCommand(int slot, Command onCommand, Command offCommand) {
                 onCommands[slot] = onCommand;
                 offCommands[slot] = offCommand;
         }
         /**
          * @param slot
          * slot ON button 눌리면  slot OnCommand execute()메소드가 호출된다.
          */
         public void onButtonWasPushed(int slot) {
                 onCommands[slot].execute();
                 undoCommand = onCommands[slot];
         }
         public void offButtonWasPushed(int slot) {
                 offCommands[slot].execute();
                 undoCommand = offCommands[slot];
         }
         @Override
         public String toString() {
                 StringBuffer stringBuffer = new StringBuffer();
                 stringBuffer.append("\n-----Remote Control-----\n");
                 for(int i=0; i<onCommands.length; i++){
                          stringBuffer.append("[slot " + i + "] " + onCommands[i].getClass().getName()+"\n");
                 }
                 return stringBuffer.toString();
         }
         public void undoButtonWasPushed() {
                 // TODO Auto-generated method stub
                 undoCommand.undo();
         }
}


Command 소스 (fan의 속도를 높임)

/**
 *  class fan 속도를 높이고, Receiver CeilingFan 사이를 bind한다.
 */
public class CeilingFanHighCommand implements Command{
         CeilingFan ceilingFan;
         int prevSpeed;
 
         public CeilingFanHighCommand(CeilingFan ceilingFan) {
                 // TODO Auto-generated constructor stub
                 this.ceilingFan=ceilingFan;
         }
         
         /* 
          * Invoker에서 execute 호출하면 fan 속도를 높인다.
          * unDo 구현하기 위해 prevSpeed 속도 값을 저장해 둔다.
          */
         @Override
         public void execute() {
                 // TODO Auto-generated method stub
                 prevSpeed = ceilingFan.getSpeed();
                 ceilingFan.high();
         }
         
         /* 
          * prevSpeed 기준으로 speed 설정한다.
          */
         @Override
         public void undo() {
                 // TODO Auto-generated method stub
                 if(prevSpeed == CeilingFan.HIGH){
                          ceilingFan.high();
                 }else if (prevSpeed == CeilingFan.MEDIUM){
                          ceilingFan.medium();
                 }else if (prevSpeed == CeilingFan.LOW){
                          ceilingFan.low();
                 }else if (prevSpeed == CeilingFan.OFF){
                          ceilingFan.off();
                 }
         }
}


 

Command소스 (Fan의 속도를 설정)

public class CeilingFanOffCommand implements Command{
         CeilingFan ceilingFan;
         int prevSpeed;
 
         public CeilingFanOffCommand(CeilingFan ceilingFan) {
                 // TODO Auto-generated constructor stub
                 this.ceilingFan = ceilingFan;
         }
 
         @Override
         public void execute() {
                 // TODO Auto-generated method stub
                 prevSpeed = ceilingFan.getSpeed();
                 ceilingFan.off();
         }
 
         @Override
         public void undo() {
                 // TODO Auto-generated method stub
                 if(prevSpeed == CeilingFan.HIGH){
                          ceilingFan.high();
                 }else if(prevSpeed==CeilingFan.MEDIUM){
                          ceilingFan.medium();
                 }else if(prevSpeed==CeilingFan.LOW){
                          ceilingFan.low();
                 }else if(prevSpeed==CeilingFan.OFF){
                          ceilingFan.off();
                 }
         }
}

 

Command

/**
 *  class null object이다.
 * return object 없어도 client에서 null 처리하지 않아도
 * 되게   사용한다.
 */
public class NoCommand implements Command{
         @Override
         public void execute() {
                 // TODO Auto-generated method stub
 
         }
         @Override
         public void undo() {
                 // TODO Auto-generated method stub
 
         }
 
}


Interface

/**
 * 모든 command에서 구현해야 하는 interface이다.
 * 모든 command execute method 통해서 호출되며,
 *  method에서는 receiver 특정 작업을 처리하게 한다.
 */
public interface Command {
         public void execute();
         public void undo();
}

 


구현


Singleton Pattern (싱글턴 패턴)

Singleton Pattern


정의


1.    Singleton Pattern은 해당 Classinstance가 하나만 만들어진다.


2.    어디서든 그 instance에 접근할 수 있게 한다.


3.    class에서 하나뿐인 instance를 관리하게 한다.


4.    Instance가 사용될 때 똑 같은 instance를 만드는 것이 아닌, 동일 instance를 사용하게끔 하는 것이다.

 

고전적인 Singleton Pattern

public class Singleton {
    private static Singleton uniqueInstance;
 
    private Singleton(){}
 
    public static Singleton getInstance() {
        if(uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

전역변수로 instance를 생성하는데 private static 키워드를 사용한다.


Static이 붙은 class 변수는, 인스턴스화에 상관없이 사용할 수 있다.


하지만, private 접근 제어자로 인해

Singleton.uniqueInstance

와 같은 방법으로 접근할 수 없다


이 상태에서 private 키워드를 붙이는데 그러면 new 키워드를 사용할 수 없게 된다.


그 결과 외부 class가 위 classinstance를 가질 수 있는 방법은, getinstance() method를 사용하는 방법밖에 없다.


 하지만 위의 방법은 Multi-threading과 관련해서 문제가 생긴다


       그 이유는 thread getinstance() method를 호출하면 instance가 두 번 생길 수 있기 때문이다.


       이러한  문제를 방지하기 위해, getinstance() method를 동기화 시킨다.

public class Singleton {
    private static Singleton uniqueInstance;
    // 기타 인스턴스 변수
    private Singleton() {}
    //synchronized 키워드만 추가하면 
    // 두 스레드가 이 메소드를 동시에 실행시키는 일은 일어나지 않게 된다.
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}


위와 같이 할 수 있는데 이 방법도 문제가 있다. 수 많은 thread들이 getinstance()

method를 호출하기 위해 동기화 하면 성능이 떨어진다.

이러한 문제를 방지하기 위해서 instance를 필요할 때 생성하는 것이 아니라, 처음부터 생성하는 방법이 있다.

public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

위와 같이 하는 방법 외에도 DCL(Double-Checking Locking)을 사용하여 getinstance()에서 동기화되는 부분을 줄이는 방법이 있다.


DCL을 사용하면 instance가 생성되어 있는지 확인한 후, 생성이 되어있지 않을 때만 동기화를 할 수 있다


Volatile 키워드를 사용해서 multi-threading을 사용하더라도변수가 Singleton instance로 초기화 되는 과정이 올바르게 할 수 있다.

public class Singleton {
    private volatile static Singleton uniqueInstance;
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            //이렇게 하면 처음에만 동기화 된다
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}


Factory Pattern (팩토리 패턴)

Factory Pattern



모든 factory pattern에서는 app의 구상 클래스에 대한 dependency를 줄여줘서 loose-coupling을 도와준다. 또한 Object 생성을 encapsulation 한다. 주로, Super-class와 다수의 sub-class가 있을 때 input을 기준으로 하나의 sub-class를 반환할 경우 사용한다. 구상 class가 아닌 추상 class/interface에 맞춰서 코딩할 수 있게 해주는 강력한 기법이다.

 


  Simple Factory

    디자인 패턴이라 할 수 없고, 자주 쓰이는 관용구라 할 수 있다. Client와 구상 class를 분리시키기 위한 간단한 기법으로 활용한다.

 


  Factory Method Pattern

    Object를 생성하기 위한 Interface를 정의하는데, Object를 생성하는 부분을 Sub-Class에 위임하는 pattern이다. ‘new’키워드를 호출하는 부분을 Sub-Class에 위임하는 것이다. 그 결과, class간의 결합도(class의 변경이 있을 경우, 다른 class에 영향을 주는 정도)가 떨어진다. SimpleFactory와의 차이점은 Factory Method Pattern은 어떤 구현을 사용할지를 sub-class에서 결정하는 framework를 만들 수 있다는 것이다. SimpleFactory에서도 Object생성을 Encapsulation하는 방법을 사용하긴 하지만 생성하는 제품을 마음대로 변경할 수 없기 때문에 Factory Method Pattern처럼 강력한 flexibility를 제공하지는 못한다.

 


  Abstract Factory Pattern

    Interface를 이용하여, 연관성이 있는 많은 sub-class를 특정 그룹으로 묶어 일괄적으로 수정할 수 있도록 하는 pattern이며, 제품을 추가하려면 Interface를 수정하면 된다. 예를 들어 특정 library를 배포하는데 국가마다 기능이 상이하다면 abstract factory pattern을 이용해서 일괄적으로 기능을 변경하여 대처할 수 있다.

 


●  Dependency Inversion Principle

   추상화된 것에 의존하도록 하고, 구상 클래스에 의존하지 않도록 해야 한다.


1.    어떤 변수에도 구상 클래스에 대한 reference를 지정하지 않는다(new 연산자를 사용하는 것이 reference를 사용하게 되는 것)

2.    구상 클래스에서 유도된 클래스를 만들지 않는다. (구상클래스에서 유도된 클래스를 만들면 특정 구상 클래스에 의존하게 된다. 추상화된 것을 사용해야 한다.)

3.    클래스에 이미 구현된 메소드를 override 하지 않는다. (이미 구현된 메소드를 override 한다는 것은 클래스가 제대로 abstract 되지 않았다고 할 수 있다. 클래스에 method를 정의할 때에는 모든 sub-class에서 공유할 수 있는 것으로 정의해야 한다.



 

구현


Decorator Pattern (데코레이터 패턴)

Decorator Pattern

 

정의

상속 또는 interface 이용하여 Type 맞춰 객체의 추가적인 요건을 동적으로 추가한다. 서브클래스(decorator class) 만드는 것을 통해서 기능을 유연하게 확장할 있는 방법을 제시한다. 중요한점은 Decorator 상속을 통해서 행동을 물려받는 것이 목적이 아니라는 것이다.

 


구성

1.    구성요소는 직접 쓰일 수도 있고 decorator 감싸져서 쓰일 있다.

2.    ConcreteComponent 새로운 행동을 동적으로 추가하게 된다.

3.    decorator 안에는 Component 객체가 들어있다.

4.    Decorator 자신이 decorate 구성요소와 같은 interface abstract class implement 한다.

5.    Decorator component 상태를 확장할 있다.

6.    ConcreteDecorator에는 Object decorate하고 있는 것을 위한 interface 변수가 있다.

7.    decorator에서 새로운 method 추가할 있다.

 

l  OCP (Open-Closed Principle) : class 확장에 대해서는 open 해야 하지만 코드 수정에 대해서는 close 해야 한다. 기존 코드는 건드리지 않은 채로 확장을 통해서 새로운 행동을 추가할 있도록 코드의 수정을 허용한다는 것으로 기능 추가에 유연하기에 튼튼한 디자인을 있다.


 

장점

1.    기존 코드를 수정하지 않고도 행동을 확장하는 방법이 된다.

2.    Composition delegate 통해서 실행 중에 새로운 행동을 추가할 있다.

3.    상속대신 decorator pattern 통해서 행동을 확장할 있다.

4.    OCP 충실하면서 유연한 디자인을 만들어 있다.

 


단점

1.    잡다한 클래스들이 많아 있다.

2.    객체가 다른 객체들로 쌓여 있을 있기 때문에 파악하기 힘들 있다.

3.    디자인 유연성 면에서는 좋지 않다.

 


구현 (Coffee 제조)


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

Singleton Pattern (싱글턴 패턴)  (0) 2017.09.05
Factory Pattern (팩토리 패턴)  (0) 2017.09.05
Observer Pattern (옵저버 패턴)  (0) 2017.09.05
Strategy Pattern (스트레테지 패턴)  (0) 2017.09.05
DesignPattern이란? 왜쓰지?  (0) 2017.09.05

Observer Pattern (옵저버 패턴)

Observer pattern

 

정의

객체의 상태가 바뀌면 객체에 의존하는 다른 객체들이 자동으로 갱신되는 방식으로 객체들 간에 one-to-many 의존성을 정의한다. 객체의 상태 변화를 관찰하는 Observer들을 객체에 등록하여 상태 변화가 있을 경우 객체가 메소드를 통해 Observer들에게 알리는 Design pattern이다. 주로 분산 이벤트 핸들링을 구현하는데 사용하거나, 하나의 객체에 여러 개가 의존할 경우 사용하는 것이 대부분이다.

 

l  자바에는 java.util.Observable이라는 API 있다. 하지만 interface 아니라 class이기에 이미 다른 super class extends하고 있는 class에서는 사용할 없다는 점과, 직접 구현이 불가능하다는 단점이 존재한다. 그리고, 핵심 method(‘setChange()’) protected 선언이 되어있기때문에 해당 method 외부에서 사용하는 것이 불가능하다 라는 단점이 있다.

 


조건

Loose-Coupling : 필요한 이유는 객체 간의 결합도가 높아질수록 유지보수는 힘들기 때문이다.  Loose-Coupling 특징으로는 주체가 observer 대해서 observer interface implement하고 있다는 점을 제외하고는 있는 정보가 없다. Loose-Coupling 장점은 Observer 언제든지 추가할 있고, 새로운 Observer 해도 주체를 변경할 필요가 없으며 주체와 observer 독립적으로 사용할 있다. 또한 Observer 바뀌어도 서로 영향을 미치지 않는다는 것이다.


 

구성

 2개의 역할을 하는 interface(subject, observer) 생성한다. subject라는 interface observer들을 관리하는 method들을 가진다. method들은 observer add, delete, notifyObserver 이렇게 3가지 역할을 한다. Observer interface 정보를 업데이트 해주는 update method 가진다. Subject implment class 정보를 제공하는 subject 되며 observer 객체들을 가진다. 그리고 observer interface implement class notifyObserver method 호출하면서 알려줄 때마다 update method 호출된다.

 


정리

1.    Observer pattern object state 바뀌면 해당 object 의존하는 다른 object들에게 신호를 보내고 자동으로 정보가 갱신되는 1:N 관계를 가진다.

2.    연결은 interface 이용하여 loose-coupling 유지한다.

3.    Observer pattern push 방식(주체 object에서 observer 데이터를 보내는 방식) pull 방식(observer에서 주체 object 데이터를 가져가는 방식)으로 언제든지 구현할 있다.

4.    JAVA에서는 Observable class Observer interface 제공한다.


 

구현 (팬관리)


Strategy Pattern (스트레테지 패턴)

Strategy Pattern

 

정의

어떤 동작을 하는 알고리즘을 정의하고 각각을 Encapsulation하고 Delegate를 통해서 어떤 행동을 할지 결정하는 패턴이다. Strategy pattern을 이용하면 알고리즘을 사용하는 client는 독립적으로 알고리즘을 변경할 수 있다. 상속보다는 구성을 이용한다. ‘AB보다는 ‘A B가 있다라는 패턴이다. 정리하자면, 하나의 결과를 만드는 목적은 동일하나, 그 목적을 달성할 수 있는 방법이 여러가지 존재할 경우 기본이 되는 template method와 함께 많이 사용되는 패턴이다.

 


사용하기 좋은 경우

1.    행동만 조금씩 다를 뿐 관련된 class가 많을 때

2.    알고리즘의 변형이 필요할 때

3.    User가 몰라야 하는 data를 사용하는 알고리즘이 있을 때

4.    하나의 class가 많은 행동을 정의하고, 이런 행동들이 그 class의 연산 안에서 복잡한 조건문의 형태일 때

 

 

구성

1.    여러 개의 sort algorithm을 정의하고 필요에 따라 선택적으로 적용한다.

2.    App에서 달라지는 부분을 찾아내어, 그렇지 않은 부분으로부터 분리한다.

3.    구현이 아닌 interface에 맞춘다.

4.    inheritance보단 composition을 활용한다.

 


장점

1.    알고리즘을 Encapsulation 시켰기 때문에 확장성이 좋다.

2.    프로그램이 실행 중에 알고리즘을 setting할 수 있다.

3.    로직을 독립적으로 관리하기 쉽다는 장점이 있다.

4.    코드의 중복을 줄일 수 있다.

 


단점

Strategy Object Context Object 사이 의사소통에 overhead가 있다.


    UML

   


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

Singleton Pattern (싱글턴 패턴)  (0) 2017.09.05
Factory Pattern (팩토리 패턴)  (0) 2017.09.05
Decorator Pattern (데코레이터 패턴)  (0) 2017.09.05
Observer Pattern (옵저버 패턴)  (0) 2017.09.05
DesignPattern이란? 왜쓰지?  (0) 2017.09.05

'design'에 해당되는 글 11건

1 2 →