'디자인패턴'에 해당되는 글 2건

  1. 2019.04.27 singleton-pattern
  2. 2015.09.03 Decorator Pattern (데코레이터 패턴)

singleton-pattern

JAVA/Design patten 2019. 4. 27. 17:47 |

Singleton-pattern

단 하나의 인스턴스를 생성해 사용하는 패턴

 

why?

고정된 메모리 영역을 얻으면서 한번의 new로 인스턴스를 사용하기 때문에 메모리 낭비를 방지할 수 있음

또한,싱글톤으로 만들어진 클래스의 인스턴스는 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다

커넥션 풀, 스레드 풀 등 객체생성시 싱글톤 사용

 

1. eager initialization  (이른 초기화)

싱글톤 패턴중에 가장 기본적인 싱글톤 패턴이다

이른 초기화 방식은 싱글톤 객체를 미리 생성해 놓은 방식이다.

전역변수로 instance 변수 생성하고 private static 선언하여 직접 바로 접근할 수 없도록 합니다.

생성자에도 private 접근 제어 키워드를 붙여 다른 클래스에서 new EagerInitialization() 방식으로 새로운 인스턴스 생성하는 것을 방지한다.

오로지 정적메소드인 getInstance() 메서드를 통해 인스턴스를 접근하도록 하여 동일한 인스턴스를 사용하는 기본 싱글톤원칙을 지키게한다.

/**
 *
 * 가장 기본적인 singleton pattern
 * new EagerInitialization () 될때 어떠한 예외처리도 할 수 없다는것이 단점
 */
public class EagerInitialzation {

	//private 으로 선언하여 외부에서 접근 불가
	//static 으로 인스턴스화 없이 사용 가능함
	private static EagerInitialzation instance = new EagerInitialzation();

	// private 으로 생성자 선언하여 new 키워드로 인스턴스 생성 불가능
	private EagerInitialzation(){
		System.out.println("call EagerInitialzation constructor");
	}

	//instance의 직접 접근도 불가능하고 생성자도 불가능하기 때문에 getInstance() 로 만 인스턴스 가질수있음
	public static EagerInitialzation getInstance(){
		return instance;
	}
}

 

2. Static block initialization

이른초기화와 같은 방식으로 초기에 인스턴스 만들어놓지만 static 구문에 로직을 추가하여 수행하는 패턴이다

logic을 담을수 있기 때문에 복잡한 초기변수 셋팅이나 예외처리를 위한 구문을 담을수 있다.

 

/**
 *
 * Static block initialization
 * logic을 담을수 있기 때문에 복잡한 초기변수 셋팅이나 위와 같이 에러처리를 위한 구문을 담을수 있다.
 */
public class StaticBlockInitialization {

	private static StaticBlockInitialization instance;

	private StaticBlockInitialization (){ }
	// static 으로 정의하여 초기화 블럭을 이용하여 클래스가 로딩될때 최초 한번 실행하게 됨.
	static {
		try {
			System.out.println("instance create...");
			instance = new StaticBlockInitialization();
		}catch (Exception e){
			throw new RuntimeException("Exception creating StaticBlockinitalation instace.....");
		}
	}

	public static StaticBlockInitialization getInstance(){
		return instance;
	}
}

 

 

3. Lazy initialization (게으른 초기화)

게으른 초기화는 인스턴스를 사용하는 시점에 사용한다. 

instance 변수를 private static 으로 전역변수로 만들고 생성자도 private 로 선언하여 외부에서 생성 못하게 한다.

오직 getIntance() 메소드를 통해 인스턴스 생성 및 호출되는데, 최초 호출일 경우 인스턴스 객체 생성한다.

만약 멀티 쓰레드를 방식에서 getInstance() 를 두번 호출하게 되면 인스턴스가 두번 생성될 수 있는 문제점이 있다.

 

/**
 * Eager initialization,static block 방법은 클래스가 로딩될때 인스턴스 생성하기 때문에 프로그램이 커지만 부담이 될수 있다.
 * Lazy 초기화는 인스턴스를 사용하는 시점에 사용한다.
 * 만약 멀티 쓰레드 방식이라서 getinstance()를 두번 호출하게 되면 두번 생성 될수 있다.
 */
public class LazyInitialization {

	private static LazyInitialization instance;
	private LazyInitialization(){};

	public static LazyInitialization getInstance(){
		if (instance == null)
			instance = new LazyInitialization();
		return instance;
	}
}

 

4.Thread Safe Lazy initialization(스레드 안전한 늦은 초기화)

Lazy 싱글톤 방식에서 thread safe 문제가 있어 이를 보완하기 위해 멀티쓰레드에서 스레드들이 동시 접근하는

동시성을 synchronized 키워드를 이용해 해결하는 방식이다.

 

/**
 * sychronized 블록으로 인해, 수 많은 쓰레드 들이 인스턴스 호출하게 되면 높은 cost 비용으로 인해
 *  프로그램 전반에 성능저하가 일어난다.
 *
 */
public class ThreadSafeInitialization {
	private static ThreadSafeInitialization instance;
	private ThreadSafeInitialization(){}

	//여러 thread 들이 동시에 접근해서 인스턴스를 생성시키는 위험은 없어짐!
	public static synchronized ThreadSafeInitialization getInstance(){
		if (instance==null)
			instance = new ThreadSafeInitialization();
		return instance;
	}
}

synchronized 키워드를 사용할 경우 자바 내부적으로 해당 영역이나 메서드를 lock,unlock 처리하기때문에 많은 cost가 발생함

=> 성능저하됨!

 

4-1. Thread Safe Initialization + double - checked locking 기법 싱글톤 패턴

위에 Thread safe initialization 에서 synchronized 처리된 메서드가 성능저하가 발생되어 완화하기 위해

Double checked locking 기법을 사용한다.

instance가 null 일 경우에 synchronized 블럭에 접근하고 한번더 instance의 null 유무를 체크한다.

if문 두번다 null 일 경우 new를 통해 인스턴스화시킨다.

그후 instance 가 null이 아니기때문에 synchronized 블록을 타지 않는다.

Double checked locking 으로 성능저하 줄임

 

public class ThreadSafeLazyInitialization {
 
    private static ThreadSafeLazyInitialization instance;
 
    private ThreadSafeLazyInitialization(){}
     
    public static ThreadSafeLazyInitialization getInstance(){
        //Double-checked locking
        if(instance == null){
            synchronized (ThreadSafeLazyInitialization.class) {
                if(instance == null)
                    instance = new ThreadSafeLazyInitialization();
            }
 
        }
        return instance;
    }
}

 

'JAVA > Design patten' 카테고리의 다른 글

factory method pattern  (0) 2019.04.27
Decorator Pattern (데코레이터 패턴)  (0) 2015.09.03
옵저버 패턴의 정의  (0) 2015.08.21
Posted by 양승아
:

Decorator Pattern (데코레이터 패턴)


데코레이터 패턴

- 데코레이터는 서브 클래스를 만드는 것을 통해서 기능을 유현하게 확장할 수 있는 방법을 제공한다.

- 한 객체를 여러 개의 테코레이터로 감쌀수 있다.

- 기존 코들르 수정하지 않고도 기능을 확장할 수 있다.  


단점 

- 데코레이터 패턴을 이용해 디자인을 하다 보면 답다한 클래스가 많아질 수 있음

- 겹겹이 애워싼 객체의 정체를 알기가 힘들다

- 상속을 통해 확장할 수도 있지만, 디자인 유연성 면에서는 별로 좋지 않다.


그럼 이제부터 데이레이터 패턴 사용 하지 않은 예를 들면 !!

카페에서 음료를 주문할때 음료하나에 시럽,우유,휘핑 등을 추가하는 경우가 있다.

이 행동을 구현하면 



- 음료를 나타내는 추상 메소드 Beverage 는 카페에서 판매되는 모든 음료는 이 클래스의 서브 클래스가 된다. 즉 Beverage 가 부모클래스 , 슈퍼클래스 이다. 

- beverage 라는 음료 클래스에 우유,두유,모카,휘핑 크림을 나타내는 인스턴스 변수를 추가한다.

- 첨가물에 첨부 여부와 첨가물을 설정한다

- cost() 에서 추가된 첨가물의 가격을 계산한다. 서브클래스에서 cost()메소드를 재구현한다.


자 근데 이런식으로 하면 문제점이 무엇인가 ?

첨가물 가격이 바뀔때 마다 기존 코드를 수정해야된다.

첨가물의 종류가 많아지면 새로운 메소드를 추가해야한다.

새로운 음료가 출시될 수도 있다. 그 중에는 첨가물이 들어가면 안되는 음료도 있다. !! (필요없는 첨가물 메소드 상속 받게됨)

손님이 모카를 두번 추가하면 어떻게 할것인가 ????


부모클래스와 자식클래스

- 자식클래스를 만드는 방식으로 행동을 상속 받으면 그 행동은 컴파일시! 완전히 결정되고 모든 서브 클래스에서 똑같은 행동을 상속 받아야한다.

- 하지만 구성을 통해 객체의 행동을 확장하면 실행 중에 동적으로 행동을 설정할 수 있다.



Decorator pattern 적용







- Beverage 는 가장 기본이 되는  Component 클래스이다. 

- 음료 종류마다 Beverage에 대한 구상 클래스를 하나씩 만든다. (DripCoffee,DripCoffee)

- 각각의 첨가물을 나타내는 데코레이터를 추가(Mocha, Milk )하고  cost(), getDescription() 를 구현해야한다.

- 각 데코레이터 안에는 Beverage 클래스가 들어있다.

- 데코레이터에는 구성요소에 대한 레퍼런스가 들어있는 인스턴스 변수가 있다!!! (private Beverage beverage)        




Beverage.java


DripCoffee.java

Espresso.java

CondimentDecorator.java

Milk.java

Mocha.java

StarbuckCafe.java


결과 


에스프레소, 우유$2.29

제목없음$3.21




'JAVA > Design patten' 카테고리의 다른 글

factory method pattern  (0) 2019.04.27
singleton-pattern  (0) 2019.04.27
옵저버 패턴의 정의  (0) 2015.08.21
Posted by 양승아
: