- 예제 코드 Diagram -



* Factory Method Pattern :

객체를 생성하기 위한 인터페이스를 만든다. 

어떤 클래스의 인스턴스를 만드는 것을 서브클래스에서 결정하도록 한다. 

이 패턴을 이용하면 인스턴스를 만드는 일을 서브클래스에게 맡길 수 있다.



아래 그림을 참고해서 위의 그림을 설명하자면,

(출처 : http://www.cnblogs.com/zhuqiang/archive/2012/05/05/2484930.html )

Pizza 라는 추상 클래스(or 인터페이스)는 Product, 이를 상속받은 피자들이 Concrete Product.

PizzaStore 라는 추상 클래스(or 인터페이스)Creator, 이를 상속받은 체인점들이 ConcreteCreator 에 해당된다.

그리고 createPizza() 라는 메소드는 factoryMethod() 에 해당된다.


PizzaStore 는 생산자로서 Pizza 를 생산할 뿐이지 실질적으로 어떤 피자가 만들어질 지는 전혀 모른다.

(여기서 PizzaStore 는 클라이언트 코드다)

실질적으로 어떤 피자가 만들어질 지는 Pizza 의 서브클래스에 따라 결정이 된다.



* 의존성 뒤집기 원칙 (Dependency Inversion Principle) :

- 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하지 않도록 만들어야한다.

- 이를 지키는 데 도움이 될만한 3가지 가이드 라인

. 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 않는다.

. 구상 클래스에서 유도된 클래스를 만들지 않는다.

. Base 클래스에 이미 구현되어 있는, 모든 서브클래스에서 공유하는 메소드를 오버라이드하지 않는다.

 


* Abstract Factory Pattern :

서로 연관된, 또는 의존적인 객체들로 이루어진 제품군(Product Family)을 생성하기 위한 인터페이스를 제공한다.

구상 클래스는 서브클래스에 의해 만들어진다.

예제의 NYPizzaStore 클래스의 createPizza() 코드 中

Pizza pizza = null;

PizzaIngredientFactory inredientFactory = new NYPizzaIngredientFactory();

pizza = new CheesePizza(ingredientFactory);


PizzaIngredientFactory 가 위 그림의 Abstract Factory, NYPizzaIngredientFactory 가 Concrete Factory,

NYPizzaStore 가 Client 에 해당한다.


그 외에 Abstract Product A 는 피자 도우 정도 되고

그 하위의 Product A1, A2 는 각각 얇은 도우, 두꺼운 도우 정도라고 생각하면 된다.

마찬가지로 Abstract Product B 도 치즈 정도에 해당, Product B1, B2 는 모짜렐라 치즈 등에 해당된다.


Concrete Factory 1 에서는 Product A1, B1 을 제품군으로 이루고 있고,

Concrete Factory 2 에서는 Product A2, B2 를 제품군으로 이루고 있다.


그리고 위 그림의 Factory 가 생산자(Creator)라고 생각하면 

Abstract Factory 패턴에서는 Factory Method 패턴이 쓰이는 걸 볼 수 있다.



* Factory Method Abstract Factory

- 공통점

서브클래스를 통해서 객체를 만듦으로서 클라이언트(PizzaStore) 에서는

자신이 사용할 추상 형식(Pizza)만 알면 되고, Concrete 형식은 서브클래스에서 처리해준다.

즉, 클라이언트와 Concrete 형식을 분리시켜주는 역할을 한다.

객체 생성을 캡슐화해서 애플리케이션이 결합을 느슨하게 만들고 

특정 구현에 대한 의존성을 낮힐 수 있다.

- Factory Method

상속을 통해 Product 의 객체를 만든다. ex ) PizzaStore 추상 클래스의 createPizza() 를 오버라이드

클라이언트 코드와 인스턴스를 만드는 구상 클래스를 분리시켜야 할 때 활용할 수 있다.

- Abstract Factory

객체 구성을 통해 Product 의 객체를 만든다. ex ) pizza = new CheesePizza(ingredientFactory)

팩토리를 이용하고 싶으면 일단 인스턴스를 생성하고 추상 형식을 써서 만든 코드(Pizza)에게 이를 전달.

Concrete Factory 구현할 때, 종종 Factory Method 패턴을 사용하는 경우가 있다.

일련의 연관된 제품을 하나로 묶을 수 있다는 장점이 있다.

단, 제품군에 제품을 추가시킬 경우 Abstract Factory 의 createProductC() 를 추가해야 하는데 요약하자면!

인터페이스를 바꿔야해서 모든 서브클래스에도 영향을 미치게 된다는 단점이 있다.



* 객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다.

2) 상속보다는 구성을 활용한다.

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

4) 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

5) 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다(OCP).

6) 추상화된 것에 의존해라. 구상 클래스에 의존해서는 안 된다(의존성 뒤집기 법칙).



이 장에서의 정리 :

- Factory 를 쓰면 객체 생성을 캡슐화 할 수 있다.

- Factory Method 패턴에서는 어떤 클래스에서 인스턴스를 만드는 일을 서브클래스에게 넘긴다.

- Abstract Factory 패턴에서는 구상 클래스에 직접 의존하지 않고도 

서로 관련된 객체들로 이루어진 제품군을 만들기 위한 용도로 쓰인다.

- Factory 는 구상 클래스가 아닌 추상 클래스 / 인터페이스에 맞춰서 코딩할 수 있게 해주는 강력한 기법이다!


by kelicia 2014. 5. 19. 02:13

- 예제 코드 Diagram -



* Decorator Patterns

(상속 또는 인터페이스를 이용해 형식(Type)을 맞춤으로서객체에 추가적인 요건을 동적으로 첨가할 수 있다.

Decorator 는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

주의할 건 Decorator 는 상속을 통해서 행동을 물려받는 것이 목적이 아니다!


위 Diagram 를 통해 설명하자면,

- Beverage 가 추상 구성요소 (Component)

- DarkRoast, HouseBlend, Decaf, Espresso 가 구상 구성요소 (Concrete Component)

- CondimentDecorator 가 추상 데코레이터 (Decorator)

- Mocha, SteamMilk, Whip, Soy 가 구상 데코레이터 (Concrete Decorator)


※ Beverage 클래스가 추상 클래스인데, 이는 기존의 코드가 추상 클래스로 되어있었고

기존 코드는 가능한 고치지 않는 것이 좋기 때문에 그대로 쓴 것이다. 인터페이스로 구현하는 것도 가능하다.



* 예제 코드

Beverage beverage = new DarkRoast();
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
System.out.println(beverage.cost());

Beverage 의 구상요소들 cost() 의 내용은

return (음료 가격);


Condiment Decorator 의 구상요소들 cost() 의 내용은

return (데코 가격) + (생성자에서 전달받은 음료).cost();




* java.io 클래스와 Decorator 패턴


(출처:http://i.stack.imgur.com/O7pqc.png)



* OCP (Open-Closed Principle) :

- 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다 !



* Decorator 패턴의 장점 & 단점

- OCP 에 충실하면서 유연한 디자인을 만들어 낼 수 있다.

- Decorator 를 바로 끼워넣어도 클라이언트 쪽에서는 Decorator 를 사용한다는 것을 전혀 알 수 없다.

- 자잘한 클래스들이 엄청나게 추가되는 경우가 종종 발생해 이해하기 힘든 디자인이 만들어 질 수 있다.

(ex. java.io 클래스)

- Decorator 를 도입하면 구성 요소를 초기화하는 데 필요한 코드가 훨씬 복잡해진다.

구성 요소 인스턴스만 만드는 것 뿐만 아니라 꽤 많은 Decorator 로 감싸는 경우가 자주 발생한다.

(but 이 문제는 차후 다루는 장 ~팩토리, 빌더~ 를 통해 해결할 수 있다고 한다)


* 객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다.

2) 상속보다는 구성을 활용한다.

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

4) 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

5) 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다(OCP).



이 장에서의 정리 :

- 상속 또는 인터페이스 구현을 통해서 Decorator 가 감쌀 클래스와 같은 형식을 가지게 된다.

- Decorator 에서는 자기가 감싸고 있는 구성요소의 메소드를 호출한 결과에

 새로운 기능을 더함으로써 행동을 확장한다.

- 구성요소를 감싸는 Decorator 의 갯수에는 제한이 없으나

 자잘한 객체들이 너무 많이 추가될 수 있고, 이로 인해 코드가 필요 이상으로 복잡해질 수도 있다. 

 주의해서 남용하는 일이 없도록 하자!


by kelicia 2014. 5. 18. 02:44

- 예제 코드 Diagram -


쉽게 이해하자면,

출판사(Subject:주제) + 구독자(Observer) = Observer Pattern.


* Observer Pattern

- 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고,

자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다.



* 느슨한 결합 (Loose Coupling) 

- 두 객체가 상호작용을 하긴 하지만 서로에 대해 잘 모른다!

- Observer Pattern 에서는 이러한 디자인을 제공한다. Why ?

(1) Subject 가 Observer 에 대해서 아는 것은 Observer 가 특정 인터페이스를 구현한다는 것 뿐이다.

(2) Observer 는 언제든지 추가, 삭제할 수 있다.

(3) 새로운 타입의 Observer 를 추가하려고 해도 Subject 를 변경할 필요가 전혀 없다.

(4) Subject 와 Observer 는 서로 독립적으로 사용이 가능하다.

(4) Subject 또는 Observer 가 바뀌더라도 서로한테 영향을 미치지 않는다.



* Push 방식 vs. Pull 방식

Push 방식 : Subject 는 데이터가 변경될 때 마다 Observer 들에게 알려준다.

- 장점

Observer 는 Subject 에게 따로 데이터를 요청할 필요가 없다.

- 단점

Observer 가 불필요한 데이터까지 받아야 되는 경우가 생긴다.


Pull 방식 : Observer 가 갱신된 데이터가 필요할 때 Getter 메소드를 이용해 Subject 로부터 데이터를 가져온다.

- 장점

Observer 는 필요한 데이터만 가져올 수 있다.

Subject 가 확장되더라도 Subject 는 notify 메소드를 수정할 필요없이 Getter 메소드만 추가하면 된다.

- 단점

Observer 는 필요한 데이터가 많을 수록 Getter 메소드를 여러 번 호출해야만 한다.

(Pull 방식이 더 옳은 것으로 간주된다고 한다)



* java.util.Observable, java.util.Observer

(이미지 출처 : http://dellabate.wordpress.com/2012/03/03/gof-patterns-observer/)


- 처음의 Diagram 에서는 인터페이스를 이용해 Observer Pattern 을 구현했지만 

바로 위의 이미지와 같이 Java 에서 API 로 지원하기도 한다.


- Observable 의 주의점*

1) Interface 가 아닌 Class 라는 점!

- 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다는 객체지향의 원칙 3)에 위배된다.

- 클래스이기 때문에 서브클래스를 만들어야 한다는 것이 문제.

  이미 다른 수퍼클래스를 확장하고 있는 클래스에 Observable 의 기능을 추가할 수 없기 때문이다.

  그래서 재사용성에 제약이 생기게 된다.

- Observable 인터페이스가 없어 Java 의 Observer API 하고 잘 맞는 클래스를 직접 구현하는 것이 불가능.

2) Observable 클래스의 핵심 메소드를 외부에서 호출할 수 없다!

- protected 로 선언된 setChanged(), clearChanged() 는 Observable 의 서브클래스에서만 호출 가능하다.

- 상속보다는 구성을 활용한다는 객체지향의 원칙 2)에 위배된다.



* 객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다.

2) 상속보다는 구성을 활용한다. - Subject 와 Observer 사이의 관계는 상속이 아니라 구성에 의해 이루어진다!

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

4) 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.



이 장에서의 정리 :

- Subject (또는 Observable 객체) 는 동일한 인터페이스를 써서 Observer 에게 연락을 한다.

- Observer Pattern 은 Swing 및 여러 GUI 프레임워크, JavaBeans 나 RMI 같이 광범위하게 쓰인다.

ex ) Swing API 에서 JButton 의 ActionListener 등록


by kelicia 2014. 5. 16. 20:11

- 예제 코드 UML Diagram -



* Strategy Pattern :

- 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다.

이를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.




객체지향의 기초 :

1) 추상화    2) 캡슐화    3) 다형성    4) 상속



객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다. (무엇이 바뀌고, 안 바뀌는지 잘 구분해야한다)

2) 상속보다는 구성을 활용한다.

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.



이 장에서의 정리 :

- 훌륭한 객체지향의 디자인이라면... 재사용성, 확장성, 관리의 용이성을 갖춰야 한다.



# Reference

- '캡슐화'에 대한 강좌 (네이버 검색 키워드 : 객체지향 캡슐화, 카페 이름 : GIS 프로그래밍 연구소)

http://cafe.naver.com/gisdev/543


by kelicia 2014. 5. 16. 19:10


인턴하면서 WPF 프로그래밍을 접해봤고, MVVM 패턴이라는

디자인 패턴을 처음 접해보았다. 


학교에서 배운 디자인 패턴들도 있었으나 그 패턴들을

직접 코딩해보면서 알아가지 않아 확실히 느낀 바가 없어서 

배워도 그게 뭐였더라 하면서 애매했었는데 이번 기회에 정리해보려고 한다 :D





그림이 쬐끔해서 엄청 마음에 안듦



위 그림들을 서술형으로 정리해보자면



MVC (Model - View - Controller)

- Controller 에 직접 Input

- View 와 Controller : Many to One 관계

- View 는 Controller 를 참조하지 않음

- Model 은 View 를 간접적으로 참조함


- > Controller 에 입력이 들어오면 Controller 는 Model 에 있는 Data 를 조작하고, View 는 Model 에서 조작된 data 를

참조하여 View 를 수정한다. 이 때 View 가 Model 을 참조하거나 Model 이 View 를 참조하거나 하는 방식으로

변화에 대한 업데이트를 할텐데 결국 View 와 Model 이 참조를 할 수 밖에 없다는 이야기.



MVP (Model - View - Presenter)

- View 에 직접 Input

- View 와 Presenter : One to One 관계

- View 는 자신의 Presenter 를 참조하고 Presenter 역시 View 를 알고 있음

- View 는 Model 를 참조하지 않아 Presenter 를 통해 Model 을 업데이트함


-> View 에 입력이 들어오면 Presenter 에 data 를 요청하고, Presenter 는 자신이 참조하는 Model 에 업데이트를

요청하는 방식으로 동작한다. 이 경우 View와 Model 은 완벽히 분리되지만 View 와 Code 가 완벽히 분리됐다고

보기는 어렵다.



MVVM (Model - View - ViewModel)

- View 에 직접 Input

- View 와 ViewModel : Many to One 관계

- ViewModel 은 View 를 참조하지 않음

- View 는 Model 를 참조하지 않아 ViewModel 를 통해 Model 을 업데이트함


-> View 에 입력이 들어오면 View 가 참조하고 있는 ViewModel 에서 Binding 된 객체를 찾아 업데이트를 한다.

MVP 패턴에서는 Presenter 는 전적으로 View 의 형태에 따라 달라지지만,

MVVM 패턴에서는 ViewModel 이 View 를 참조하지 않으므로 Model 의 형태를 따른다고 할 수 있다.

View 는 Model 과 완벽히 분리되며 ViewModel 과도 Binding 을 통해 자동 업데이트 되므로 data 와도 완벽히 분리된다.


ex )

- .xaml (xaml의 .cs 파일에서 DataContext 프로퍼티를 참조할 ViewModel 로 Set)

<TextBlock Text={Binding TestText} />


- ViewModel.cs

private string _testText = "";

public string TestText

{

get { return _testText; }

set { _testText = value; OnPropertyChanged("TestText"); }

}




- 참고 자료

1. 선배님 발표자료 'WPF / MVVM 소개'

2. http://geekswithblogs.net/dlussier/archive/2009/11/21/136454.aspx




by kelicia 2013. 12. 19. 15:27
| 1 2 |