[0820 석재호]headfirst디자인패턴

Post on 23-Jun-2015

1.852 views 3 download

Transcript of [0820 석재호]headfirst디자인패턴

Head First 디자인 패턴 1/2

데브루키 꿜라 석재호

목 차

디자인 패턴 소개

전략 (Strategy) 패턴

옵저버 패턴

데코레이터 패턴

팩토리 패턴

싱글턴 패턴

커맨드 패턴

어댑터 패턴 & 퍼사드 패턴

디자인 패턴

좋은 구조의 소프트웨어 만들자

설 계

구 현 개 선 점 발 견

하 나 의 검 증 된 패 턴 으 로 정 립

디자인 패턴

디자인 패턴은 검증된 KNOW HOW

디자인 패턴은 라이브러리 / 프레임워크의 상위 개념

디자인 패턴의 장점

디자인 패턴은 검증된 해결책 - 더 적은 노력으로 비슷한 문제의 해결이 가능

팀 내 의사소통의 효율성 - 디자인에 대한 아이디어를 간단한 용어만으로 확실히 전달하고 이해할 수 있다

디자인 패턴 , 주의할 점

디자인 패턴은 그 자체로 해답이 되지 않는다 - 각 상황에 맞는 변형이 필요 이를 위해서는 경험을 통한 노하우 습득이 중요

개발 경험이 부족하면 이해하기 어렵다 - … 그래서 이해를 못하고 있습니다…

* 참고로 발표 중 ‘ 인터페이스’ == ‘ 추상클래스’ 라고 보시면 ..

전략 패턴

디자인 원칙

• 애플리케이션에서 달라지는 부분과 달라지지 않는 부분을 분리– 요구사항에 따라 변경될 부분은 따로 캡슐화 시킨다면

이후 바뀌지 않는 부분에 변경을 주지 않고 수정이 가능

• 구현이 아닌 인터페이스에 맞추는 프로그래밍– 요소 간 관계가 느슨해져 프로그램에 유연성이 생기고

동적으로 동작을 변경하는 것이 가능해진다

디자인 원칙

• 상속보다는 구성을 활용한다– 유연성의 향상– 동적인 행동 변경

전략 패턴이란 ?

사용되는 알고리즘군을 정의하고각각을 캡슐화 하여교환해가며 사용할 수 있도록 만든다

전략 패턴 예제파생된 두 오리 클래스가 자신에게 적절한출력을 하도록 Display 함수를 오버라이드

Duck

Quack()Display()

M_Duck

Display() 적절히 오버라이드

R_Duck

Display() 적절히 오버라이드

음… . 잘 나는걸 ?

Duck

Quack()Display()Fly()

M_Duck

Display() 적절히 오버라이드

R_Duck

Display() 적절히 오버라이드

“ 날게 해주세요 .”

음… . 귀여운걸 ?

Duck

Quack()Display()Fly()

M_Duck

Display() 적절히 오버라이드

R_Duck

Display() 적절히 오버라이드

“ 귀여운 고무오리 인형을 추가해주시죠 .”

Gomu_Duck

Quack() 삑삑 소리Display()

음… . 안되겠는걸 ?

Duck

Quack()Display()Fly()

M_Duck

Display() 적절히 오버라이드

R_Duck

Display() 적절히 오버라이드

“ 인형이 왜 날죠 ? 장난하시나 .. ”

Gomu_Duck

Quack() 삑삑 소리Display()

음… .

날지 못하도록Fly() 를 오버라이드 하면

되 겠 는 걸 ?

Gomu_Duck

Quack() 삑삑 소리Display() Fly() { 아무것도 하지 않는다}

음… .

이젠 슬슬 힘든걸 ?

Duck

Quack()Display()Fly()

M_Duck

Display() 적절히 오버라이드

R_Duck

Display() 적절히 오버라이드

“ 날지도 못하고 소리도 안 나는 나무 인형도 ..”

Gomu_Duck

Quack() Display() Fly()

인터페이스를 쓰면 괜찮겠는걸 ?

변화할 수 있는 부분은 따로 빼라고 했었지 ..

부모인 Duck 클래스로부터 Fly() 와 Quack() 을 빼내서 각각을 순수가상함수로 가지는 추상 클래스 Flyable 과 Quackable 을 만들면 어떨까 ? ( C++ 버전… )

필요한 오리 놈들은 이것을 다중상속 받아 저마다 필요한대로 구현하게 되겠지

점점 오리는 추가되고 ..

지나고 보니 나는 법도 우는 법도 거의 동일한데 그 메소드 몇 개 오버라이드 하는게 귀찮아서 87 종의 오리에게 일일이 Fly() 를 넣어주고 102 종의 오리에게 Quack() 을 넣어주게 되었구나

이건 좀 아닌 것 같은걸 ?

전략 패턴의 등장

• 알고리즘군의 정의 , 그리고 캡슐화 필요

• Fly() 와 Quack() 은 다양하게 변화할 수 있다Duck 의 다른 부분은 변화하지 않는다– 변화하는 부분을 떼어놓자

알고리즘군

행 동

나는 행동 우는 행동

똑바로

삐뚤빼뚤

못 남 ㅋ

뒤 로

꽥 꽥

어머니 ~

쾍 쾍

콜록 콜록

알고리즘군의 구현 - 인터페이스와 구현객체

FlyInterface

Fly()

FlyStraightFly()

FlyBackFly()

CannotFlyFly()

QuackInterface

Quack()

QquaackQuack()

GuakQuack()

SilentQuack()

알고리즘군의 사용

Duck

performQuack();performFly();Display();…

FlyInterface *flyInt;QuackInterface *quackInt;

이제 여기에 알고리즘 객체를갈아 끼우기만 하면되겠는걸 ?

새로운 행동이요구된다 해도이 녀석은 더 이상 고칠 필요 없겠는걸 ?

구현보단 인터페이스 . 상속보단 구성

Duck

Fly 인터페이스

GomuDuck

OhDuck

BindaeDuck

똑바로 날기

옆으로 날기

못 날기

Quack 인터페이스

꽥 꽥

엉 엉

뜨얿

의견 교환의 시간

음 .. 설명이 부족한걸 ?

옵저버 패턴

디자인 원칙

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

이상하게도… 그 얘기가 그 얘기 같은…

옵저버 패턴

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고자동으로 연락 받은 객체의 내용이 갱신되는 방식

객체 – 주제 (subject) 객체의존하는 다른 객체 – 옵저버 (obse….) 객체

예를 들자면 ..

신문 , 잡지의 구독 매커니즘- 신문사에서 신문을 찍는다- 독자가 신문사에 구독 신청을 하면 , 구독을 해지하기까지 계속해서 배달을 받을 수 있다- 더 보기 싫으면 구독 해지 신청을 한다 . 신문은 더 이상 오지 않는다- 신문사에는 개인 , 영업소 , 기타 회사 등에서 계속해서 구독 및 해지 신청이 온다신문사 = 주제 객체

옵저버 패턴신문사 ( 주제 객체 )

구독자를 등록 , 해지하는 기능 필요등록된 구독자 모두에게 새 신문이 나올 때마다 전달등록된 구독자들을 관리할 목록이 필요

- 옵저버 객체 리스트를 저장할 수 있어야 한다

옵저버 패턴구독자 ( 옵저버 객체 )새 신문이 도착하면 신문을 받아야 함

- 주제 객체의 상태 변화 시 옵저버의 업데이트 함수를 호출 할 수 있어야 함

옵저버 패턴신문사는 구독자가 누구건 동일하게 등록 , 해제구독자도 신문사가 어디건 새 신문을 받는 것은 동일

구현된 객체가 아닌 인터페이스에 맞춰라 !

느슨한 결합으로 유연성을 높여라 !

옵저버 패턴 구조신문사 인터페이스구독자등록 ()구독자해지 ()새신문전달 ()

구독자 인터페이스새신문받기 ()

신문사 구현클래스구독자등록 ()구독자해지 ()새신문전달 ()

추가 _ 상태설정함수()

구독자 구현클래스새신문받기 ()

추가 _ 옵저버용함수()

구독자 여러 개 등록 가능

신문사를 알아야 등록요청 가능

느슨 느슨

느슨느슨 열매의 힘주제는 옵저버가 뭘 하는 애인지 알 필요가 없음옵저버는 언제든지 추가 , 제거가 가능새로운 형식의 옵저버가 나타나도 주제 변경 없음주제와 옵저버는 독립적으로 다른 곳에 재사용이

가능각각의 변경이 서로에게 영향을 주지 않음

다시 찾아온 의견 교환의 시간

보충을 부탁드립니다

옵저버 패턴의 두 가지 방식 주제가 데이터를 보내는 푸시 방식 (방금 본 방식 )

옵저버가 주제로부터 필요한 상태를 빼내는 풀 방식

데코레이터 패턴

디자인 원칙클래스는 확장에 대해서는 열려있어야 하고

코드 변경에 있어서는 닫혀있어야 한다

데코레이터 패턴

객체에 추가적인 요건을 동적으로 첨가한다

서브클래스를 만드는 것을 통해서 기능을 유연하게 확장하는 방법을 제시하는 패턴

데코레이터 패턴책에서는 오만가지 커피를 예로 들어 설명을 하지만 ..

게임 아이템으로 예를 들어 흥미를 유발하겠습니다

사실 흥미유발 안되겠죠 ..

칼칼

“… 좀 너무 밋밋하네요 .

무기 강화 시스템도넣어주시구요 .

불속성 , 바람속성버프도 있으면 좋겠고 ..

음 ..

축복받은 무기랑 저주받은 무기는 기본으로 있어야겠죠 ?”

무기 인터페이스

getDamage();getDescription();

int 데미지 ;

칼getDamage();

도 끼getDamage();

활getDamage();

음…

무기 인터페이스

getDamage();getDescription();

int 데미지 ;

칼getDamage();

도 끼getDamage();

활getDamage();

저주받은 칼

getDamage();

바람의 축복받은 칼

getDamage();강화된 저주받은 칼

getDamage();불의 도끼

getDamage();강화된 얼음의 축복받은 도끼

getDamage();요정의 저주받은 활

getDamage();

강화된 바람의 활

getDamage();

오크족의 드러운 칼

getDamage();

디아 3 룬 + 스킬 조합은 968억개가 넘는다던데… .

바람의

getDamage();

무기와 버프를 모두 따로 따로

칼getDamage();

축복받은

getDamage();칼

getDamage();

데미지 계산

데코레이터 패턴 구조무기와 버프 객체가 동일한 인터페이스로 겹쳐지려면 같은 상속계통에 있어야 한다 - 공통의 부모가 있어야 공통의 그릇이 생긴다

버프는 다중으로 겹쳐지기 위해 자신이 담을 객체를저장할 수 있어야 한다 ( 무기는 그럴 필요 없다 )

- 다시 말해 기반 클래스의 포인터를 멤버로 가진다

데코레이터 패턴 구조무기 인터페이스

getDamage();getDescription();

버프 인터페이스

getDescription();

칼getDamage();getDescription(); 바 람 의

getDamage();getDescription();

무기인터페이스 * 안쪽버프 ;

강 화 된

getDamage();getDescription();

무기인터페이스 * 안쪽버프 ;

도 끼getDamage();getDescription();

객체 겹치기

생성자나 기타 함수의 인수로 객체를 받아서 ..Sword kal;

Powered kangWha(kal);

WindElement baram(kangWha);

baram.getDamage();

호출 시 강화된 바람의 칼 데미지가 계산되어 나옴Description 도 같은 방식으로 구현 가능

의 to the 견 to the 교 to the 환

실제로 위와 같은 경우 이러한 패턴이 유효한지 ..?

데코레이터 패턴의 실제 사례 : 자바 I/OFileInputStream

BufferedInputStream

LineNumberInputStream 들이 안쪽 객체를 싸고 동작

팩토리 패턴

스타 2 하시는 분nufac@hanmail.net친추

실버 , 브론즈 우대

팩토리 패턴사실 팩토리 자체는 디자인 패턴이 아님디자인 패턴인 ‘팩토리 메서드 패턴’을 설명하기 위해 필요

팩토리 메서드 패턴 객체를 생성하기 위한 인터페이스를 정의하는데 어떤 클래스의 인터페이스를 만들지는 서브클래스에서 결정한다

화염차 (Hellion)

단축키 - E

크루시오 공성전차 (Siege Tank)

단축키 - S

토르 (Thor)

단축키 - T

생산을 .. Unit *factoryUnit;

If(입력키 == ‘E’)

factoryUnit = new Hellion();

Else if(입력키 == ‘S’)

factoryUnit = new Siege();

Else if(입력키 == ‘T’)

factoryUnit = new Thor();

팩토리의 필요성

이전과 같은 방식의 코드는변경이나 확장 시마다 코드의 추가 제거가 필요관리 갱신이 어려워지고 오류 발생 가능성이 높아짐

다시 한 번 디자인 원칙

바뀌는 부분을 안 바뀌는 부분에서 떼어내 캡슐화

바뀌는 부분은 방금 본 객체 생성 부분안 바뀌는 부분은 그 객체를 가지고 행해지는 일련의 동작들 (attack, move 등 동일한 인터페이스로 동작 )

생성부 캡슐화

아까의 코드를 Factory 라는 클래스에 넣고 메인부에서는 Factory 객체를 만들어키 값으로 유닛을 생성하는 함수를 호출한다

Factory* fact; // 객체 초기화 코드 생략Unit* newUnit;

newUnit = fact->createUnit(key); // 캡슐화된 결과

군수공장의 다양화에도 ..

새로운 형태의 공장 추가에도군수공장 자체는 변화 필요없다

유닛 생산시에 어떤 유닛이 생산될지를 서브클래스가 결정하게 된다

군수공장 인터페이스

createUnit();displayUnit();

비싼 군수공장

createUnit();

싼 군수공장

createUnit();

유닛 생산을 위해Unit

싸구려 토르

싸구려 화염차

싸구려 탱크

비싼 토르

비싼 화염차

비싼 탱크

비싼 군수공장

createUnit();

당연히 군수공장이 유닛을 알아야 유닛을 만들 수 있다

팩토리 메서드 패턴 구조

제품 인터페이스

구상 제품

팩토리 인터페이스

factoryMethod();otherMethod();

구상 팩토리

factoryMethod();

의견 교환의 시간

꼭 팩토리 메서드를 추상으로 만들 필요는 없다

추상 팩토리 패턴 ..

싱글턴 패턴

싱글턴 패턴

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

유일무이한 객체를 필요한 타이밍에 생성

싱글턴 패턴의 이점

전역변수에 비해필요할 때만 만들 수 있어 자원 절약

여러 번역단위에서 중복 생성할 위험이 없음

싱글턴 패턴의 이점

전역변수에 비해필요할 때만 만들 수 있어 자원 절약

여러 번역단위에서 중복 생성할 위험이 없음

고전적인 싱글턴 패턴 구현법생성자를 통한 생성을 막아라

- 생성자의 private 선언

해당 클래스의 포인터 멤버 변수를 내부에 하나 생성

정적 멤버 함수를 만들어라- 포인터가 null 이면 새 객체 생성 , 아닐 시 그대로 해당 포인터를 리턴한다

class Singleton {

public :

static Singleton* getInstance() {

if(singleInstance == null)

singleInstance = new Singleton();

return singleInstance;

}

private :

static Singleton* singleInstance;

Singleton() { }

}

의견 교환의 시간

과연 멀티스레드 환경에서도 유일무이할까 ?

싱글턴은 언제 파괴되는가 ?

피닉스 싱글턴 ? ( http://ikpil.com/1275 )

어댑터 패턴 & 퍼사드 패턴

어댑터 패턴과 퍼사드 패턴‘ 적응시키기’

어댑터 패턴

한 클래스의 인터페이스를 클라이언트에서사용하고자 하는 다른 인터페이스로 변환

인터페이스 호환성 문제 때문에 같이 쓸 수 없는클래스들을 연결해서 쓸 수 있다

어댑터 패턴

오리 .. 우리 다시 만나게 되었는걸 ?

M_Duck 이나 R_Duck 객체를 만들고 그 포인터를인자로 testDuck 을 호출할 수 있겠는걸 ?

Duck

Quack();Fly();

M_Duck

Quack();Fly();

R_Duck

Quack();Fly();

메 인

Void testDuck(Duck* d) { d->Quack(); d->Fly();}

“ 그간 편했죠 ? 칠면조도 넣어주시죠 ?”

칠면조 ? 이건 뭘 어쩌라는 건지 ..

testDuck 의 인자로는 넣을 수가 없어 ..

이깟 새 때문에 메인 파트를 고치고 새로운 함수를 만들어 따로 호출해야겠는걸 ?

어차피 우는 것은 같은데 망할 인터페이스 때문에 ..

이젠 내가 울고 싶은걸 ?

Turkey

Gobble();Fly();

WildTurkey

Gobble();Fly();

메 인

Void testDuck(Duck* d) { d->Quack(); d->Fly();}

어댑터 !!!

TurkeyAdapter

Quack();Fly();

TurkeyAdapter

TurkeyAdapter(Turkey* turkey): tu(turkey) {}

Void Quack() { tu.Gobble(); }

Void Fly(){ tu.Fly(); }

Duck

Quack();Fly();

Turkey *tu;

어댑터 패턴

클라이언트에서는 어댑터가 껴있는지 알지도 못하겠는걸 ?

어떠냐 !!!!! 요구사항의 괴롭힘에도 나는 굴하지 않는걸 !!!!

Duck

Quack();Fly();

M_Duck

Quack();Fly();

M_Duck* mD = new M_Duck();WildTurkey* wT = new WildTurkey();Duck* tA = new TurkeyAdapter(wT);

testDuck(mD);testDuck(tA); // 문제없이 동작

TurkeyAdapter

어댑터 패턴 구조

Target Interface

Request()

Client

Adapter

Request()

Adaptee

specificRequest()

어탭터에서 타겟 인터페이스를 구현 . 어댑티를 구성을 통해 보유 .클라이언트가 구현이 아닌 인터페이스에 연결된다 어댑터를 교체해가며 유연한 사용이 가능하다는 이야기

클래스 어댑터지금까지 배운 것은 객체 어댑터 ( 구성 활용 )

클래스 어댑터는 다중 상속이 가능한 언어에서 사용 가능

특정 어댑티 클래스에만 적용되는 어댑터 클래스라는 단점어댑티의 행동을 오버라이드 할 수 있는 장점어댑터 내에 어댑티 객체가 필요하지 않아 효율적

Target Interface

Request()

Client

Adapter

Request()

Adaptee

specificRequest()

퍼사드 패턴현재 이 페이지 작성시각 8월 20 일 오후 1:50……

아직 씻지도 못했는데…… 아아아아앙ㅁ ;ㅣ나얼

어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스 제공

서브시스템을 더 쉽게 사용할 수 있다

디자인 원칙

최소 지식 원칙 – 정말 친한 친구하고만 얘기하라

객체간 상호 관계에서 서로 서로는서로에 대해 아는 것이 적을 수록 좋다

영화를 봅시다

팝콘 기계 ON();

팝콘 튀기기 ON();

전등 OFF();

프로젝터 ON();

앰프 ON();

DVD 플레이어 ON();

에이 복잡해… 그냥…

MovieFacade팝콘기계

DVD 플레이어

앰프

스 크 린DVD 플레이어

조 명 프 로 젝 터

하나로 통합

MovieFacade

Void WatchMovie(){팝콘 . 기계 ON();팝콘 . 튀기기 ON();전등 . OFF();프로젝터 . ON();앰프 . ON();DVD 플레이어 . ON();

}

클라이언트는 MovieFacade 객체를 만들고WatchMovie() 를 호출하는 것 하나로

적절한 조명에서 빵빵한 음향으로맛있는 팝콘을 먹으며 영화를 보는 것을

서브시스템에 대한 신경을 쓸 필요없이즐길 수 있습니다

마지막 의견 교환의 시간

감 사 합 니 다