holub on-patterns-2-1

29
인인인인인인 인인인인인인인 인인인 인 인인 인인인인 (1/2) 인인인 10.06.19 인인인

Upload: -

Post on 13-Jan-2015

776 views

Category:

Technology


3 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Holub on-patterns-2-1

인터페이스로 프로그래밍하기그리고 몇 개의 생성패턴 (1/2)

아꿈사10.06.19

임정환

Page 2: Holub on-patterns-2-1

왜 extends 가 나쁜가 ?

• extend 는 OO 언어에서 중요한 기능을 차지• 따라서 extend 를 쓴다면 뭔가 OO 적인 것을 하고

있구나 ~ 하고 생각하면서 여기저기 막 쓰는 게 문제( 그러나 이 extends 는 부작용이 참 많다 )

• 객체 지향이란 ! 동일한 타입에 다양한 구현을 제공하는 다형성 (기반 클래스의 행동을 이를 상속한 클래스에서 재정의 하는 것 )이 객체 지향 사고의 핵심 !– 이런 다형성은 extends 보다는 implements(

인터페이스 ) 를 통해 가장 잘 성취할 수 있음 !( 앞으로 계속 설명할 것임 )

Page 3: Holub on-patterns-2-1

인터페이스 vs 클래스• 구현 상속보단 인터페이스 상속이 훨씬 바람직• 유연성의 상실– 구현 상속이 나쁜이유는 베이스 클래스의 함수가

이미 구현되어 있기 때문에 이 구현에 종속적일 수 밖에 없기 때문 !

– 결과적으로 수정이 힘듬 !– 애자일 개발 방법론의 핵심중 하나는 디자인과

개발을 병행 ! 완벽한 요구 사항 도출 이전에 프로그래밍 시작 .. 즉 자주 고쳐야 된다는 사실 ! 유연한 프로그램이 아니라면 병행 개발은 불가능 !

Page 4: Holub on-patterns-2-1

인터페이스 vs 클래스 ( 예제 1)

void f(){

LinkedList list = new LinkedList();//...modify(list);

}

void modify(LinkedList list){

list.add(...);doSomethingWith(list);

}

옵빠 !.. LinkedList 즐이에요 .. HastSet 으로 바꿔주세요 님하…

Page 5: Holub on-patterns-2-1

인터페이스 vs 클래스 ( 예제 1)

void f(){

LinkedList list = new LinkedList();//...modify(list);

}

void modify(LinkedList list){

list.add(...);doSomethingWith(list);

}

고쳐야 할 곳이 점점 는다 .

Page 6: Holub on-patterns-2-1

인터페이스 vs 클래스 ( 예제 1)

void f(){

Collection list = new LinkedList();//...modify(list);

}

void modify(Collection list){

list.add(...);doSomethingWith(list);

}

Page 7: Holub on-patterns-2-1

인터페이스 vs 클래스 ( 예제 1)

void f(){

Collection list = new Hashset();//...modify(list);

}

void modify(Collection list){

list.add(...);doSomethingWith(list);

}

한 곳만 바꾸면 된다 .

Page 8: Holub on-patterns-2-1

인터페이스 vs 클래스 ( 예제 2)f(){

Collection c = new Hashset();//...examine(c);

}

void examine(Collection c){

for(Iterator i = c.iterator(); i.hasNext();)//i.next() 통해 원소를 순회

}

f(){

Collection c = new Hashset();//...examine(c.iterator());

}

void examine(Iterator i){

for(; i.hasNext();)//i.next() 를 통해 원소를 순회

}

Iterator 를 받기 때문에 Collection 으로는 처리못하는 Map 도 처리 가능Iterator 를 오버라이딩하여 다르게 순회하는 기능 가능

일반화된 버전은 수정없이 이러한 모든 변화가 수용가능 !!

유연하구나 !!

Page 9: Holub on-patterns-2-1

결합도• 결합이 없는 프로그램이란 존재하지 않지만

… ..• 프로그램에서 코드간의 결합도가 낮을수록

좋다 .• 전역 변수가 나쁜이유 !( 강한 결합 )–전역 변수의 타입을 바꾼다고 생각햇을때 이

변수를 사용하는 모든 코드를 검사 , 수정 ,제테스트해야함

Page 10: Holub on-patterns-2-1

결합도• OO 개념을 충실히 지키면 결합도를 줄일 수

있다 ..–상수를 제외한 모든 필드는 private 로 !– get, set 은 줄여라 ..(public 이나 진배없음 )–나중에 유지보수시 편리함

Page 11: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제• 구현 상속은 자식과 부모 클래스간 강한

결합을 의미• 부모를 조금 수정했음에도 자식이 망가질 수

있음

Page 12: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 ( 예제 ) class Stack extends ArrayList{

private int topOfStack = 0;public void push(Object article){

add(topOfStack++, article);}public Object pop(){

return remove(--topOfStack);}public void pushMany(Object[] articles){

for(int i =0; i < articles.length; ++i)push(articles[i]);

}}

// 스택의 현재 위치를 저장하는 topOfStack 를 정의함Stack aStack = new Stack();aStack.push("1");aStack.push("2");aStack.clear(); //clear 는 Ar-rayList Method

대략 낭패 .. topOfStack은 0 이 안됨

개망 ..!

상속을 하게 되면 원하지 않던 메소드 clear() 까지 모두 상속하게 됨

Page 13: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 ( 예제 )

• ArrayList 를 상속한 것 자체가 개념적으로 말이안됨– Stack is ArrayList 가 아니다 ..

• 어떻게 고칠까 ?– Clear() 에서 예외를 던저버리자 !

• 이건 컴파일 에러를 런타임 에러로 옮겨 버리는 망할 놈의 생각– topOfStack 대신 size() 메소드 이용

• 이건 clear() 만 어떻게 고쳐보겠다는생각• 다른 함수에서 개망… . (ex. removeRange..)

– 지금 상황에서 상속은 어폐가 있다 .. 상속 대신 합성을 이용하자 !!!

Page 14: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 ( 예제 )class Stack{

private int topOfStack = 0;private ArrayList theData = new ArrayList();public void push(Object article){

theData.add(topOfStack++, article);}public Object pop(){

return theData.remove(--topOfStack);}public void pushMany(Object[] articles){

for(int i =0; i < articles.length;++i)push(articles[i]);

}public int size(){

return theData.size();}

}

이제 ArrayList 가 수정되는 일이 생겨도 개망하는 일은 없어짐 !변경에 강해짐 ~

상속하지 않고 ArrayList 를 이용하여 구현하였다 .

Page 15: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 ( 예제 )

class MonitorableStack extends Stack{

private int highWaterMark = 0;private int lowWaterMark = 0;public void push(Object o){

push(o);if(size() > highWaterMark)

highWaterMark = size();}public Object pop(){

Object poppedItem = pop();if(size() < lowWaterMark)

lowWaterMark=size();return poppedItem;

}public int maximumSize(){return highWaterMark;}public int minimumSize(){return lowWaterMark;}public void resetMarks(){highWaterMark=lowWaterMark=size();}}

}

85~86 쪽 예제인데 , Stack 을 또다시 구현 상속하는 Moni-torableStack 에 대해 애기하고 있음 ..

앞에 경우처럼 pushMany에서 또 개망하는 일이 발생됨 ..!

이와 같은 이유는 기본적으로 구현상속이란 개념이 Base Class 에 뭔가 구현이 되어 있는데 구현자의 의도와는 다르게 베이스 클래스의 함수가 동작할 수 있기 때문임 ..

정리하면 .. 구현상속 쓰지마 ! 깜빡했다간 오동작한다구 !!

Page 16: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 ( 예제 )

• 구현 상속은 문제가 많다 . 왠만하면 인터페이스 상속을 사용하라 !– 상속된 기능이 없기 때문에 잘못될 일도없다– 인터페이스 상속을 사용하면 안전하게 다형성 획득 !– 구현 상속은 필요한 기능이지만 위험하기 때문에 사용하기 전에 충분히

심사숙고 필 !– 물론 인터페이스 상속은 일일히 다 만들어줘야 하기 때문에 코딩이 느는

단점이 있기는 하다 .– 하지만 오동작하는 것보다 낫다… ! ( 이클립스를 사용하면 쉽게 위임

메소드를 만들 수 있다고 함 )

• 그러나 이렇게 말하는 자 있도다…– 난 기반 클래스 수정할 때마다 파생 클래스는 다 검토를 하기 때문에 문제

없다구 !!!– 이미 당신은 기반 클래스를 확장하는 것이 아니라 인터페이스 구현을 하고

있소이다… 걍 인터페이스 구현을 하시오 .

Page 17: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 ( 예제 )

• 앞에서 설명한 Stack, MonitorableStack 정답은…– 죄송합니다… 타이핑이 넘 많아서… . 88,89 쪽을 펴주세요 ..

• Interface 로 Stack 만듭니다 .• SimpleStack 은 implements 를 통해 구현함• SimpleStack 은 ArrayList 를 사용하여 기능을 구현 -

위임 ( 합성 )• MonitorableStack 은 역시 implements 를 통해 구현• MonitorableStack 은 SimpleList 를 사용하여 기능을

구현 – 위임 ( 합성 )

Page 18: Holub on-patterns-2-1

다중 상속• 다중 상속이 유용할 때도 있는데 역시 구현

상속보다는 캡슐화 ( 합성 ) 을 이용하라 ~

Page 19: Holub on-patterns-2-1

프레임워크• MFC 를 잘근잘근 씹고 있는데… .–MFC 가 널리 사용됬기 때문에 마소의 방식은 올바른 거야라고 많이들 생각하고 있음

–MFC 는 기본적으로 구현 상속 기반이다 .• View, document 를 상속해서 쓰고 있음 ..• 역시 . 개망의 지름길…

–또한 뭔가 기능을 변경하고 싶으면 상속 기반은 클래스를 상속해서 만들어야 되는데 이렇게 되면 클래스가 엄청늘어나게 되고 클래스가 늘어나면 늘수록 개발기간은 길어진다 .

Page 20: Holub on-patterns-2-1

지금까지 구현상속의 폐혜를 보셨습니다 . 이제 본격적으로 패턴을 보아요 .. 하지만

여기서도 구현 상속의 폐혜를 설명하기 위해 패턴을 예로 듭니다 .

Template Method 패턴Factory Method 패턴 ( 애가 문제임 )

Page 21: Holub on-patterns-2-1

Template Method 패턴

Editor

#UpdateBuffer(keystroke)

CustomEditor

#UpdateBuffer(keystroke)

EditableTextControl StandaloneEditor

Template Method 패턴

기반 클래스 코드가 오버라이딩 가능한 메소드를 호출하고 메소드의 구현은 기반 클래스를 구현 상속한 클래스에 제공하는 ..

저자도 이디엄과 패턴의 경계가 모호한 정도의 패턴이라고 함

패턴 참 쉽죠잉 ..~~?

Page 22: Holub on-patterns-2-1

Factory Method 패턴• Factory Method 패턴은 기반 클래스에 알려지지

않은 구체 클래스를 생성하는 Template Method라 할 수 있다 .

• 사실 코드 보고 이해하는 데 정말 어려웠음 ;; • Factory Method 패턴은 아주 유연하지만 ( 기능

수정 추가가 쉽다 ) 필요 이상으로 복잡하다 . 이 복잡함이 유지 보수성과 사용 편의성을 크게 해침 ..

• 책에 예제는 너무 복잡하여 다른 예제를 준비– 인용 : http://underclub.tistory.com/125

Page 23: Holub on-patterns-2-1

Factory Method 패턴

Product 는 Factory Method 패턴을 사용하여 생성할 부모 클래스

IDCard 는 실제 생성되는 자식 클래스

Factory 는 Product 를 생성을 담당하는 클래스

IDCardFactory 는 실제 기능을 가진 자식 Product를 생성하는 클래스

Page 24: Holub on-patterns-2-1

Factory Method 패턴

Use 함수가 product 마다의 고유 기능을 담당Create 는 public 에 final 로 객체생성은 저 함수로 담당Createproduct 와 registerproduct 는 생성해줄 때객체마다 다른 기능을 부여해줄 수 있음 (abstract)

Page 25: Holub on-patterns-2-1

Factory Method 패턴

실제 사용

IDCardFactory 를 만들고Create 함수로 생성Use 함수로 사용

Factory Method 패턴의 문제점

뭔가 패턴을 사용하기 위해 extends 를 사용하고 있다는 냄새다 .Product 를 상속해서 IDCard 를 만들고 있는데 이는 Factory 에서 사용하기 위해 이런 구조로 만들었다 . Extends 가 필요해서 한것인 아니라 !!!;;Factory Method 패턴은 extends 관계를 잘못 사용하고 있다 .!! 대안은 있다 . Strat-egy 패턴이 멋진 대안이 될 수 있다 .( 전 모른다는 … 담에 설명 좀 잘…부탁 ㅎ )

Page 26: Holub on-patterns-2-1

깨지기 쉬운 기반 클래스 문제 정리• 꼼수를 쓰지말고 , 구현 상속 대신 캡슐화 ( 합

성 ) 와 인터페이스를 사용하라 !• 앞으로 설명할 패턴 중 상당수가 구현 상속을

인터페이스 상속으로 바꾸는 방법에 대한 내용• 추상화를 잘하라 .. 추상화를 잘하면 유연해짐

( 수정이 쉬워짐 ), 하지만 복잡성을 증가함 . 유연성과 복잡성 사이에서 적절한 트레이드 오프를 하라 ~

• 지금까지 한 말들을 정리한 챕터임 ..

Page 27: Holub on-patterns-2-1

언제 extends 를 사용해도 좋은가 ?

• 클래스 정규화– 클래스의 중복되는 기능을 제거하는 경우– 구현 상속을 사용하지 않는다면 파생 클래스에서 동일한 코드를 반복해야 함– 여기서는 구현하고자 하는 프로그램이 런타임에 객체들이 서로 어떻게 메시지를 주고

받는 지에 대한 ‘동적모델’을 규명한 후 !에 각 클래스가 사용하는 공통적인 기능이 파악이 된다면 그 때 ! extends를 사용하라고 함 !

• 수행되는 연산이 공유될 때– Is- a 관계가 클래스 계층 구조를 검증하는 데 효율적이지 못함

• Manager is a Employee 라고 할때 과연 Manager Extends Employee 인가… .– Employee 와 Manager 가 같은 일을 할 수도– 전혀 다른일을 할 수도– Manager 가 Employee 가 하는 일까지 총괄해서 할 수도

– 결론 : 자연어에 휘둘리지 말고 , 수행되는 연산의 공유에 초점을 맞추어라 !

• 컴파일 타임 타입 검사– 컴파일 타임에 타입 검사를 해야할 경우가 있다 .– 물론 타입검사를 하지않고 Implements를 사용하는 쪽으로 구현할 수도 있다 .– 복잡도를 분석한 후 단순한 것을 사용하자

Page 28: Holub on-patterns-2-1

결론• extends( 구현 상속 ) 왠만하면 쓰지마 ..• extends 써야 한다면 기능에 맞게 잘 써 ..– Factory Method 처럼 쓸려면 쓰지마…

Page 29: Holub on-patterns-2-1

• 캄사합니다… .• 질문은 사절~ C++ 촙오에요 ..