'JAVA/Design patten'에 해당되는 글 4건

  1. 2019.04.27 factory method pattern
  2. 2019.04.27 singleton-pattern
  3. 2015.09.03 Decorator Pattern (데코레이터 패턴)
  4. 2015.08.21 옵저버 패턴의 정의

factory method pattern 

주의! 팩토리 메소드와 추상팩토리 패턴은 다르다.

 

팩토리 메소드 패턴

객체를 만들어내는 부분을 서브클래스에 위임하는 하는 패턴이다.

키워드를 호출하는 부분을 서브클래스에 위임하는것으로 팩토리 메소드 패턴은 객체를 만들어내는 공장(팩토리)라고 이해하면 된다.

 

로봇으로 예제를 들어보자.

여러 종류의 로봇을 생산하는 공장도 있어야한다. 

로봇과 로봇공장을 만들어서 예제를 돌려봅니다.

 

<Robot>

package pattern.factory;

public abstract class Robot {
	public abstract String getName();
}
public class SuperRobot extends Robot {
	@Override
	public String getName() {
		return "SuperRobot";
	}
}
public class PowerRobot extends Robot {
	@Override
	public String getName() {
		return "PowerRobot";
	}
}

 

 

<Robot Factory>

로봇의 팩토리 클래스이다.

public abstract class RobotFactory {
	abstract Robot createRobot(String name);
}

아래 클래스는 기본 팩토리클래스를 상속받아 실제 로직을 구현한 팩토리이다.

public class SuperRobotFactory extends RobotFactory {
	@Override
	Robot createRobot(String name) {
		switch( name ){
			case "super": return new SuperRobot();
			case "power": return new PowerRobot();
		}
		return null;
	}
}

아래 클래스도 기본팩토리를 상속받아 실제 로직을 구현한 팩토리지만 위와 다른점은 클래스이름을 String 으로 받아서 직접 인스턴스 생성한다.

package pattern.factory;

public class ModifiedSuperRobotFactory extends RobotFactory {
	@Override
	Robot createRobot(String name) {
		try {
			Class<?> cls = Class.forName(name);
			Object obj = cls.newInstance();
			return (Robot)obj;
		} catch (Exception e) {
			return null;
		}
	}
}

 

<Test Code>

public class FactoryMain {
	public static void main(String[] args) {

		RobotFactory rf = new SuperRobotFactory();
		Robot r = rf.createRobot("super");
		Robot r2 = rf.createRobot("power");

		System.out.println(r.getName());
		System.out.println(r2.getName());

		RobotFactory mrf = new ModifiedSuperRobotFactory();
		Robot r3 =  mrf.createRobot("pattern.factory.SuperRobot");
		Robot r4 =  mrf.createRobot("pattern.factory.PowerRobot");

		System.out.println(r3.getName());
		System.out.println(r4.getName());
	}
}

결과

main() 에서 new() 키워드가 없다는것을 확인할 수 있음.

객체 생성을 팩토리에 위임한 결과이다. 

main() 에서는 팩토리를 통해 원하는 로봇 객체를 얻는다.

팩토리 메소드 패턴을 사용하는 경우 직접 객체를 생성해 사용하는것을 방지하고 서브 클래스에 위임함으로써 효율적인 코드 제어를 할 수 

있고 의존성을 제거한다.

 

팩토리 메소드 패턴을 사용하는 이유는 클래스간의 결합도를 낮추기 위한것이다. 

즉, 로봇 클래스에 변경이 일어나도 다른 클래스에도 영향을 주지않는다.

팩토리 메소드 패턴을 사용하는 경우 직접 객체를 생성해 사용하는것을 방지하고 서브 클래스에 위임함으로써 보다 효율적인 코드 제어

할 수 있고 의존성을 제거한다.

결과적으로 결합도를 낮출수 있다.

 

 

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

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

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 양승아
:


옵저버패턴

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


옵저버 패턴 조건

객체간의 결합도는 높아질수록 유지보수가 힘들기 때문에 일단 객체간의 결합도는 낮추는게 일반적이다.

옵저버를 언제든지 추가하려고 할때도 주제를 전혀 변경할 필요가 없다.


예제

가상 모니터링 애플리케이션을 구현해보자.

이 시스템은 다음과 같이 3개의 요소로 어루어져 있고(가상 스테이션, 날씨 데이터, 디스플레이 장비) Weather Data를 통해서 3개 혹은 그 이상 디스플레이 장비에 데이터를 보여주게 된다.





분석

1. 옵저버 패턴의 정의를 떠올리면 one-to-many 관계이므로 one = Weather Data , many = Display 장치가 된다

2. Weather Data 객체를 주제객체로 하고 Display 장치에서 옵저버로 하는 경우 Display 애서 자기가 원하는 정보를

    얻기위해  Weather Data 객체에 등록할수 있는 registerObserver()메소드가 필요하다.

3. 모든 Display 장치는 항목이 다를 수 있다. 그러므로 바로 이 부분이 공통 인터페이스를 이용해서 처리가 가능함

   각 장치마다 구성 요소의 형식이 달라도 똑같은 인터페이스를 구현해야만 Weather Data 객체에서 기상 스테이션으로 측정값을 보낼      수 있다.

4. 즉 모든 디스플레이 장치는 Weather Data 에서 호출할 수 있는 update()메소드가 필요하다. 그리고 이 update() 메소드는 모든 장치들    이 구현하는 공통 인터페이스에 정의 되야 한다.



옵저버 패턴 적용

UML 확인하고 코딩하기!




Observer .java


Subject .java

DisplayElement .java

WeatherData.java

CurrentConditionsDisplay.java

Main .java


display는 하나만 구현함!

동작 시나리오

① WeatherStation에서는 온도, 습도, 기압 정보를 수집한다.

② WeatherStation에서 주기적으로 (30분에 한번식) 현재 데이터(온도,습도,기압)을 WeatherData에 던져 준다.

③ WeatherData는 새로운 데이터를 받으면, Observable에 Observer로 등록되어 있는, 현재상태 출력장비(CurrentConditionDisplay)와 기압변동 출력장비(ForecastDisplay)에 새로운 데이터를 전달한다.

④ 현재상태 출력장비와 기압변동 출력장비는 새로운 데이터(온도, 습도, 기압)을 각자의 활용 방법에 따라 화면에 출력한다.

⑤ WeatehrStation에서 다시 새로운 데이터를 던져준다. 

⑥ ①~④ 번이 반복되며, 주기적으로 현재상태 출력장비와 기압변동 출력장비는 새로운 데이터를 출력한다.


java에서 제공하는 옵저버 패턴 적용




WeatherData.java


CurrentConditionsDisplay .java

Main.java

 java.util.Observable.class  확인


java.util.Observable.class


소스코드 보기


- addObserver(Observer o) : 옵저버를 등록한다. 이후에 들어오는 데이터는 등록된 옵저버에 전달된다.

- deleteObserver(Observer o) : 옵저버를 제거한다. 이후에 들어오는 데이터는 해당 옵저버에 전달되지 않는다.

- notifyObservers(), notifyObservers(Object arg) : 새로운 데이터가 들어오면 등록된 옵저버에 새로운 데이터와, 파라미터를 전달한다.

- deleteObservers() : 등록된 모든 옵저버를 제거한다. 이후에 들어오는 데이터는 전달되지 않는다.

- setChanged() : 신규 데이터가 들어오면, changed 값을 true로 변경, changed 변수가 true 일때만 데이터가 옵저버에 전달된다.

- clearChanged() : changed 값을 false 로 변경한다. 신규 데이터를 옵저버에 전달이 완료되면 clearChanged()를 호출한다.

- hasChanged() : 현재 changed 의 값을 반환한다.

- countObservers() : 현재 등록되어있는 옵저버의 수를 반환한다.



자바의 내장된 옵저버 패턴 이용 (Observable)


- setMeasurements(float temperature, float humidity, float pressure) : 기상스테이션에서 온도, 습도, 기압 측정값을 전달할때 호출한다. 파라미터로 받은 데이터를 갱신하고 measuermentChanged()를 호출한다.

- measurementsChanged() : 옵저버에 전달할 새로운 데이터가 있다고 알리고(setChanged()), 등록된 옵저버에 데이터를 전달한다.(notifyObservers())  (setChanged()와 notifyObservers()는 Observable에서 상속받은 함수이다.)


보는것과 같이 WeatherData.java는 Observable 을 상속하였다. 따라서 Observable의 함수를 사용하여, Observer들에게 데이터를 전달할 수 있다.


15열의 setMeasurements() 함수와 24열의 measurementsChanged() 함수를 보자

기상스테이션(WeatherStation.java)는 주기적으로 데이터(온도,습도,기압)값을 WeatherData의 setMeasurements() 함수를 사용하여 전달한다.

setMeasurements()는 전달받은 데이터를 각 변수에 갱신한 뒤, measurementsChanged() 함수를 호출한다.


measuremensChanged() 함수는 Observer에게 데이터를 전달한다.

먼저 setChanaged() 함수를 호출하여, 전달할 새로운 데이터가 있음을 설정한다. 그리고 notifyObservers() 함수를 호출하여, 갱신한 새로운 데이터를 각 등록된 옵저버에게 전달한다.


정말 간단하다. Observable 을 상속한뒤, 옵저버들에게 데이터를 전달할 때, setChanged()와 notifyObservers()를 순차적으로 호출하면 된다. 그러면 데이터가 등록된 옵저버들에게 전달되는 것이다.







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

factory method pattern  (0) 2019.04.27
singleton-pattern  (0) 2019.04.27
Decorator Pattern (데코레이터 패턴)  (0) 2015.09.03
Posted by 양승아
: