Prototype Pattern

정의
클래스로부터 인스턴스를 만드는 것이 아니고, 인스턴스에서 새로운 인스턴스를 만든다.(복사해서 사용)

Prototype
인스턴스를 복사하여 새로운 인스턴스를 만들기 위한 메소드를 결정하는 클래스

ConcretePrototype
인스턴스를 복사해서 새로운 인스턴스를 만드는 메소드를 실제로 구현한 클래스

Client
인스턴스를 복사하는 메소드를 이용해서 새로운 인스턴스를 만든다.

언제 Prototype 패턴을 사용하나?
1. 종류가 너무 많아서 클래스로 정리할 수 없는 경우
2. 클래스로부터 인스턴스 생성이 어려운 경우
3. framework와 생성하는 인스턴스를 분리하고 싶은 경우





 
public interface Product extends Cloneable {
	public abstract void use(String s);
	public abstract Product createClone();
}

public class MessageBox implements Product {
	private char decochar;
	
	public MessageBox(char decochar) {
		this.decochar = decochar;
	}	
	
	public void use(String s) {
		int length = s.getBytes().length;
		for (int i = 0; i < length + 4; i++) {
			System.out.print(decochar);
		}
		System.out.println("");
		System.out.println(decochar + " " + s + " " + decochar);
		for (int i = 0; i < length + 4; i++) {
			System.out.print(decochar);
		}
		System.out.println(" ");
	}

	public Product createClone() {
		Product p = null;
		try {
			p = (Product)clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return p;
	}
}

public class Manager {
	private HashMap showcase = new HashMap();
	
	public void register(String name, Product proto) {
		showcase.put(name, proto);
	}
	
	public Product create(String protoname) {
		Product p = (Product)showcase.get(protoname);
		return p.createClone();
	}
}

public class Main {
	public static void main(String[] args) {
		Manager manager = new Manager();
		MessageBox mbox = new MessageBox('*');
		MessageBox sbox = new MessageBox('/');
		manager.register("strong message", mbox);
		manager.register("slash box", sbox);
		
		Product p1 = manager.create("strong message");
		p1.use("Hello world");
		Product p2 = manager.create("slash box");
		p2.use("Hello world");
	}
}




결과 화면
***************
* Hello world *
***************
///////////////
/ Hello world /
///////////////

예제소스 : Java 언어로 배우는 디자인 패턴 입문
Posted by outliers
,
State Pattern

정의
객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다. 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.


상태를 별도의 클래스로 캡슐화한 다음 현재 상태를 나타내는 객체에게 행동을 위임하기 때문에, 내부 상태가 바뀜에 따라서 행동이 달라지게 된다는 것을 알 수 있습니다.


객체지향 디자인 원칙
- 바뀌는 부분은 캡슐화 한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 클래스는 확장에 대해서는 열려있지만 변경에 대해서는 닫혀있어야 한다.(OCP)
- 추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.
- 친한 친구들하고만 이야기 한다.
- 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
- 클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.


핵심정리
- 스테이트 패턴을 이용하면 내부 상태를 바탕으로 여러 가지 서로 다른 행동을 사용할 수 있습니다.
- 스테이트 패턴을 사용하면 프로시저형 상태 기계를 쓸 때와는 달리 각 상태를 클래스를 이용하여 표현하게 됩니다.
- Context 객체에서는 현재 상태에게 행동을 위임합니다.
- 각 상태를 클래스로 캡슐화함으로써 나중에 변경시켜야 하는 내용을 국지화시킬 수 있습니다.
- 스테이트 패턴과 스트래티지 패턴의 클래스 다이어그램은 똑같지만 그 용도는 서로 다릅니다.
- 스트래티지 패턴에서는 일반적으로 행동 또는 알고리즘을 Context 클래스를 만들 때 설정합니다.
- 스테이트 패턴을 이용하면 Context의 내부 상태가 바뀜에 따라 알아서 행동을 바꿀수 있도록 할 수 있습니다.
- 상태 전환은 State 클래스에 의해서 제어할 수도 있고, Context 클래스에 의해서 제어할 수도 있습니다.
- 스테이트 패턴을 이용하면 보통 디자인에 필요한 클래스의 개수가 늘어납니다.
- State 클래스를 여러 Context 객체의 인스턴스에서 공유하도록 디자인할 수도 있습니다.
Posted by outliers
,
Iterator Pattern

정의
컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해 주는 방법을 제공해 줍니다.

장점
집합체 내에서 어떤 식으로 일이 처리되는지에 대해서 전혀 모르는 상태에서 그 안에 들어있는 모든 항목들에 대해서 반복작업을 수행할 수 있습니다.
컬렉션 객체 안에 들어있는 모든 항목에 접근하는 방식이 통일되어 있으면 어떤 종류의 집합체에 대해서도 사용할 수 있는 다형적인 코드를 만들 수 있습니다.
이터레이터 패턴을 사용하면 모든 항목에 일일이 접근하는 작업을 컬렉션 객체가 아닌 반복자 객에에서 맡게 된다는 점입니다. 이렇게 하면 집합체의 인터페이스 및 구현이 간단해 질 뿐 아니라 집합체에서는 반복작업에서 손을 떼고 원래 자신이 할 일(객체 컬렉션 관리)에만 전념할 수 있습니다.

객체지향 디자인 원칙
- 바뀌는 부분은 캡슐화 한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 클래스는 확장에 대해서는 열려있지만 변경에 대해서는 닫혀있어야 한다.(OCP)
- 추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.
- 친한 친구들하고만 이야기 한다.
- 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
- 클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.


Composite Pattern

정의
객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들 수 있습니다. 이 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합 객체(composite)를 똑같은 방법으로 다룰 수 있습니다.

특징
컴포지트 패턴에서는 단일 역할 원칙을 깨면서 대신에 투명성을 확보하기 위한 패턴이라고 할 수 있습니다.
어떤 원소가 복합 객체인지 잎 노드인지가 클라이언트 입장에서는 투명하게 느껴지는 거죠. 원칙이 우리가 생각하고 있는 디자인에 어떤 영향을 끼칠지를 생각해 봐야 합니다.
가장 큰 특징은 클라이언트를 단순화 시킬 수 있다는 점입니다. 올바른 객체에 올바른 메소드를 호출하고 있는지 확인하기 위해 if문을 쓰지 않아도 됩니다. 그리고 메소드 하나만 호출하면 전체 구조에 대해서 반복해서 작업을 처리할 수 있는 경우도 자주 있습니다.
객체들을 모아서 관리할 때 매우 유용할 것입니다.

핵심 정리
- 반복자를 이용하면 내부 구조를 드러내지 않으면서도 클라이언트로부터 컬렉션 안에 들어있는 모든 원소들에 접근하도록 할 수 있습니다.
- 이터레이터 패턴을 이용하면 집합체에 대한 반복작업을 별도의 객체로 캡슐화할 수 있습니다.
- 이터레이터 패턴을 이용하면 컬렉션에 있는 모든 데이터에 대해서 반복작업을 하는 역할을 컬렉션에서 분리시킬 수 있습니다.
- 이터레이터 패턴을 쓰면 다양한 집합체에 들어있는 객체에 대한 반복작업들에 대해 똑같은 인터페이스를 적용할 수 있기 때문에, 집합체에 있는 객체를 활용하는 코드를 만들 때 다형성을 활용할 수 있습니다.
- 한 클래스에는 될 수 있으면 한 가지 역할만 부여하는것이 좋습니다.
- 컴포지트 패턴에서는 개별 객체와 복합 객체를 모두 담아둘 수 있는 구조를 제공합니다.
- 컴포지트 패턴을 이용하면 클라이언트에서 개별 객체와 복합 객체를 똑같은 방법으로 다룰 수 있습니다.
- 복합 구조에 들어있는 것을 구성요소라고 부릅니다. 구성요소에는 복합 객체와 잎 노드가 있습니다.
- 컴포지트 패턴을 적용할 때는 여러가지 장단점을 고려해야 합니다. 상황에 따라 투명성과 안정성 사이에서 적절한 평형점을 찾아야 합니다.
Posted by outliers
,
Template Method Pattern

정의
메소드에서 알고리즘의 골격을 정의합니다. 알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있습니다. 템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의할 수 있습니다.

간단하게 말하자면 알고리즘의 틀을 만들기 위한 것입니다. 일련의 단계들로 알고리즘을 정의한 메소드 입니다. 여러 단계 가운데 하나 이상이 추상 메소드로 정의되며, 그 추상 메소드는 서브클래스에서 구현됩니다.

 
abstract class AbstractClass {
	final void templateMethod() {
		primitiveOperation1();
		primitiveOperation2();
		if (hook()) {
			concreteOperation();
		}
	}
	
	// 알고리즘의 일부를 구상 서브 클래스에서 구현한다.
	abstract void primitiveOperation1();
	abstract void primitiveOperation2();
	
	/**
	 *  기본적으로 아무것도 하지 않는 구상 메소드를 정의할 수도 있습니다.
	 *  이런 메소드는 "후크(hook)"라고 부릅니다. 
	 *  서브클래스에서 오버라이드 할 수도 있지만 반드시 그래야 하는건 아니죠.
	 *  서브 클래스에서 오버라이드를 해서 알고리즘의 일부를 변경 할 수 있습니다.
	 */
	boolean hook() {
		return true;
	}

	void concreteOperation() {
		// TO DO
	}	
}


헐리우드 원칙
먼저 연락하지 마세요. 저희가 연락 드리겠습니다.

헐리우드 원칙을 적용을 활용하면 "의존성 부패(dependency rot)"를 방지할 수 있습니다.
저수준 구성요소에서 시스템에 접속을 할수는 있지만, 언제 어떤 식으로 그 구성요소들을 사용할지는 고수준 구성요소에서 결정하게 됩니다. 즉, 고수준 구성요소에서는 저수준 구성요소에게 "먼저 연락하지 마세요. 제가 먼저 연락 드리겠습니다."라고 얘기를 하는 것과 같죠

Arrays.sort() 도 변형된 템플릿 메소드 패턴 입니다.

객체지향 원칙
- 바뀌는 부분은 캡슐화 한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 클래스는 확장에 대해서는 열려있지만 변경에 대해서는 닫혀있어야 한다.(OCP)
- 추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.
- 친한 친구들하고만 이야기 한다.
- 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.

핵심 정리
- "템플릿 메소드"에서는 알고리즘의 단계들을 정의하는데, 일부 단계는 서브클래스에서 구현하도록 할 수 있습니다.
- 템플릿 메소드 패턴은 코드 재사용에 크게 도움이 됩니다.
- 템플릿 메소드가 들어있는 추상클래스에서는 구상 메소드, 추상 메소드, 후크를 정의할 수 있습니다.
- 추상 메소드는 서브클래스에서 구현합니다.
- 후크(hook)는 추상 클래스에 들어있는, 아무 일도 하지 않거나 기본 행동을 정의하는 메소드로, 서브클래스에서 오버라이드 할 수 있습니다.
- 서브클래스에서 템플릿 메소드에 들어있는 알고리즘을 함부로 바꾸지 못하게 하고 싶다면 템플릿 메소드를 final로 선언하면 됩니다.
- 헐리우드 원칙에 의하면, 저수준 모듈을 언제 어떻게 호출할지는 고수준 모듈에서 결정하는 것이 좋습니다.
- 템플릿 메소드 패턴은 실전에서도 꽤 자주 쓰이지만 반드시 "교과서적인" 방식으로 적용되진 않습니다.
- 스트래티지 패턴과 템플릿 메소드 패턴은 모두 알고리즘을 캡슐화하는 패턴이지만 전자에서는 구성을, 후자에서는 상속을 이용합니다.
Posted by outliers
,

어댑터 패턴(Adapter Pattern)

정의

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환합니다. 어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있습니다.

- 이 패턴을 이용하면 호환되지 않는 인터페이스를 사용하는 클라이언트를 그대로 활용할 수 있습니다.
- 어댑티를 새로 바뀐 인터페이스로 감쌀 때는 객체 구성(Composition)을 사용합니다.
- 클라이언트를 특정 구현이 아닌 인터페이스에 연결 시킵니다
.

// Target Interface
public interface Duck {
	public void quack();
	public void fly();
}

public class DuckAdapter implements Turkey {
	Duck duck;	
	public DuckAdapter(Duck duck) {
		this.duck = duck;
	}
	public void gobble() {
		duck.quack();
	}
	public void fly() {
		duck.fly();
	}
}
// Adaptee Interface
public interface Turkey {
	public void gobble();
	public void fly();
}

public class WildTurky implements Turkey {
	public void gobble() {
		System.out.println("Gobble gobble");
	}
	public void fly() {
		System.out.println("I'm flying a short distnace");
	}
}
// Adapter에서는 Target Interface를 구현하고, Adapter는 Adaptee Interface로 구성되어 있습니다.
// 모든 요청은 Adaptee 에게 위임 됩니다. 
public class TurkeyAdapter implements Duck {
	Turkey turkey;
	
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}
	public void quack() {
		turkey.gobble();
	}
	public void fly() {
		for (int i = 0; i < 5; i++) {
			turkey.fly();
		}
	}
}
// Client 에서는 target Interface 만 볼 수 있습니다.
public class Main {
	public static void main(String[] args) {
		MallardDuck duck = new MallardDuck();
		
		WildTurky turkey = new WildTurky();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);
		
		System.out.println("The Turkey says.....");
		turkey.gobble();
		turkey.fly();
		
		System.out.println("\nThe Duck says.....");
		testDuck(duck);
		
		System.out.println("\nThe TurkeyAdapter says.....");
		testDuck(turkeyAdapter);
	}
	
	static void testDuck(Duck duck) {
		duck.quack();
		duck.fly();
	}
}


퍼사드 패턴(Facade Pattern)

정의

어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공합니다. 퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있습니다.

public class Facade {
	// 우리가 사용하고자 하는 서브시스템의 모든 구성요소들이 인스턴스 변수 형태로 저장 됩니다.
	DvdPlayer dvd;
	Light light;
	Screen screen;
	
	public Facade(DvdPlayer dvd, Light light, Screen screen) {
		this.dvd = dvd;
		this.light = light;
		this.screen = screen;
	}
	// 하나하나 수동으로 작업 했던 내용들을 하나의 메소드로 순서대로 처리 합니다.
	//  각 작업은 서브시스템에 들어있는 구성요소들에게 위임 됩니다.
	public watchMove(String movie) {
		dvd.on();
		light.on();
		dvd.open();
		dvd.insert();
		screen.on();
		dvd.play();
	}
}


객체지향 원칙(디자인 원칙)
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (의존성 뒤집기 윈칙:Dependency Inversion Principle)
7. 최소 지식 원칙(Principle of Least Knowledge) - 정말 친한 친구하고만 얘기하라.
- 시스템을 디자인할 때, 어떤 객체든 그 객체와 상호작용을 하는 클래스의 개수에 주의해야 하며, 그런 객체들과 어떤 식으로 상호작용을 하는지에도 주의를 기울여야 한다
- 최소 지식 원칙 단점 : 이 원칙을 적용하다 보면 다른 구성요소에 대한 메소드 호출을 처리하기 위해 "래퍼" 클래스를 더 만들어야 할 수도 있습니다. 그러다 보면 시스템이 더 복잡해지고, 개발 시간도 늘어나고, 성능이 떨어질 수도 있습니다.

7번 원칙에 대한 가이드 라인을 제시 합니다. 어떤 메소드에서든지 다음 네 종류의 객체의 메소드만을 호출 하면 됩니다.
1. 객체 자체
2. 메소드에 매개변수로 전달된 객체
3. 그 메소드에서 생성하거나 인스턴스를 만든 객체
4. 그 객체에 속하는 구성요소

예제)
- 원칙을 따르지 않은 경우

// station으로부터 thermometer라는 객체를 받은 다음, 그객체의 
// getTemperature() 메소드를 직접 호출 합니다.
public float getTemp() {
	Thermometer thermometer = station.getThermometer();
	return thermometer.getTemperature();
}


- 원칙을 따르는 경우
// 최소 지식 원칙을 적용하여 Station 클래스에 thermometer에 요청을 해 주는
// 메소드를 추가했습니다. 이렇게 하면 의존해야 하는 클래스의 개수를 줄일 수 있죠.
public float getTemp() {
	return station.getTemperature();
}


최소 지식 원칙을 따르면서 메소드를 호출하는 방법 예제
 
public class Car {
	// 클래스의 구성요서. 이 구성요소의 메소드는 호출해도 되죠.
	Engine engine;
	// 기타 인스턴스 변수
	
	public Car() {
		// 엔진 초기화 등을 처리
	}
	
	public void start(Key key) {
		// 새로운 객체를 생성 합니다. 이 객체의 메소드는 호출해도 됩니다.
		Doors doors = new Doors();
		// 매개변수로 전달된 객체의 메소드는 호출해도 됩니다.
		boolean authorized = key.turns();
		
		if (authorized) {
			// 이 객체의 구성요소의 메소드는 호출해도 되죠?
			engine.start();	
			// 객체 내에 있는 메소드는 호출해도 됩니다.
			updateDashboardDisplay();
			// 직접 생성하거나 인스턴스를 만든 객체의 메소드는 호출해도 됩니다.
			doors.lock();	
		}
		
	}
	
	public void updateDashboardDisplay() {
		// 디스플레이 갱신
	}
}


어댑터 패턴과 퍼사드 패턴의 차이점
어댑터 패턴은 인터페이스를 변경해서 클라이언트에서 필요로 하는 인터페이스로 적응시키기 위한 용도
퍼사드 패턴은 어떤 서브시스템에 대한 간단한 인터페이스를 제공하기 위한 용도
퍼사드는 인터페이스를 단순화 시키기 위한 용도로 쓰이는 반면, 어댑터는 인터페이스를 다른 인터페이스로 변환하기 위한 용도로 쓰입니다.

핵심 정리
- 기존 클래스를 사용하려고 하는데 인터페이스가 맞지 않으면 어댑터를 쓰면 됩니다.
- 큰 인터페이스, 또는 여러 인터페이스를 단순화시키거나 통합 시켜야 되는 경우에는 퍼사드를 쓰면 됩니다.
- 어댑터는 인터페이스를 클라이언트에서 원하는 인터페이스로 바꿔주는 역할을 합니다.
- 퍼사드는 클라이언트를 복잡한 서브시스템과 분리시켜주는 역할을 합니다.
- 어댑터를 구현할 때는 타겟 인터페이스의 크기와 구조에 따라 코딩해야 할 분량이 결정됩니다.
- 퍼사드 패턴에서는 서브시스템을 가지고 퍼사드를 만들고, 실제 작업은 서브클래스에 맡깁니다.
- 어댑터 패턴에는 객체 어댑터 패턴과 클래스 어댑터 패턴이 있다. 클래스 어댑터를 쓸려면 다중 상속 기능이 필요 합니다.
- 한 서브시트템에 퍼사드를 여러개 만들어도 됩니다.
- 어댑터는 객체를 감싸서 인터페이스를 바꾸기 위한 용도로, 데코레이터는 객체를 감싸서 새로운 행동을 추가하기 위한 용도로, 퍼사드는 일련의 객체들을 감싸서 단순화 시키기 위한 용도로 쓰입니다.
Posted by outliers
,

정의
요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있습니다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업취소 기능도 지원 가능 합니다.
어떤 작업을 요청한 쪽하고 그 작업을 처리한 쪽을 분리시킬 수 있다.

브레인 파워
커맨드 패턴 디자인을 사용했을 때 작업을 요구한 인보커와 작업을 처리하는 리시버가 어떤 식으로 분리되는 걸까요?
Invoker ---> Interface(Command) ---> Receiver
구성(Composition) 을 통해서 분리되어 있다.??

Null Object
클라이언트 쪽에서 null을 처리하지 않아도 되도록 하고 싶을 때 널 객체를 활용

Command noCommand = new NoCommand();
onCommand = noCommand
// 널 객체를 사용하면 if 문을 생략하고 사용할 수 있음.
if (onCommand != null)
    onCommand.execute();

다른 커맨드를 실행시킬수 있는 새로운 종류의 커맨드를 만들어서 여러 가지 커맨드를 한꺼번에 실행시킬 수 있는 생각
매크로커맨드
public class MacroCommand implements Command {
    Command[] command;
    public MacroCommand(Command[] command) {
        this.command = command;
    }
    public void execute() {
        for (int i = 0; i < command.length; i++) {
            command[i].execute();
        }
    }
    public void undo() {
        for (int i = 0; i < command.length; i++) {
            command[i].undo();
        }
    }
}


객체지향 원칙(디자인 원칙)
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (의존성 뒤집기 윈칙:Dependency Inversion Principle)

핵심 정리
- 커맨드 패턴을 이용하면 요청을 하는 객체와 그 요청을 수행하는 객체를 분리시킬 수 있습니다.
- 이렇게 분리시키는 과정의 중심에는 커맨드 객체가 있으며, 이 객체가 행동이 들어있는 리시버를 캡슐화 합니다.
- 인보커에서는 요청을 할 때는 커맨드 객체의 execute()메소드를 호출하면 됩니다. execute() 메소드에서는 리시버에 있는 행동을 호출합니다.
- 인보커는 커맨드를 통해서 매개변수화될 수 있습니다.
- execute() 메소드가 마지막으로 호출되기 전의 기존 상태로 되돌리기 위한 작업취소 메소드를 구현하면 커맨드 패턴을 통해서 작업취소 기능을 지원할 수도 있습니다.
- 매크로 커맨드는 커맨드를 확장해서 여러 개의 커맨드를 한꺼번에 호출할 수 있게 해주는 간단한 방법입니다. 매크로 커맨드에서도 어렵지 않게 작업취소 기능을 지원할 수 있습니다.
- 프로그래밍을 하다 보면 요청자체를 리시버한테 넘기지 않고 자기가 처리하는 "스마트" 커맨드 객체를 사용하는 경우도 종종 있습니다.
- 커맨드 패턴을 활용하여 로그및 트랜잭션 시스템을 구현하는 것도 가능합니다.

Posted by outliers
,

Singleton Pattern
정의
해당 클래스의 인스턴스가 하나만 만들어 지고, 어디서든 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.

싱글톤 패턴 예
public class Singleton {
    private static Singleton instance;

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

위와 같이 사용 할 경우 멀티 스레드 환경에서 여러개의 인스턴스가 생성 될 수 있습니다. 그래서 getInstance() 에 synchronized 를 사용해서 스레드 환경에서 발생되는 문제를 해결 할 수 있습니다. 문제는 해결되긴 하지만 동기화를 하면 속도가 느려지는 문제가 발생 합니다. 동기화가 꼭 필요한 시점은 인스턴스를 만드는 그 시점 한 번 뿐이지만 아래와 같이 사용을 하면 불필요한 오버헤드만 증가됩니다.
public class Singleton {
    private static Singleton instance;	
    private Singleton() {}	
    /**
     * getInstance()에 synchronized 키워드만 추가하면 한 스레드가 
     * 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 합니다.
     * 즉, 두 스레드가 이메소드를 동시에 실행시킬 수 없습니다.
     */
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

싱글톤을 사용하기 위한 방법
1. getInstance() 의 속도가 그리 중요하지 않으면 그냥 동기화를 사용한다.
2. 인스턴스를 필요할 때 생성하지 말고 처음부터 만들어 버린다.

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

3. DCL(Double-Checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄인다.
public class Singleton {
    // 1.5 이전의 버전에서는 volatile 키워드를 사용 할 수 없습니다.
    private volatile static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            // 이 방법을 사용 하면 처음에 한번만 동기화가 된다.
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Posted by outliers
,

객체 생성을 처리하는 클래스를 팩토리라고 부릅니다.

Simple Factory
객체를 생성하는 일을 전담하는 클래스

public class PizzaStore {
    Pizza orderPizza(String type) {
        Pizza pizza;
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        }
        pizza.prepare(); 
        pizza.bake(); 
        piaaz.cut();
        pizza.box();
        reutnr pizza;
    }
}

상황에 따로 코드가 변경되기 때문에 바뀌는 부분과 바뀌지 않는 부분을 분리 한 후 캡슐화를 한다.
Simple Factory 적용해서 위와 같은 문제점을 수정한다.
public class SimplePizzaFactory {
    public Pizza create Pizza(String type) {
        Pizza pizza = null;
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        }
        return pizza;
    }
}

분점에서 PizzaStore 를 활용하여 NYPizzaFactory라는 factory 를 만들어서 뉴욕식 피자를 만드는 팩토리를 사용한다.
NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.order("cheese");


문제점
분점에서 독자적인 방법들을 사용하기 시작했습니다.
굽는 방식이 달라진다거나 피자를 자르는 것을 까먹어 버리는일, 이상한 피자 상자를 사용 등
그래서 피자 가게와 피자 제작 과정 전체를 하나로 묶어주는 프레임워크를 만들어야 겠다는 결론에 도달했습니다.

피자를 만드는 활동 자체는 전부 PizzaStore 클래스에 국한시키면서도 분점마다 고유의 스타일을 살릴 수 있도록 하는 방법이 있습니다.

createPizza() 를 PizzaStore에 다시 넣고 그 메소드를 추상 메소드로 선언하고, 각 분점마다 고유의 스타일에 맞게 PizzaStore의 서브클래스를 만들도록 할 것입니다.

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    abstract Pizza createPizza(String type);
}

팩토리 메서드 패턴(Factory Method Pattern)

정의

객체를 생성하기위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만듭니다. 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것이죠.
-> 클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리시켜야 할 때, 어떤 구상 클래스를 필요로 하게 될지 미리 알 수 없는 경우에도 매우 유용합니다.

"메서드 팩토리 패턴에서는 어떤 클래스의 인스턴스를 만들지를 서브클래스에서 결정한다?
서브클래스에서 실제로 뭔가를 "결정"하는 것이 아니라, 우리가 선택하는 PizzaStore의 서브클래스 종류에 따라 결정되는 것이지만, 만들어지는 피자의 종류를 해당 서브클래스에서 결정한다고 할 수도 있겠죠
수퍼클래스에 있는 orderPizza()에서는 어떤 피자가 만들어 지는지 전혀 알 수 없다는 점을 잘 이해해야 합니다.
Pizza 인스턴스를 만드는 일을 이제 팩토리 역할을 하는 메소드에서 맡아서 처리 합니다.
팩토리 메소드는 객체 생성을 처리하며, 팩토리 메소드를 이용하면 객체를 생성하는 작업을 서브클래스에 캡슐화시킬 수 있습니다. 수퍼클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성 코드를 분리시킬 수 있습니다.

모든 팩토리 패턴에서는 객체 생성을 캡슐화합니다. 팩토리 메소드 패턴에서는 서브클래스에서 어떤 클래스를 만들지를 결정하게 함으로써 객체 생성을 캡슐화 합니다.

객체 생성 코드를 전부 한 객체 또는 메소드에 집어넣으면 코드에서 중복되는 내용을 제거할 수 있고, 나중에 관리할 때도 한군데에만 신경을 쓰면 됩니다. 구현이 아닌 인터페이스를 바탕으로 프로그래밍을 할 수 있게 되고, 그 결과 유연성과 확장성이 뛰어난 코드를 만들 수 있게 됩니다.

Simple Factory 와 Factory Method 차이점
간단한 팩토리를 사용할 때는 팩토리가 PizzaStore 안에 포함되는 별개의 객체이다.
팩토리 메서드 패턴을 이용하면 어떤 구현을 사용할지를 서브클래스에서 결정하는 프레임워크를 만들 수 있다는 결정적인 차이점이 있다.
간단한 팩토리에서는 객체생성을 캡슐화하는 방법을 사용하긴 하지만 팩토리 메소드 패턴처럼 강력한 유연성을 제공하지는 못한다. 생성하는 제품을 마음대로 변경할 수 없기 때문이다.

객체지향 원칙(디자인 원칙)
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (의존성 뒤집기 윈칙:Dependency Inversion Principle)

3번과 6번이 비슷하지만 의존성 뒤집기 원칙에서는 추상화를 더 많이 강조 합니다. 이 원칙에는 고수준 구성요소가 저수준 구성요소에 의존하면 안 된다는 것이 내포되어 있습니다. 항상 추상화에 의존하도록 만들어야 합니다.
고수준 구성요소는 다른 저수준 구성요소에 의해 정의되는 행동이 들어있는 구성요소를 뜻합니다.
예를 들어, PizzaStore의 행동은 Pizza에 의해 정의되기 때문에 PizzaStore는 고수준 구성요소라고 할 수 있고, Pizza 는 저수준 구성요소라고 할 수 있습니다.

의존성 뒤집기 원칙을 지키는데 도움이 될만한 가이드 라인
- 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 맙시다.
- 구상 클래스에서 유도된 클래스를 만들지 맙시다.
- 베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드 하지 맙시다.


추상 팩토리 패턴

정의
인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있습니다.
구상 클래스는 서브 클래스에 의해 만들어 집니다.
-> 클라이언트에서 서로 연관된 일련의 제품들을 만들어야 할 때, 즉 제품군을 만들어야 할 때

팩토리 메소드 패턴과 추상 팩토리 패턴
두 패턴 모두 객체 생성을 캡슐화해서 애플리케이션의 결합을 느슨하게 만들고, 특정 구현에 덜 의존하도록 만들 수 있다.
팩토리 메소드 패턴
- 상속을 통해서 객체를 생성
- 클라이언트와 구상 형식을 분리시켜주는 역할을 한다.
- 서브클래스에서 만드는 구상 형식을 활용하는 추상 생산자에서 코드를 구현한다.
- 클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리시켜야 할 때, 어떤 구상 클래스를 필요로 하게 될지 미리 알 수 없는 경우에도 유용하다.

추상 팩토리 패턴
- 객체 구성을 통해서 생성
- 일련의 연관된 제품을 하나로 묶을 수 있다.
- 제품을 추가하려면 인터페이스를 바꾸어야 한다.
- 팩토리 메소드 패턴을 사용하여 제품을 생성하는 경우가 있다.
- 클라이언트에서 서로 연관된 일련의 제품들을 만들어야 할 때, 즉 제품군을 만들어야 할 때 사용

핵심 정리
- 팩토리를 쓰면 객체 생성을 캡슐화할 수 있습니다.
- 간단한 팩토리는 엄밀하게 말해서 디자인 패턴은 아니지만, 클라이언트와 구상 클래스를 분리시키기 위한 간단한 기법으로 활용할 수 있습니다.
- 팩토리 메소드 패턴에서는 상속을 활용합니다. 객체 생성이 서브클래스에게 위임되죠. 서브클래스에서는 팩토리 메소드를 구현하여 객체를 생산합니다.
- 추상 팩토리 패턴에서는 객체 구성을 활용합니다. 객체 생성이 팩토리 인터페이스에서 선언한 메소들에서 구현되죠.
- 모든 팩토리 패턴에서는 애플리케이션의 구상 클래스에 대한 의존성을 줄여줌으로써 느슨한 결합을 도와줍니다.
- 팩토리 메소드 패턴에서는 어떤 클래스에서 인스턴스를 만드는 일을 서브클래스한테 넘깁니다.
- 추상 팩토리 패턴은 구상 클래스에 직접 의존하지 않고도 서로 관련된 객체들로 이루어진 제품군을 만들기 위한 용도로 쓰입니다.
- 의존성 뒤집기 원칙을 따르면 구상 형식에 대한 의존을 피하고 추상화를 지향할 수 있습니다.
- 팩토리는 구상 클래스가 아닌 추상 클래스/인터페이스에 맞춰서 코딩할 수 있게 해주는 강력한 기법입니다.
Posted by outliers
,
Decorator Pattern

정의
객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
- 데코레이터의 수퍼클래스는 자신이 장식하고 있는 객체의 수퍼클래스와 같습니다.
- 한 객체를 여러 개의 데코레이터로 감쌀 수 있습니다.
- 데코레이터는 자신이 감싸고 있는 객체와 같은 수퍼클래스를 가지고 있기 때문에 원래 객체(싸여져 있는 객체)가 들어갈 자리에 데코레이터 객체를 집어넣어도 상관 없습니다.
- 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 것 외에 원하는 추가적인 작업을 수행할 수 있습니다.
- 객체는 언제든지 감쌀 수 있기 때문에 실행중에 필요한 데코레이터를 마음대로 적용할 수 있습니다.

객체지향 원칙
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)

핵심정리
- 상속을 통해 확장을 할 수도 있지만, 디자인의 유연성 면에서보면 별로 좋지 않습니다.
- 기존 코드를 수정하지 않고도 행동을 확장하는 방법이 필요합니다.
- 구성과 위임을 통해서 실행중에 새로운 행동을 추가할 수 있습니다.
- 상속 대신 데코레이터 패턴을 통해서 행동을 확장할 수 있습니다.
- 데코레이터 패턴에서는 구상 구성요소를 감싸주는 데코레이터들을 사용합니다.
- 데코레이터 클래스의 형식은 그 클래스가 감싸고 있는 클래스의 형식을 반영 합니다.(상속 또는 인터페이스 구현을 통해서 자신이 감쌀 클래스와 같은 형식을 가지게 됩니다.)
- 데코레이터에서는 자기가 감싸고 있는 구성요소의 메소드를 호출한 결과에 새로운 기능을 더함으로써 행동을 확장합니다.
- 구성요소를 감싸는 데코레이터의 개수에는 제한이 없습니다.
- 구성요소의 클라이언트 입장에서는 데코레이터의 존재를 알 수 없습니다. 클라이언트에서 구성요소의 구체적인 형식에 의존하게 되는 경우는 예외입니다.
- 데코레이터 패턴을 사용하면 자잘한 객체들이 매우 많이 추가될 수 있고, 데코레이터를 너무 많이 사용하면 코드가 필요 이상으로 복잡해질 수도 있습니다.

I/O 도 데코레이터 패턴으로 이루어져 있습니다.
추상 구성요소 : InputStream
구상 구성요소 : FileInputStream, StringBufferInputStream, ByteArrayInputStream
추상 데코레이터 : FilterInputStream
구상 데코레이터 : PushbackInputStream, BufferedInputStream, DataInputStream, LineNumberInputStream
Posted by outliers
,
옵저버 패턴(Observer Pattern)

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

느스한 결합(Loose coupling)
두 객체가 느슨하게 결합되어 있다는 것은, 그 둘이 상호작용을 하긴 하지만 서로에 대해 서로 잘 모르다는 것을 의미
옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어 있는 객체 디자인을 제공합니다.

주제가 옵저버에 대해서는 아는 것은 옵저버가 특정 인터페이스(Observer 인터페이스)를 구현 한다는 것 뿐입니다.
- 옵저버는 언제든지 새로 추가할 수 있습니다.
- 새로운 형식의 옵저버를 추가 하려고 할 때도 주제를 전혀 변경할 필요가 없습니다.
- 주제와 옵저버는 서로 독립적으로 재사용할 수 있습니다.
- 주제나 옵저버가 바뀌더라도 서로한테 영향을 미치지는 않습니다.

디자인 원칙
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시킨다.
    -> 옵저버 패턴에서 변하는 것은 주제의 상태와 옵저버의 개수, 형식 입니다.
        옵저버 패턴에서는 주제를 바꾸지 않고도 주제의 상태에 의존하는 객체들을 바꿀수 있습니다.

2. 특정 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
    -> Subject 와 Observer 에서 모두 인터페이스를 사용했습니다.
        Subject에서는 Subject 인터페이스를 통해서 Observer 인터페이스를 구현하는 객체들의 등록/탈퇴를 관리하고
        그런 객체들한테 연락을 돌리지요. 이렇게 함으로써 결합을 느슨하게 만들 수 있습니다.

3. 상속보다는 구성을 활용한다.
    -> 옵저버 패턴에서는 구성을 활용하여, 옵저버들을 관리 합니다. 주제와 옵저버 사이의 관계는 상속이 아니라 구성에
        의해서 이루어지니까요.

4. 서로 상효작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
    -> 느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을
        구축할 수 있습니다. 객체 사이의 상호의존성을 최소화할 수 있기 때문이죠

핵심 정리
- 옵저버 패턴에서는 객체들 사이에 일대다 관계를 정의합니다.
- 주제, 또는 Observable 객체는 동일한 인터페이스를 써서 옵저버에 연락을 합니다.
- Observable에서는 옵저버들이 Observer 인터페이스를 구현한다는 것을 제외하면 옵저버에 대해 전혀 모르기 때문에, 이들 사이의 결합은 느슨한 결합입니다.
- 옵저버 패턴을 이용하면 주제객체에서 데이터를 보내거나(푸시 방식) 옵저버가 데이터를 가져오는(풀 방식)을 쓸 수 있습니다. (풀 방식이 더 "옳은" 것으로 간주됩니다.)
- 옵저버들한테 연락을 돌리는 순서에 절대로 의존하면 안 됩니다.
- 자바에는 범용으로 쓸 수 있는 java.util.Observable을 비롯하여 옵저버 패턴을 구현한 것들이 여럿 있습니다.
- java.util.Obseervable의 몇가지 문제점에 주의합시다.
- 필요하면 언제든지 주저하지 말고 Observable 클래스에 상응하는 클래스를 직접 구현해 봅시다.
- 스윙 및 여러 GUI 프레임워크에서 옵저버 패턴이 많이 쓰입니다.
- 옵저버 패턴은 JavaBeans나 RMI를 비롯하여, GUI가 아닌 다른 부분에서도 광범위하게 쓰입니다.


고민할 문제)
Observer Pattern 에서 데이터를 Pull 과 Push 방식이 존재 한다. 각각 장단점을 고민해 보세요.
느슨한 결합이 되지 않는다면 어떤 식으로 바꾸어야 느슨한 결합을 유지 할 수 있을까요?

Posted by outliers
,