티스토리 툴바

달력

052012  이전 다음

  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  

■ 기본 커맨드 패턴

1. 커맨드 객체는 특정 객체에 대한 특정 작업 요청을 함께 캡슐화한다.


[그림] 커맨드 객체 구조

실제로, 요청을 처리하는 리시버(receiver)에 대한 참조와 Execute() 메소드를 가지고 있다. Execute()에서는 리시버의 메소드를 호출함으로써 필요에 따라서는 일련의 메소드를 복수해 호출해서 비즈니스 요청을 수행한다. 커맨드 객체를 호출하는 측에서는 단지 Execute() 메소드만 호출하면 된다. 그 커맨드가 구체적으로 Execute()를 어떻게 구현하는지는 알 필요가 없다.

2. 작업을 요청하는 쪽(리모콘)과 작업을 하는 쪽(TV, CD 플레이어)을 커맨드 객체를 통해서 분리할 수 있다.

3. 캡슐화시 매개변수를 써서 여러 가지 다른 요구사항을 집어넣을 수도 있다.

ConcreteCommand(매개변수);

4. 커맨드 패턴을 사용하면 요청 내역을 큐에 저장해서 스케쥴러, 작업큐같은 프로그램을 만들거나 또는 작업 내역을 스택성 로그로 기록해서 도중에 에러가 발생하면 롤백하는 구조의 프로그램을 만들 수도 있다. 이것을 좀더 정교하게 만들면 트랜잭션 시스템을 구현하는 것도 가능하다.

레퍼런스

Head First Design Patterns 스토리가 있는 패턴 학습법

-Oreilly, 한빛 미디어

■ 커맨드 패턴 변형

앞에서는 커맨드 객체가 리시버(Receiver)에 대한 참조와 그 리시버를 이용해서 요청을 구현해야 하는 Execute()가 있어야 한다. 이런 경우는 리시버에 대한 참조를 가지고 있어야 한다는 점에서 리시버와 커맨드 객체와의 커플링이 존재하게 된다. 이런 커플링을 없애기 위해서 때로는 리시버와 Execute() 대신에 단일 이벤트 멤버로 대신할 수 있다.


[그림] 커맨드 객체

1. One handler : 하나의 이벤트 핸들러에서 여러 UI 이벤트(메뉴, 툴바)를 핸들링할 수 있다.

2. the business logic for handling the command : 그 하나의 이벤트 핸들러에 해당 명령을 핸들링할 수 있는 비즈니스 로직을 중앙 관리할 수 있다.

■ 커맨드 패턴 구현의 핵심 요소

커맨드 관리자

- 애플리케이션에서 필요로 하는 모든 커맨드들의 중앙 저장소

- 애플리케이션이 커맨드 목록을 관리할 수 있는 컬렉션용 API를 제공.

레퍼런스

Command Management
--------------------------------------------------------------------------------------------------------

Command Pattern

메소드 호출을 캡슐화 하는 패턴이다. 메소드 호출을 캡슐화하면 계산 과정의 각 부분들을 결정화 시킬 수 있기 때문에 계산하는 코드를 호출한 객체에서는 어떤 식으로 일을 처리해야 하는 지에 대해서 전혀 신경 쓰지 않아도 된다.

 

 

1.     Command Pattern의 정의

커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있다. 또한 요청한 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업 취소 기능도 지원 가능하다,

 

2.     Command Pattern의 특성

커맨드 패턴을 사용하면 어떤 작업을 요청한 쪽하고 그 작업을 처리한 쪽을 분리시킬 수 있다. 커맨드 객체는 특정 객체에 대한 특정 작업 요청을 캡슐화한다. 커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구 사항을 캡슐화한 것이다. 행동과 리시버를 한 rorrorc 집어넣고, excute()라는 메소드 하나만 외부에 공개하여 이 메소드 호출에 의해서 리시버에서 일련의 작업이 처리된다. 외부에서 볼 때는 어떤 객체가 리시버 역할을 하는지, 그 리시버로 실제로 어떤 일을 하는지 알 수 없다. Excute()메소드를 호출하면 요구 사항이 처리된다는 것만 알 수 있을 뿐이다. 커맨드를 확장해서 여러 개의 커맨드를 한꺼번에 호출할 수 있게 해주는 매크로 커맨드 패턴도 있다.

 

3.     Command Pattern의 클래스 다이어그램



 

4.     Command Pattern의 구성

A.     클라이언트는 ConcreteCommand를 생성하고 Receiver를 설정한다.

B.     Invoker에는 명령이 들어있으며, execute()메소드를 호출함으로써 커맨드 객체에게 특정 작업을 수행해 달라는 요청을 하게 된다.

C.     Command는 모든 커맨드 객체에서 구현해야 하는 인터페이스 이다. 모든 명령은 execute()메소드 호출을 통해서 수행되며, 이 메소드에서는 리시버에 특정 작업을 처리하라는 지시를 전달한다.

D.     Execute() 메소드에서는 리시버에 있는 메소드를 호출하여 요청된 작업을 수행한다.

E.      Receiver는 요구사항을 수행하기 위해 어떤 일을 처리해야 하는지 알고 있는 객체이다.

F.      ConcreteCommand는 특정행동과 리시버 사이를 연결 해준다.

 

5.     Command Pattern의 장단점

커맨드 패턴은 작업을 요청하는 객체와 실제 작업을 수행하는 객체를 분리시켜주므로 시스템의 결합도가 낮아질 수 있으며, 두 객체가 독립적으로 변경 가능한 장점을 가진다. 커맨드 및 그 하위 클래스는 기존 클래스와 무관하게 확장이 가능하며, 확장된 클래스들은 클라이언트의 별다른 수정 없이 사용이 가능하다. Macro Command처럼 커맨드패턴에 컴포지트 패턴을 복합해서 적용할 경우 복잡한 작업을 기본적인 작업들로 구조화시켜 다룰 수 있는 장점이 있다
----------------------------------------------------------------------------------------------------

커맨드패턴
 
메소드의 호출을 캡슐화 한다는 것이 핵심입니다.
메소드 호출을 캡슐화하면 계산과정의 각 부분들을 결정화시킬수 있기 때문에 계산하는 코드를 호출한 객체에서는 어떤식으로 처리해야하는지에 대해 전혀 신경 쓰지 않아도 됩니다.
 
* 클라이언트는 ConcreateCommand를 생성하고 Receiver 를 설정합니다.
* Invoker 에는 명령이 들어 있으며 , execute() 메소드를 호출함으로써 커맨드 객체에게 특정 잡업을 수행해 달라는 요구
  를 하게 됩니다.
* Command 는 커맨드 객체에서 구현해야하는 인터페이스입니다.
* Receiver 는 요구사항을 수행하기 위해 어떤일을 처리해야 하는지 알고 있는 객체입니다.
* ConcreteCommand 는 특정행동과 리시버 사이를 연결해 줍니다. 인보커에서 execute() 호출을 통해 요청하면
  ConcreteCommand객체에서 리시버에 있는 메소드를 호출함으로써 그 작업을 처리합니다.
 
 
예제를 보면서 설명하겠습니다..
다음 코드는 전등을 켜는 리모콘을 생성하여 처리하는 과정을 보여줍니다.
 
우선은 커맨트 인터페이스를 생성합니다. 당연히 execute()메소드를 정의합니다.

 Command.java
 

public interface Command {
 public void execute();
}

 

 실제 작업을 정의한 Receiver 객체입니다. 

 Light.java
 

public class Light {
 
 public void on()
 {
  System.out.println("불 켜다");
 }
 public void off()
 {
  System.out.println("불 끄다");
 }

}

 

 실제작업을 실행할 커맨드 클래스 ConcreteCommand 를 구현한다. 

 LightOnCommand.java
 

public class LightOnCommand implements Command {
 
 Light light;
 
 public LightOnCommand(Light light)
 {
  this.light = light;

 // 생성자에서 이 커맨드 객체를 제어할 특정 정보를 전달합니다..그 객체는 light라는 인스턴스 변수에 저장되고

 // execute() 메소드가 호출되면 light 객체가 바로 그 요청에 대한 리시버가 됩니다.
 }

 public void execute() {
  light.on();
 }

}

 

버튼이 하나 밖에 없는 리모콘을 만들어 보면. 다음과 같다.

 SimpleRemoteControl.java
 

public class SimpleRemoteControl {
 Command slot;
  // 커맨드를 집어넣을 슬롯이 하나 밖에 없습니다. 이걸가지고 장비를 제어합니다.
 public SimpleRemoteControl() {}
 

 

// 슬롯을 가지고 제어할 명령을 설정하기 위한 메소드
 public void setCommand(Command command)
 {
  slot = command;
 }
 
 public void buttonWasPressed()
 {
  slot.execute();
 }
}

 

자 이제 테스트클래스를 만들고 실행해보자.

 RemoteControlTest.java
 

public class RemoteControlTest {

 public static void main(String[] args) {
  
  SimpleRemoteControl remote = new SimpleRemoteControl();

//remote 변수가 인보커(invoker)역할을 합니다.

//필요한 작업을 요청할 때 사용할 커맨드 객체를 인자로 전달 받는다.


  Light light = new Light();

//리시버 객체를 생성


  LightOnCommand lightOn = new LightOnCommand(light);

//커맨드 객체를 생성합니다. 이때 리시버를 전달합니다.
  
  remote.setCommand(lightOn);

//커맨드 객체를 인보커한테 전달합니다.
  remote.buttonWasPressed();


 }

}

 

 

커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구사항을 캡슐화한다.

이렇게 하기 위해서 행동과 리시버를 한 객체에 집어 넣고, execute() 라는 메소드 하나만 외부에 공개하는 방법을 사용한다. 메소드 호출에 의해서 리시버에서 일련의 작업이 처리된다. 외부에서 볼때는 어떤 객체가 리시버 역할을 하는지 그 리시버에서 실제로 어떤 일을 하는지 알 수 없습니다. 다만 execute()메소드를 호출하면 요구사항이 처리 된다는 것만 알수 있다.

 

정리하자면

커맨드 패턴을 사용하면 요청하는 객체와 그 요청을 분리시킬수 있다.

이렇게 분리시키는 과정의 중심에는 커맨드객체가 있으며 이객체가 행동이 들어있는 리시버를 캡슐화 한다.

인보커에서는 요청을 할때는 커맨드 객체의 execute()메소드만 호출하면 된다.

execute() 메소드에서는 리시버에 있는 행동을 호출한다.

 

적용예

스트러츠1.0 에서 Action클래스의 execute 메소드가 있다.

 

정의

요청내역을 객체로 캡슐화하여 클라이언트를 서로 다른 요청에 따라 매개변수화 할 수 있습니다.

Posted by 인천총각





1. synchronized 키워드를 사용한다.




2. 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버린다.


3. DCL(Double Checking Locking)을 써서 getInstance()에서 동기화 되는 부분에 대한 체크를 줄입니다.


유일무이한 객체

  • 싱글턴패턴은 인스턴스가 하나 뿐인 특별한 객체를 만들 수 있게 해주는 패턴이다.
  • 어떤 용도로 쓰는 건가?
    • 스레드 풀이라던가, 캐시, 대화상자, 사용자설정, 디바이스드라이버 등등 객체가 전체프로그램에서 오직 하나만 생성되어야 하는 경우에 사용한다.
  • 그럼 전역변수로 static 으로 선언해서 사용하면 되지 않느냐?
    전역 변수로 객체를 생성하면 어플리케이션이 실행 될 때 객체가 생성될 것이다.(P208 맨밑줄)
    그 객체가 자원을 많이 차지 한다면 사용도 되기전에, 괜히 자원만 차지한다. 사용하지 않는다면 아무 쓸 데 없는 객체가 되겠지.

고전적인 싱글턴 패턴 구현법

  • 조심하세요.. 이 코드에 문제가 있다는 것을 알게 될 것입니다

public class Singleton {
 
  //private으로 Sinleton클래스의 유일한 인스턴스를 저장하기 위한 정적 변수를 선언
  private static Singleton uniqueInstance;

  //생성자를 private로 선언했기 때문에 Singleton에서만 클래스를 만들 수 있다.
  private Singleton() {}

  //클래스의 인스턴스를 만들어서 리턴해 준다.
  public static Singleton getInstance() {
    if(uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqueInstance;
  }

}



초콜릿 공장

  • 만약 애플리케이션에서 ChocolateBoiler 인스턴스가 두 개 이상 만들어지게 되면 어떤 문제가 생길까?


 public class ChocolateBoiler {
  private boolean empty;
  private boolean boiled;
 
  private ChocolateBoiler() {
    //이 코드는 보일러가 비어있을 때만 돌아갑니다
    empty = true;
    boiled = false;
  }
 
  public void fill() {
    if (isEmpty()) {
      //보일러가 비어있을 때만 재료를 집어 넣습니다.
      //원료를 가득 채우고 나면 empty와 boiled 플래그를 false로 설정합니다.
      empty = false;
      boiled = false;
      // 보일러에 우유/초콜릿을 혼합한 재료를 집어넣음
    }
  }
 
  public void drain() {
    //보일러가 가득 차 있고(비어있지 않고), 다 끓여진 상태에서만
     //보일러에 들어있는 재료를 다음 단계로 넘깁니다.
    //보일러를 다 비우고 나면 empty 플래그를 다시 true로 설정합니다.
    if (!isEmpty() && isBoiled()) {
      // 끓인 재료를 다음 단계로 넘김
      empty = true;
    }
  }

//보일러가 가득 차 있고 아직 끓지 않은 상태에서만
//초콜릿과 우유가 혼합된 재료를 끓입니다.
//재료가 다 끓고 나면 boiled 플래그를 true로 설정합니다  
  public void boil() {
    if (!isEmpty() && !isBoiled()) {
      // 재료를 끓임
      boiled = true;
    }
  }

  public boolean isEmpty() {
    return empty;
  }
 
  public boolean isBoiled() {
    return boiled;
  }
}


public class ChocolateBoiler {
  private static ChocolateBoilerSingleton cb;
  private boolean empty;
  private boolean boiled;
 
  private ChocolateBoiler() { empty = true;
    boiled = false;
  }

  public static ChocolateBoilerSingleton getInstance(){
    if(cb == null)
      cb = new ChocolateBoilerSingleton();

    return cb;
  } 
 
  public void fill() {
    if (isEmpty()) {
      empty = false;
      boiled = false;
    }
  }
 
  public void drain() {
    if (!isEmpty() && isBoiled()) {
      empty = true;
    }
  }


  public void boil() {
    if (!isEmpty() && !isBoiled()) { boiled = true;
    }
  }

  public boolean isEmpty() {
    return empty;
  }
 
  public boolean isBoiled() {
    return boiled;
  }
}


싱글턴 패턴의 정의

  • 싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어진다,
  • 어디서든지 그 인스턴스에 접근할 수 있도록 한다.
  • 클래스에서 자신의 단 하나뿐인 인스턴스를 관리하도록 만들면 된다.


문제가 생겼다.

도대체 무슨 일이 일어난 거지? 새로 만든 싱글턴 코드가 얼마 전까지만 해도 문제 없이 잘 돌아가고 있었는데.
조금 전에 다중 스레드를 사용하도록 초콜릿 보일러 컨트롤러를 최적화시킨 걸 빼면 이런 문제를 일으킬 만한 게없는데...

두 개의 스레드에서 여기에 있는 코드를 실행시킨다고 가정해 보면.
두 스레드가 다른 보일러 객체를 사용하게 될 가능성은 없는지 따져 보자.
 ChocolateBoiler boiler = ChocolateBoiler.getInstance(); fill(); boil(); drain();

멀티스레딩 문제 해결 방법

  • getInstance()를 동기화시키기만 하면 멀티스레딩과 관련된 문제가 간단하게 해결된다.

public class Singleton {
  private static Singleton uniqueInstance;
  // 기타 인스턴스 변수
  private Singleton() {}
 
  //synchronized 키워드만 추가하면 두 스레드가 이 메소드를 동시에 실행시키는 일은 일어나지 않게 된다.
  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
       uniqueInstance = new Singleton();
    }
    return uniqueInstance;
  }
// 기타 메소드
}
  • 이렇게 하면 문제가 해결되긴 하겠지만, 동기화를하면 속도 문제가 생기지 않나?
    동기화는 불필요한 오버헤드만 증가시킬 수 있다.

  • 더 효율적인 방법은 없을까요?

    1. getInstance()의 속도가 그리 중요하지 않다면 그냥 내비 둔다.

    • 메소드를 동기화하면 성능이 100배 정도 저하된다는 것은 기억해 두자
    • 만약 getInstance( )가 애플리케이션에서 병목으로 작용한다면 다른 방법을 생각해봐야 한다.

    2. 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버린다.


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

      private Singleton() {}

      public static Singleton getInstance() {
        return uniqueInstance;
      }
    }
  • 이런 접근법을 사용하면 클래스가 로딩될 때 JVM에서 Singleton의 유일한 인스턴스를 생성해 준다.
  •  

    3. DCL(Double-Checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄인다.

    • DCL(Double-Checking Locking)을 사용하면, 일단 인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만 동기화를 할 수 있다.
    • volatile 키워드를 사용하여 멀티스레딩을 쓰더라도 uniqueInstance 변수가 Singleton 인스턴스로 초기화 되는 과정이 올바르게 할 수 있다.
    • DCL은 자바 1.4 이전 버전에서는 쓸 수 없다

    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;
      }
    }

    핵심 정리

    • 어떤 클래스에 싱글턴 패턴을 적용하면 애플리케이션에 그 클래스의 인스턴스가 최대 한 개 까지만 있도록 할 수 있다.
    • 싱글턴 패턴을 이용하면 유일한 인스턴스를 어디서든지 접근할 수 있도록 할 수 있다.
    • 자바에서 싱글턴 패턴을 구현 할 때는 private 생성자와 정적 메소드, 정적 변수를 사용 한다.
    • 다중 스레드를 사용하는 애플리케이션에서는 속도와 자원 문제를 파악해보고 적절한 구현법을 사용해야 한다.
    • DCL을 사용하는 방법은 자바2 버전 5(자바 1.5)보다 전에 나온 버전에서는 쓸 수 없다는 점에 주의.
    • 클래스 로더가 여러 개 있으면 싱글턴이 제대로 작동하지 않고, 여러 개의 인스턴스가 생길 수 있다.

    --------------------------------------------------------------------------------------------------------

    О situation : 초코릿 공장

     

    초코릿 끓이는 장치를 컴퓨터로 제어한다.

     

    보일러에서 초콜릿과 우유를 받아서 초코바를 만드는 단계로 넘겨준다.

     

    재료를 넣고 끓이고 비우고 하는 단계를 순서대로 거쳐야 하는데 여러 개의 객체가 생성되면 해당 작업의 동기화가

     

    맞질 않아 단계가 뒤죽박죽이 되어 버린다.

     

    즉 각각의 동작들이 동기화가 되어야만 한다는 말이다.

     

     

    О point : 한 개의 인스턴스만 생성하는 객체를 만든다.

     

    객체의 생성자를 private 로 선언한다,

     

    인스턴스를 생성하여 반환하는 static 메소드를 선언한다.

     

    New 부분을 동기화하여 상호배재 현상에 대응한다.

    <?xml:namespace prefix = o /> 

     

    О solution :

     

    싱글턴 1 >

     

    Public class Singleton {

     

                 Private volatile static Singleton uniqueInstance;

     

                 Private Singleton(){}

     

                 Public static Singleton getInstance(){

                               If(UniqueInstance == null){ 

                               //메소드 전체를 동기화 시키지 않기 때문에 UniqueInstance 가 생성되지 않은

                               //처음 1번 빼고는 오버헤드를 발생시키지 않는다.

                                            Synchronized (Singleton.class){

                                                         If(UniqueInstance == null){

                                                                      uniqueInstance = new Singleton();

    }

    }

    }

    }

     

    }

     

    싱글턴2 >

     

    Public class Singleton {

     

                 Private volatile static Singleton uniqueInstance = new Singleton();

     

                 Private Singleton(){}

     

                 Public static Singleton getInstance(){

    return uniqueInstance;

                 }

     

    }

     

    처음에만 동기화하여 오버헤드를 줄인다. uniqueInstance null이 아니면 동기화 되지 않는 것이다.

     

    О thinking :

     

    전역변수 vs 싱글턴

     

    전역변수를 사용할 경우 처음부터 끝까지 인스턴스를 가지고 있어야 하므로 자원의 낭비가 심하다.

     

    해당클래스가 하나만 만들어지고 어디서든지 그 인스턴스에 접근 할 수 있도록 한다.

     

    새로운 인스턴스가 생성되는 것이 아니란 것이다.

      

    싱글턴이 쓰이는 경우

     

    ->>>>>>>>>>> 

     

    1) 스레드 풀 (서버 프로그램 할때 데몬 스레드 만들어본 경험있을 것이다)

     

    2) 사용자 설정, 레지스트리 설정등의 환경설정 관련 객체

     

       3) 로그 기록용 객체, 프린터, 그래픽 카드 같은 디바이스 드라이버

    ----------------------------------------------------------------------------------------------------


    Java 를 사용하여 Singleton 패턴을 구현할 경우 주의해야 할 사항이 있다. 멀티 쓰레드 환경에서 하나 이상의 객체가 생성되는 현상이 발견된다는 것이다.
    이 현상을 어디서 접하게 되었는고 하니... Tomcat 기반의 웹 어플리케이션을 개발하면서 Singleton 패턴으로 커넥션풀 라이브러리를 만들어 두었다. 여러분도 알다시피 Tomcat에는 클래스파일이 변경되었는지 자동으로 감지하는 "Watch Dog" 이라는 기능이 탑재되어 있어서, 클래스의 변경을 감지하여 웹 어플리케이션(컨텍스트)을 다시 로딩하는 작업을 수행한다. 당시 데이터베이스의 커넥션 자원을 너무 많이 소모하는 것 같아서 디버깅 작업에 착수하였는데, WatchDog에 의하여 클래스를 다시 로딩할 때 Singleton 패턴으로 정의한 클래스의 객체가 여러 개 생겼음을 발견하게 되었다!!!
    당연히 Singleton 패턴을 사용하면 같은 JVM 상에서는 오직 하나만 존재할 것으로 생각했던 내게 큰 충격이 아닐 수 없었다. 하여 문제를 해결하고자 검색해보니 멀티 쓰레드 환경에서 이런 문제가 꽤나 자주 있었음을 알 수 있었으며, 다음의 두 아티클을 찾을 수 있었다. 이 글들은 Java에서의 Singleton 패턴 구현 방식에 대하여 가장 잘 정리하고 있다:
    1. http://www-106.ibm.com/developerworks/java/library/j-dcl.html?dwzone=java
    2. http://c2.com/cgi/wiki?JavaSingleton
    첫 번째 자료는 IBM 에 게시된 자료인데 그냥 참고로 읽고, 주로 두 번째 자료를 숙독하기 바란다. 내가 두 번째 자료를 접했을 당시에는 아래와 같은 해결책이 제시되어 있었다.
    public class Singleton {   // <b>final</b> 키워드를 사용한 것에 주의할 것!   private static final Singleton _theInstance = new Singleton();    private Singleton() {    }    // <b>synchronized</b> 키워드가 사용되지 않았다는 점도 눈여겨 보자   public static Singleton getInstance() {      return _theInstance;    }  }
    위 해결책은 Darren Hobbs 가 제안한 방법으로 Java Spec에 근거하여 제시한 방법이다. 하지만 Adam 은 이 방식이 거의 대부분의 상황에 잘 적용되긴 하지만 Tomcat 과 JBoss 두 컨테이너에서 테스트 해보니 생성자(constructor)에서 멤버 변수를 초기화하지 못하는 문제가 발견되었다고 문제를 제기하였다.
    최근에 다시 두 번째 링크를 방문해 본 결과, Josh Bloch의 "Effective Java" 책 저술작업에 기여했던 Scot Floess 라는 사람이 David Geary가 JavaWorld에 게재한 글에 대해 Thread-safety 문제를 지적하며 제시한 해결책을 접할 수 있었다(※ 참고 : http://www.javaworld.com/javaworld/jw-05-2003/jw-0530-letters.html). 일단 Darren Hobbs 가 제안한 방법과 거의 같지만, 1) 클래스 선언 시 final 키워드를 붙여 상속이 불가능하도록 하였으며, 2) static 의 inner class를 사용하여 Singleton 패턴의 클래스를 감싸고 있는 형태가 다르다. 분명 이런 형태로 Singleton 패턴의 클래스를 생성하는 작업은 그 자체로 매우 무거운 작업이긴 하지만, 간단하면서도 완벽한 방식의 Singleton 패턴 구현 방식이라고 한다. 자세한 것은 링크를 참고하도록 하자.
     /*  * final 키워드를 사용하여 이 클래스로부터 상속이 불가능하도록 하였다.  */ public final class Singleton {    // static inner class (여기도 final 키워드 사용) 를 사용하여 Singleton 클래스의    // 객체를 생성함    private static final class SingletonHolder {       // 역시 이 내부에서도 static final 키워드 사용       static final Singleton singleton = new Singleton();     }     private Singleton() {}     public static Singleton getInstance() {       return SingletonHolder.singleton;     }  }
    --------------------------------------------------------------------------------------------------------

    전역 변수 vs Singleton

    단순 static과 싱글턴의 차이점은:싱글턴이 static 객체로 구현되더라도, 메모리나 자원을 필요로 하기전까지 기다렸다 생성하는 게으른 초기화를 할 수 있다는 것입니다.
    또다른 차이는, static 크래스는 interface를 구현할 수 없습니다.
    interface에 의해 표현되는 클래스를 구현하기 위해선 싱글톤 패턴으로 만들어야 합니다.



    --------------------------------------------------------------------------------------------------------

    싱글톤 은 디자인 패턴 중에 한 가지의 방법입니다.

    디자인 패턴이란  선배 개발자들의 개발 방법 중 괜찬타 싶은 방법에 이름을 붙여 놓은 것입니다. 즉. 패턴들은 자바라는 언어뿐만 아니라 씨 씨플 씨샵 파이톤 등등 여러 곳에서 응용되어질 수 있는 방법입니다.

     

    그럼 싱글톤 디자인 패턴은 어떤 개발 방법일까요?

     

    우리는 객체를 생성할때 new  라는 키워드를 사용합니다

     

    A a=new A();

     

    이렇게  객체를 생성하면

    heap 영역에는 A 클래스의 인스턴스(생성된 객체)가  생성 되어 지고

    그 A 클래스의 인스턴스 를  가르 키는 a 라는 변수가 스택에 저장 되어 집니다.

     

    그리고

     

    A a=new A();

    A b=new A();

    를 할경우엔

     

     

    이런식으로 메모리 상에 올라 오게 됩니다.

    한 가지 유념해야 할것이 있습니다. 힙 영역에 객체가 올라오고 내려 올때 시간이 많이 걸립니다.

    그리고 스택 저 화살표 연결 고리가 끊어지면 GC(가비지 컬렉션) 의 대상이 되는데 GC  수거 작업이 진행될시 컴퓨터가 멈춰 질정도로 느려지기 도 합니다.

     

    그래서  자주 사용되는 객체를 생성할 땐

     

     

    이런식으로 heap 영역에 한개만 올려놓고 스택에선 같은 객체를 가르키도록 코딩하는 것이 바로 싱글톤 디자인 패턴입니다.

    저런 식으로 해놓을 경우 힙영역에 객체가 올라 가고 내려가는 시간을 줄이고 또한 힙영역의 메모리 역시 쓰레기 객체들을 줄일수 있는 방법이 됩니다.

     

    그러나 ~ 한가지 조심할 점이 있습니다.

    a와 b 가 똑같은 객체를 가르키고 있기 때문에 A 클래스의 멤버 변수 역시 공유한다는 사실입니다.

    즉 a 에서 멤벼 변수값을 바꾸게 되면 b 의 멤버 변수 역시 값이 바뀌게 되어 이점 유념하여서 싱글톤 디자인 패턴을 사용할 객체 인지 판별을 해야합니다.

     

    즉  싱글톤 패턴은 멤버 변수가 없이 멤버 함수들만 으로 구성되어진 클래스 들에서 많이 사용되어 집니다

     

     

    ---싱글톤 디자인 패턴을 적용한  객체 만드는법--

     

    여러가지 방법이 있으나 가장 간단한 방법을 알려드리겠습니다.

     

    public class A{

    static A aInstence=new A();

    public static A getInstence(){

    return aInstence; 

    }

    private A(){

    }

    }

     

    ---------

    바로 위의 방법입니다

    위 의 방법으로 설계된 객체를 생성할 때는

    A a=new A();

    로 하면 에러가 나게 됩니다. 왜냐마혐 생성자가  private 로 되어 있기 때문입니다.

    그래서 스태틱 변수인 getIntence() 라는 메서드를 통해서 인스턴를 받아야합니다

     

    A a=A.getInstence();

    A b=A.getInstence();

     

    이렇게 할경우  힙영역에 A 인스턴스는 하나올라 오게 되고, a 와 b 는 같은 인스턴스를 가리키게 됩니다.

    Posted by 인천총각

    1. 디자인 패턴 소개( 스트래티지 패턴 )
    일단 패턴 소개에 앞서서 가장 기본적으로 선행 되어야 할 내용이 있다면 상속 및 인터페이스 및 다형성의 정확한 이해다.
    이를 위해서 아주 간단한 소스와 내용을 적고 그리고 본문의 스트래티지 패턴에 대해서 소개를 하는 형태로 진행을 해 나가겠다

    1. 왜 다형성을 이해해야 하는가?

    일단 진행에 앞서 요즘 스프링 참 많이 듣지 않는가? 여기도 스프링 저기도 스프링(아 따뜻한 봄이 오는구나..).. 우리 스터디에서도 불과 몇달전에 스프링을 약간 아주 약간맛을 아주 조금봤다. 그렇다 아주조금... 하고 싶은 이야기는 이 스프링을 공부 할때 dependency injection 이라는 개념이 있었는데 이게 지금 설명 하려는다형성을 기반으로 이해를 해야 하기 때문이다. 왜냐구 물어보면.. dependency injection 이라는게 살펴보고 나면 결국 스트래티지 패턴 하고 다를게 전혀 없기 때문이다.
    즉 스프링의 dependency injection <= 스트래티지 패턴 <= 다형성 이런 과정이 성립 하기 때문에 무엇보다 앞서서 다형성을 설명 하려 한다. 말이 길었다 소스로 보자.
    ------------------------------------------------------------------------------------------------------------------------
    public class Parent {
      private String name = "부모";
      public void printName(){
        System.out.println("name" + name);
      }
    }

    public class A extends Parent() {
       public void printName(){
        System.out.println("난 자식이다");
      } 
    }

    Parent a6 = new A();
    a6.printName();
    ------------------------------------------------------------------------------------------------------------------------
    위의 코드는 설명이 필요 없을거 같다. 메인을 구현해서 테스트해 보면 다형성이 어떤건지 바로 확인 할수 있을것이다.
    잘이해가 안가거나 좀더 자세한 설명을 원하신다면 다음 링크를 따라가서 읽어 보기 바란다.
    위에서 보여진 예제와 같이 다형성은 별게 아니다. 정말 그럴까? 아무것도 모를땐 ..그게 머야 당연한거아니야 별거 아니네 라고 답할수 있겠지만 이게 어떻게 쓰이는지 보게 된다면 별게 아니다 라고 하는게 아니라 "오 주여" 를 외치게 될거이다. 이제 외치러 가보자.

    2. 문제에 대한 인지..

    '문제에 대한 인지' 그렇다 이 말은 정말 중요하다. 모든 것에 앞서서 현재 어떤문제가 있는지 깨닫는 과정은 정말 중요한 시간이다. 문제가 무엇인지 알아야 솔루션을 제공할것이 아닌가?
    지금부터는 소스를 보면서 어떻게 '문제를 인지'하게 되는지 같이 살펴보자.
    상황은 다음과 같다. 아래 그림과 같이 오리클래스로 부터 상속을 받은 2개의 오리가 있다 MallaaedDuck 와 RedHeadDuck 이 그것 이다.

    이녀석들은 오리로 부터 상속을 받았기에 당연히 울수있고(quack()) 물에뜨고(swim()) 각각의 모습이 틀리기에(한놈음 머리가 빨간색이지 않는가?) 오버라이딩한 display() 메소드를 가진다.
    사용자는 문득 오리도 날수 있다라는 사실을 인지 하였기에 Duck클래스에 fly() 메소드를 추가 하기로 하였다. 그리고 RubberDuck(고무오리) 클래스를 추가 하였다.
    위 상황에서 상속이 가진 힘에 의해 RubberDuck 역시 울수있고(quack()) 물에뜨고(swim()) 고유의 외모(display()) 그리고 날수(fly())하게 되었다.
    고무오리는 날지 못한다. 더군다나 울수도 없고 말이다.
    우리는 이문제를 해결하기 위해 처음에 오리마다 각각의 고유의 모습을 그려내었던(display()) 방법인 오버라이딩을 해결책으로 떠올릴수 있고.
    행동 즉 메서드를 상속을 통해서 제공하므 인해 발생 하는 문제에 대해서 깨달을수 있다.(문제의 인지)

    첫번째 문제는 서브 클래스에 코드가 중복이 된다는점.
    두번째 문제는 모든 오리의 행동을 알기 힘들다는점.(어떤오리가 추가 될지 어떻게 알수 있단 말인가???)
    세번재 문제는 코드를 변경했을때 (또는 새로운 행동을 추가 했을때) 다른 오리들에게 원치 않는 영향을 끼칠수 있다는점.
    네번째 문제는 실행시점 그러니까 오리를 만들어내는 시점에 특징을 바꾸기 힘들다는 것이다.(이건 믿에서 알아 보도록 하자)

    2.1 첫번재 솔루션

    우리는 경험을 통해서 배운다. 글렇지 않은가? 위에서 고무오리를 하나 추가해 봄으로써 앞으로 어떤일이 펼처질수 있을지 상상 할수 있었다.(어떤 오리가 튀어 나올지 모른다 )
    예전에 자바 기초를 배울때 이런 내용을 들은거 같다. 메소드의 선언만 가지고 있고 구현은 안된 인터페이스란게 있다라고..
    그렇다 인터페이스를 이용해서 지금 문제를 일으키고 있는 행동들을 분리하자 라는 생각에 Flayble 과 Quackable 인터페이스를 만들고 모든 오리는 이걸 구현하게 하자!


    일단 보기만 해도 문제가 있어보인다. 울지 못하고 날지 못하는 1개의 오리 때문에 나머지 오리들이 전부 2개의 인터페이스를 가져다가 구현한다....

    2.2 두번재 솔루션

    이제 문제는 명확해 졌다. 클레스에서 변화는 부분과 변화지 않는 부분을 분리해 내는게 첫번째 이고. 전체가 아닌 일부만 날수 있거나 울수 있게 혹은 울수 없게 만들어야 한다.
    이제..드디어 다형성을 쓸 시간이 왔다. 그분이 오실 시간 이란 말이다. 다음을 보자.


    이그림을 볼때 바로 소스가 어떻게 그려질지 머리에 그려지는 분도 있을것이다. 많은 노력과 좋은 환경 그리고 시간을 필요로 했을거라 생각이 된다. 이 문서를 작성하는 이유는
    그러지 못한 사람들을 위해서 이므로 차근 차근 풀어 보도록 하자.

    일단 추상 클래스 Duck 부터 살펴 보면 2개의 인터페이스 형식의 레퍼런스변수(참조변수)를 선언 하고 있다. 그리고 performQuack 과 performFly 2개의 메소드를 통해서 어떤 행동을
    (꽥꽥 울어야 하는지 아니면 ?꽉 울어야 하는지 혹은 날개로 날아야 하는지 로켓으로 날아야 하는지 아니면 날수 없던지) 취해야 할지 행동을 제어하는 인터페이스에 위임 한다.
    이 위임 이라는 용어가 조금 눈에 거슬릴거 같은데, 별거 아니다 그냥 넘겨준다는 뜻이다.
    지금까지 기술한 내용을 코드로 써보면 다음과 같다.

    ------------------------------------------------------------------------------------------------------------------------
    public abstract class Duck{
      Flyable    flyBehavior;
      Quackable  quackBehavior;

      public abstract void display();

      public void performQuack() {
       quackBehavior.quack()
      }

      public void performFly(){
        flyBehavior.fly();
      }

      public void setFlyBehavior(Flyable fb){
        flyBehavior = fb;
      }
     
      public void setQuackBehavior(Quackable  qb){
        quackBehavior = qb
      }
    }
    ------------------------------------------------------------------------------------------------------------------------
    코드 자체는 전혀 어려운게 없다. 핵심은 performQuack(), performFly() 이 두개의 메소드다. 위에서 기술한 다형성의 내용을 기억 하고 있다면 이게 어떻게 쓰일지 그림이 그려질 것이다.
    setFlyBehavior()와 setQuackBehavior()의 인자인 Quackable 과 Flyable 는 아규먼트 다형성 혹은 프로모션이 라고 불리는 다형성의 형질을 이용해 값을 넘기는 아규먼트다 메인메소드 에서는 아마 다음과같이 구현될걸다
    ------------------------------------------------------------------------------------------------------------------------

      .
       .
       Flyable   fb = new FluWithWings();
       Quackable qb = new Squack();

       xxx.setFlyBehavior(fb);    // 한줄로쓰면 다음과 같이 된다. xxx.setFlyBehavior(new FluWithWings());
       xxx.setQuackBehavior(qb);

       xxx.performQuack();
       xxx.performFly();

       .
       .
    ------------------------------------------------------------------------------------------------------------------------

    이렇게 만들면 부모의 메소드로 자식의 메소드를 호출하는 메서드 다형성이 발생되는 형태를 된다.
    위와 같이 위임할 객체를 선택하는 seter를 만들어 두면 실행 시점에 행동에 대한 제어가 쉬워진다. Duck 클래스를 손대지 않고 단순히 performQuack과 performFly 메소드를 호출
    하는것많으로 행동되야할 객체를 선택할수 있게 된다.

    자 여기 까지 왔는데.. 이 스트래티지 패턴이 "어떤점이 좋은지 잘 모르겠다" 라고 하신 다면 처음에 "문제의 인지"를 할수 있었던 방법을 써보자.
    이번에 태안 사고로 인해 유전자가 이상해진 오리가 나타난 거다. 이 오리는 보통오리와 달라서 로켓 추진으로 날수 있게 되었다 라고 해보자.
    이 이오리가 가진 특성을 클래스에 추가하려면 어떻게 하면 될까?

    간단하다. Flyable 인터페이스를 구현한 RoketWithWings라는 클래스를 하나 추가만 하면된다. 다음과 같이 말이다


    멋지지 않는가?? 기능이 하나 추가 되었다고 Duck를 변경 하고 그를 상속받은 각각의 클래스를 수정 하는 이런 소모적인 일을 할필요가 전여 없다. 그렇다. 이건 영어식 표현으로 하면 Cool 한 거다. 이 패턴 꼭 기억 하고 가자
    Posted by 인천총각

    1. Strategy 패턴은..

    Strategy 패턴은 구성을 이용합니다. Starategy는 바뀌는 부분을 인터페이스로 분리하여 처리합니다. 그 인터페이스의 구현체를 바꿈으로서 로직을 변경하는 것입니다. 인터페이스를 사용하는 클래스(그 클래스를 Context라고 합니다.)입니다.

    2. 예제

    ------------------------ 상위 인터페이스 --------------------
    package ch06_Strategy;

    public interface Seller {
        public void sell();
    }

    ------------------------- 인터페이스 구현체1 -----------------
    package ch06_Strategy;

    public class CupSeller implements Seller {
        public void sell() {
            System.out.println("컵을 팔아요.");
        }
    }
    ------------------------- 인터페이스 구현체2 -----------------
    package ch06_Strategy;

    public class PhoneSeller implements Seller {
        public void sell() {
            System.out.println("전화기를 팔아요.");
        }
    }
    ------------------------- 인터페이스 사용하는 클래스 -----------------
    package ch06_Strategy;

    public class Mart {
        private Seller seller;
        public Mart(Seller seller) {
            this.seller = seller;
        }
        public void order(){
            seller.sell();
        }
    }
    ------------------------- 테스트 클래스 -----------------
    package ch06_Strategy;

    public class Test {
        public static void main(String[] args) {
            Seller cupSeller = new CupSeller();
            Seller phoneSeller = new PhoneSeller();
            Mart mart1 = new Mart(cupSeller);
            mart1.order();
            Mart mart2 = new Mart(phoneSeller);
            mart2.order();
        }
    }

    위에서 보시다 시피 테스트 클래스에서는 Seller의 sell()을 호출하지 않습니다. Mart의 order()를 호출합니다. Seller의 메쏘드는 외부로 공개되지 않습니다.
    Mart 클래스가 여기서는 외부로 공개되는 Context가 됩니다. Mart는 멤버 변수로 Seller를 가집니다. Mart에서 가지는 Seller를 바꿔치기함으로써 Mart의 order()에서 실제 실행되는 로직이 달라질 수 있습니다.

    3. Strategy의 유용성

    예제에서는 Context 클래스가 한 개의 Strategy 인터페이스만을 가집니다. Seller 외에 여러가지 인터페이스를 가질 수도 있습니다. 예를 들어 만드는 사람, 운반하는 사람, 파는 사람은 각각 다를 수 있습니다. 예제에서는 코드를 줄이기 위해 파는 사람만 2가지 종류의 클래스를 만들었습니다. 그러나, 만드는 사람 인터페이스와 운반하는 사람 인터페이스 등을 만들고 그 구현체 들을 만들면, 상당히 다양한 로직이 나올 수 있습니다. 만드는 사람의 구현체가 3종류, 운반하는 사람의 구현체가 3종류, 파는 사람의 구현체가 3종류라하면, 만들어서 운반해서 파는 로직은 총 3*3*3= 27가지가 나옵니다. 이를 상속을 이용한 클래스를 제작하면, 27가지의 구현체가 필요합니다. Strategy를 쓰면, 9개의 구현체만 필요하며, 또 인터페이스를 이용한 프로그램이 가능합니다.

    ==========================================================================================================================
    HeadFirst DesignPattern - Strategy Pattern
    ==========================================================================================================================

    상속 받은 클래스에 있어서 변경되는 사항이 발생할경우.코드을 변경하였을 경우 상속 받은 클래스들에게 원치 않은 영향을 끼칠경우
    인터페이스로 따라 분리하여 바뀌는 부분을 처리하는 것을 말한다.즉, 특정 서비스나 기능을 필요로 하거다, 여러개의 방법이 있을경우
    Strategy 패턴을 쓰는 것이 적합하다!!

    왜?
    부모로 부터 상속 받았을 경우 원치 않은 경우 도있다.
    서브클래스 마다 각각의 행동이 바뀔수 있는데도 모든 서브 클래스에서 한 행동을 사용하도록 하는 것은 그리 올바르지 못하다.
    그럴때 일일이 재정의 해야하는 불편함과 다시 필요로 할경우 재사용 측면에서 불편함이 있다.
    그럼 인터페이스를 사용하면 될텐데??
    메서드의 코드를 일일이 인스턴스마다 넣어줘야하는 불편함, 그리고 자바 인터페이스에는 구현된 코드가 전혀 들어가지 않기 때문에
    코드 재사용을 할수 없다는 문제점이 발생한다.

    어떻게?
    코드에 새로운 요구사항이 있을 때마다 바뀌는 부분이 있다면, 그 행동을 바뀌지 않는 부분으로 부터 골라내서 분리해야 한다는 것이다
    즉, 바뀌는 부분을 따로 뽑아서 캡슐화 시킨다. 그렇게 하면 나중에 바뀌지 않는 부분에는 영향을 미치지 않은 채로 그 부분만 고치거나 확장할 수 있다!!

    이러한 원리가 봐로 Strategy패턴으로 따로 분리시킨것을 인터페이스로 만들어 놓는다!!

    즉, 부모 클래스를 서브클래스 들이 상속을 받되, 일부 기능은 따로 인터페이스로 구분하여, 부모클래스가 has a 관계로 가진다!! 즉
    ues 관계가 형성되는 것이다.
    기능 인터페이스는 각각의 기능 구현 서브 클래스들을 가지고 있다.

    구성(위방법-인터페이스)을 이용하여 시스템을 만들면 유연성을 크게 향상시킬 수 있다. 단순히 알고리즘군을 별도의 클래스 집합으로 캡슐화할 수 있도록 만들어주는 것 뿐아니라, 구성요소로 사용하는 객체에서 올바른 행동 인터페이스를 구현하기만 하면 실행시에 행동을 바꿀 수도 있게 해준다.

    부모클래스의 기능구현 인스턴스 변수는 인터페이스 형식이기 떄문에,(다형성을 활용하여) 실행시에 동적으로 다른 클래스를 할당할수 있다. 부모형의 서브클래스를 만듬과 동시에 각각의 기능(방법) 클래스를 호출해주면 !! 인터페이스를 통해 구현되어진다!!!

    스트레티지 패턴에서는 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트래티지을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변결할 수 있다!!!
    ------------------------------------------------------------------------------------------------------------------------
    ex)
    //부모 클래스
    public abstract class Duck {

     FlyBehavior flyBehavior;//행동 인터페이스 형식의 레퍼런스 변수 
     QuackBehavior quackBehavior;//행동 인터페이스 형식의 레퍼런스 변수 

     public Duck(){ }

     public abstract void display();//추상 메소드 (상속받은 모든)클래스는 구현해야함

     public void performFly()
    {
      flyBehavior.fly(); //행동 클래스에 위임
     }
     public void performQuack()
    {
      quackBehavior.quack();//행동 클래스에 위임
     }
     public void swim(){
      System.out.println("모든오리는 물에 뜹니다");
     }
     public void setFlyBehavior(FlyBehavior fb){  //오리들의 행동을 바꾸기 위한 메소드
      flyBehavior = fb;
     }
     public void setQuackBehavior(QuackBehavior qb){//오리들의 행동을 바꾸기 위한 메소드
      quackBehavior = qb;
     }
    }
    ------------------------------------------------------------------------------------------------------------------------
    public interface FlyBehavior  // 이 인터페이스는 모든 나는 행동에 대한 클래스
    {
     public void fly();
    }
    ------------------------------------------------------------------------------------------------------------------------
    public class FlyNoWay implements FlyBehavior{ //실제로 날수 있는 오리들의 나는 행동을 구현
    public void fly()
    {
      System.out.println("저는 못날아요");
     }}
    ------------------------------------------------------------------------------------------------------------------------
    public class FlyWithWings implements FlyBehavior{//실제로 날수 있는 오리들의 나는 행동을 구현
    public void fly()
    {
      System.out.println("날고있어요");
     }}
    ------------------------------------------------------------------------------------------------------------------------
    public class FlyRocketPowered implements FlyBehavior{//실제로 날수 있는 오리들의 나는 행동을 구현
    public void fly()
     {
      System.out.println("로켓 추진으로 날아갑니다");
     }}
    ------------------------------------------------------------------------------------------------------------------------
    public interface QuackBehavior  // 이 인터페이스는 모든 소리에 대한 클래스
    {
     public void quack();
    }

    ------------------------------------------------------------------------------------------------------------------------
    public class MuteQuack implements QuackBehavior{//실제로 오리들의 소리 구현
     public void quack()
    {
      System.out.println("<<쉿>>");
     } }
    ------------------------------------------------------------------------------------------------------------------------
    public class Quack implements QuackBehavior{//실제로 오리들의 소리 구현
    public void quack()
    {
     System.out.println("꽥");
    }}
    ------------------------------------------------------------------------------------------------------------------------
    public class Squeak implements QuackBehavior{//실제로 오리들의 소리 구현
     public void quack()
    {
      System.out.println("삐삑");
     } }
    ------------------------------------------------------------------------------------------------------------------------
    public class MallardDuck extends Duck { //부모클래스를 상속 받은 서브클래스
    public MallardDuck(){
      quackBehavior = new Quack();
      flyBehavior = new FlyWithWings();
     }
     public void display(){
      System.out.println("저는물오리입니다");
     }}
    ------------------------------------------------------------------------------------------------------------------------
    public class ModelDuck extends Duck{//부모클래스를 상속 받은 서브클래스
     public ModelDuck(){
      flyBehavior = new FlyNoWay();//<--날지 못함
      quackBehavior = new Quack();
     }
     public void display() {
      System.out.println("저는 모형오리입니다");
     }}
    ------------------------------------------------------------------------------------------------------------------------
    public class Test {
     public static void main(String[] args) {
      Duck mallard= new MallardDuck();
      mallard.performQuack();//이메소드에서 객체의 QuackBehavior에게 할일을 위임(즉, quackBehavior 레퍼런스의 quack()호출)
      mallard.performFly();

      Duck model = new ModelDuck();
      model.performFly(); //처음 호출시 ModelDuck 생성자에 설정되어 있던 flyBehavior 즉 FlynoWay 인스턴스의 fly()메소드 호출
      model.setFlyBehavior(new FlyRocketPowered()); // 세터 메소드 호출하여 FlyRocketPowered 인스턴스를 가짐
      model.performFly();//FlyRocketPowered 인스턴스의 fly()메소드 호출
     }}
    ------------------------------------------------------------------------------------------------------------------------
    실행결과!!!!
    ------------------------------------------------------------------------------------------------------------------------

    날고있어요
    저는 못날아요
    로켓 추진으로 날아갑니다
    ------------------------------------------------------------------------------------------------------------------------









    Posted by 인천총각

    Observer Pattern
    뭔가 중요한 일이 일어났을 때 객체들한테 새 소식을 알려줄 수 있는 패턴이다. 객체 쪽에서는 계속해서 정보를 받을지 여부를 실행 중에 결정할 수 있다.

     

    1. Observer Pattern의 정의
    Observer Pattern이란 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many)의존성을 정의한다.

     2. Observer Pattern의 특징
    옵저버 패턴에서는 주제와 옵저버에 의해 일대다 관계를 정의된다. 옵저버는 주제객체에 의존하기 때문에 주제객체의 상태가 바뀌면 주제객체에 의존하는 모든 옵저버 객체에게 연락이 간다. 연락 방법에 따라 옵저버에 있는 값이 새로운 값으로 갱신될 수도 있다. Observer Pattern을 구현하는 방법에는 여러 가지가 있지만, 대부분 주제(subject)인터페이스와 옵저버(observer)인터페이스가 들어있는 클래스 디자인을 바탕으로 한다.



    • Observer란 관찰자 이다.
    • Observer Pattern은 상태 값이 변하는 Subject의 값을 체크하기 위해 생겨났다.
    • Publish subscribe패턴이라고 불리우기도 한다. 컨텐츠 발행자(Subject)가 컨텐츠의 변화가 생겼을 때 구독자(Observer)에게 알려주는 방식과 비슷하기 때문이다.
    • Observer는 2개 이상 존재 할 수 있다. 1:N
    • Observer가 취하는 값은 읽기 전용으로 간주한다.

    3. Observer Pattern의 클래스 다이어그램
    ? 주제(subject) : 데이터가 바뀌면 새로운 데이터 값을 옵저버들에게 보내 주는 객체
    ? 옵저버(observer) : 주제의 데이터가 변경되면 갱신내용을 받는 객체

      

     4. Observer Pattern의 구성
    A. 주제를 나타내는 subject 인터페이스 객체에서 옵저버로 등록하거나 옵저버 목록에서 탈퇴하고 싶을 때는 이 인터페이스에 있는 메소드를 사용한다.
    B. 옵저버가 될 가능성이 있는 객체에서는 반드시 observer 인터페이스를 구현해야 한다. 이 인터페이스에는 주제의 상태가 바뀌었을 때 호출되는 메소드가 있다.
    C. 주제 역할을 하는 구상 클래스에서는 항상 subject인터페이스를 구현해야 한다. 주제 클래스에서는 등록 및 해지를 위한 메소드 외에 상태가 바뀔 때마다 모든 옵저버들에게 연락하기 위한 메소드도 구현해야 한다.
    D. Observer 인터페이스만 구현한다면 무엇이든 옵저버 클래스가 될 수 있다. 각 옵저버는 특정 주제 객체에 등록을 해서 연락을 받을 수 있다.

     5. Observer Pattern에서 느슨한 결합(Loose Coupling)의 위력
    옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어 있는 개체 디자인을 제공한다. 두 객체가 느슨하게 결합되어 있다는 것은 그 둘이 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미한다. 주제가 옵저버에 대해서 아는 것은 옵저버가 특정 인터페이스를 구현 한다는 것뿐이며, 옵저버는 언제든지 새로 추가하거나 제거 할 수 있다. 새로운 형식의 옵저버를 추가하려고 할 때도 주제를 전혀 변경할 필요가 없으며, 주제와 옵저버는 서로 독립적으로 재사용할 수 있다. 주제나 옵저버가 바뀌더라도 서로한테 영향을 미치지는 않는다. 이런 관계를 느슨한 결합이라고 하며 느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구출할 수 있다. 객체 사이의 상호의존성을 최소화할 수 있기 때문이다.

    6. 주제가 옵저버한테 상태 정보를 전달하는 두 가지 방식
    주제가 옵저버한테 상태 정보를 전달하는 방식에는 두 가지가 있다. 주제객체에서 데이터를 보내는 방식인 푸시방식과 옵저버가 데이터를 가져오는 방식인 풀 방식이 있다. 두 가지 방식 중에는 풀 방식을 더 옳은 것으로 간주한다.



    옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의합니다.

    'Design Patterns' 카테고리의 다른 글

    스트래티지 패턴 2 (Head First 파헤치기 ver.1)  (0) 2009/11/12
    스트래티지 패턴(Strategy Pattern)  (0) 2009/11/12
    Observer Pattern  (0) 2009/11/11
    Singleton Pattern -  (0) 2009/11/11
    생성 패턴-Factory  (0) 2009/11/11
    패턴의 정의 JSTOM  (0) 2009/11/11
    Posted by 인천총각

    Singleton Pattern : 하나의 클래스 당 오직 하나의 인스턴스 만을 가지고 작업하고 인스턴스를 제어하는 단 하나의 방법만을 제공하고자 할 때 유용하다!!!
    둘 이상의 인스턴스가 생성되는 것을 막기위해 클래스의 생성자를 private으로 정의하고 그 클래스의 static 메소드를 만들어서 오직 그 메소드를 통해서만 생성자에 접근할 수 있도록 하는 것이다. 그럼 이제 클래스가 이미 인스턴스화 되어 있으면 널값을 반환하고 그렇지않으면 Spooler의 인스턴스를 반환하는 메소드를 가진 Spooler클래스를 제작해보자.

    ------------------------------------------------------------------------------------------------------------------------
    public class PrintSpooler{
    //a prototype for a spooler class
    //such that only one 인스턴스 can ever exist
    private static PrintSpooler spooler;
    private PrintSpooler(){ //privatized
    }
    //return only one spooler 인스턴스
    public static synchronized PrintSpooler getSpooler(){
    if(spooler == null) //if none created
    spooler = new PrintSpooler(); //create one
    return spooler;
    }
    public void print(String s){
    System.out.println(s);
    }
    }
    ------------------------------------------------------------------------------------------------------------------------
    private으로 선언되었기 때문에 Spooler클래스의 인스턴스를 직접생성할 수 없다.
    ------------------------------------------------------------------------------------------------------------------------
    예외 던지기-
    1-----------------------------------------------------------------------------------------------------------------------

    public class Spooler{
    // printer스풀 클래스를 위한 원형
    static boolean instance_flag=false;
    public Spooler() throws SingletonException{
    if(instance_flag) throw new SingletonException("Only oneallowed");
    else instance_flag=true;
    System.out.println("printer가 열렸습니다.");
    }
    }

    2-----------------------------------------------------------------------------------------------------------------------

    public class singleSpooler{
    static public void main(String argv[]){
    Spooler pr1,pr2;
    // 한개의 프린터를 열것이다.
    System.out.println("하나의 스풀러를 열고있습니다.");
    Try{
    pr1=new Spooler();
    }catch(SingletonException e)System.out.println(e.getMessage());
    // 다른 하나의 프린터를 열려한다. 실패할 것이다.
    System.out.println("두개의 스풀러를 열려합니다.");
    Try{
    pr2=new Spooler();
    }catch(SingletonException e)System.out.println(e.getMessage());
    }
    }

    결과--------------------------------------------------------------------------------------------------------------------

    하나의 스풀러를 열고 있습니다.
    printer가 열렸습니다.
    두개의 스풀러를 열려 합니다.
    Only one allowed
    마지막 줄에서 두개의 스풀러를 사용하려는 것에 대해 예외처리를 하였다.

    ------------------------------------------------------------------------------------------------------------------------
    Singleton ???
    1. Singleton의 상속은 기초 Singleton클래스가 인스턴트화되지 않기 때문에 어려울 것이다.
    2. 하나이상의 인스턴스를 허용할 수 있도록 Singleton을 변경할 수 있다.

    'Design Patterns' 카테고리의 다른 글

    스트래티지 패턴 2 (Head First 파헤치기 ver.1)  (0) 2009/11/12
    스트래티지 패턴(Strategy Pattern)  (0) 2009/11/12
    Observer Pattern  (0) 2009/11/11
    Singleton Pattern -  (0) 2009/11/11
    생성 패턴-Factory  (0) 2009/11/11
    패턴의 정의 JSTOM  (0) 2009/11/11
    Posted by 인천총각

    Creational pattern(생성 패턴) :

    객체의 인스턴스를 생성하는 방법, 프로그래머는 어떻게 객체를 생성하고 배열할지 신경 쓸 필요가 없다

    ========================================================================================================================


    ?? Factory Method 패턴 : Factory Method 패턴은 입력 데이터에따라 abstract한 부모 클래스의 여러 서브클래스 중 어떤 것을
    반환할지를 결정하는 간단한 클래스를 제공한다. 여러 factory패턴들의 기초로써 Simple Factory 패턴을 우선 살펴볼 것이며,
    그리고 나서 Factory Method 패턴을 소개할 것이다.

    ?? Abstract Factory 패턴 : Abstract Factory 패턴은 관련된 객체들의 여러 묶음(family) 중 하나를 생성하고 반환하는 인터페이스
    를 제공한다.

    ?? Builder 패턴 : Builder 패턴은 복잡한 객체의 생성을 표현(representation)과 분리함으로써, 프로그램의 필요에 따라 여러
    다른 표현(representation)이 생성될 수 있도록 해준다.

    ?? Prototype 패턴 : Prototype 패턴은 새로운 인스턴스를 복사하거나 복제하는 인스턴스화된 클래스를 제공한다. 이러한 인스턴
    스는 public 메소드를 사용하여 가공될(tailor) 수 있다.

    ?? Singleton 패턴 : Singleton 패턴은 하나 이상의 인스턴스가 생성되지 않는 클래스를 말한다. Singleton 패턴은 이러한 인스턴스
    에 접근하기 위한 하나의 포괄적인(global) 접근 포인트를 제공한다.

    ------------------------------------------------------------------------------------------------------------------------

    Simple Factory- ex) Factory클래스에서 조건여부 판단하여 두 클래스중 하나의 클래스를 선택해 인스턴스를 반환하는 방법!!
    여러 개의 클래스 중 반환할 클 래스를 결정하고 그 클래스를 반환하는 상위 추상(abstraction)클래스를 생성할 수 있다.
    그러면 당신은 실제로 당신이 어떤 서브클래스를 사용할지 알 필요 없이 그 클래스 인스턴스의 메소드를 호출할 수 있다.


    public class NameFactory {
    Namer namer;
    // Factory는 쉼표의 유무에 기초해 어떤 클래스가
    // 반환될 것인지 결정한다.
    public Namer getNamer(String entry) {
    //comma determines name order
    int i = entry.indexOf(",");
    if (i > 0)
    return new LastFirst(entry);
    else
    return new FirstFirst(entry);
    }
    }
    ------------------------------------------------------------------------------------------------------------------------

    factory 클래스는 교통경찰 같은 역할을 하며, 한 계층의 서브클래스 중 어떤 것이 인스턴스화 될것인지를 결정할 것이다.

    ------------------------------------------------------------------------------------------------------------------------

    Factory Method 패턴은 여러 면에서 쓸모가 많지만, 어떤 서브클래스를 인스턴스화할 것인지를 결정하는 클래스가 없다는 점에서 약간은 난해하다. 슈퍼클래스(superclass)는 이러한 문제에 대한 결정을 서브클래스에게 맡긴다.
    패턴에서는 여러 서브클래스들 중 하나의 서브클래스를 선택해 인스턴스화하는 루틴이 존재하지 않는다. 대신에 Factory Method 패턴을 사용한 프로그램은 abstract 클래스를 이용하여 이 문제를 해결한다.
    이 abstract 클래스는 객체를 생성하는 틀만을 제공하며 어떤 객체를 생성할 것인 가는 각각의 서브클래스가 결정한다.

    ------------------------------------------------------------------------------------------------------------------------

    다음의 상황에서 Factory 메소드 사용을 고려해야 한다

    ?? 클래스가 생성해야 하는 객체의 클래스의 종류를 예상할 수 없을 경우

    ?? 클래스가 자신이 생성하는 객체를 명확히 하기 위해서 서브클래스를 사용할  경우 어떤 클래스가 생성되었는가에 대한 정보를 국한시키기 위해서 부모(base) 클래스는 abstract이고 패턴은 완전히 작동하는(working) 클래스를반환한다.

    ?? 부모 클래스는 디폴트 메소드를 포함하고, 디폴트 메소드가 불충분할 때는 서브클래스에서 메소드를 추가한다.

    ?? 여러 클래스 타입 중 어떤 클래스가 반환될 것인지 알려주는 파라미터 들이 factory에 넘겨진다.

    이런 경우 클래스들은 같은 메소드 이름을 공유하지만 각각 매우 다른 무언가를 수행할 것이다.

    'Design Patterns' 카테고리의 다른 글

    스트래티지 패턴 2 (Head First 파헤치기 ver.1)  (0) 2009/11/12
    스트래티지 패턴(Strategy Pattern)  (0) 2009/11/12
    Observer Pattern  (0) 2009/11/11
    Singleton Pattern -  (0) 2009/11/11
    생성 패턴-Factory  (0) 2009/11/11
    패턴의 정의 JSTOM  (0) 2009/11/11
    Posted by 인천총각


    ==디자인 패턴이란?==============================================================================

    컴퓨터 과학 연구자들이 디자인 패턴이라는 것을 연구하기 시작한 주요 이유는 간단하 고 또한 재활용성이 있는 프로그래밍 솔루션을 만들기 위해서 이다.
    디자인 패턴은 프로젝트사이 또는 프로그래머 사이에서 객체 지향 코드를 재사용하게 하는 편리한 수단일 뿐이다.
    디자인 패턴은 프로그래머들이 유용하다고 생각하는객체들간의 일반적인 상호작용(interaction) 방법들을 모은 목록(Catalog)이라고 할 수 있다.

    다른 말로 디자인 패턴은 어떻게 하면 객체들이 다른 객체의 메소드, 데이터들과 뒤엉힘이 없이 통신이 이루어질 수 있는 가를 기술한 것이다.

    위와 같이 객체를 분리함으로써 객체지향 프로그래밍의 목적을 항상 유지할 수 있다.


    ?? 디자인 패턴은 자주 발생되는 설계상의 문제를 해결하기 위한 반복적인 솔루션이다. [Smalltalk Companion]

    ?? 디자인 패턴은 소프트웨어 개발 영역상에서 어떠한 작업을 수행하기 위한 코딩 원칙의 집합으로 구성되어 있다. [Pree, 1994]

    ?? 디자인 패턴은 반복되는 구조를 설계할 때 설계를 재활용하는데 초점을 두고있다. 그에 비해 프레임워크는 세부 설계와 구현에 초점을 두고 있다. [Coplien& Schmidr, 1995]

    ?? 패턴은 특정 디자인 상황에서 발생되는 반복되는 설계상의 문제를 다룬다. 또한 그것의 해결 방법을 제시한다.

    ?? 패턴은 단일의 클래스와 인스턴스 또는 컴포넌트 들의 레벨 상위의 추상성을정의한다.[Gamma et al, 1993]


    디자인 패턴은 객체를 설계하기 위한 것만은 아니다. 디자인 패턴은 객체간의 상호작용을 표현하기 위해서도 쓰인다. 이러한 관점을 가지고 쓰여진 패턴을 Communication 패턴이라고 한다.

    ?? Creational pattern(생성 패턴) : 프로그래머가 직접 객체를 생성하는 것보다 패턴을 이용하여 객체를 생성한다. 이 패턴을 적용하면 주어진 상황에 어떠한 적절한 객체가 생성되야 적합한지를 프로그램상으로 유용하게 표현할 수 있다.

    ?? Structural pattern(구조 패턴) : 객체의 그룹들을 좀더 큰 구조로 조합할 때 어떻게 하는 것이 좋은 가에 대하여 기술한 패턴들이다. 예를 들면 사용자 인터페이스나 계산 데이터에 쓰인다.

    ?? Behavioral pattern(기능 패턴) : 시스템상의 객체들의 통신과 통신의 흐름이 복잡한 프로그램상에서 어떻게 컨트롤되는지 정의한다.

    ========================================================================================================================

     

    'Design Patterns' 카테고리의 다른 글

    스트래티지 패턴 2 (Head First 파헤치기 ver.1)  (0) 2009/11/12
    스트래티지 패턴(Strategy Pattern)  (0) 2009/11/12
    Observer Pattern  (0) 2009/11/11
    Singleton Pattern -  (0) 2009/11/11
    생성 패턴-Factory  (0) 2009/11/11
    패턴의 정의 JSTOM  (0) 2009/11/11
    Posted by 인천총각