Design patterns 스터디 -strategy패턴

Preview:

Citation preview

Design Patterns Study

1. Strategy pattern

본 PPT는 Design Patterns Study를 위해 작성되었습니다.

Study에 사용한 교재는 한빛미디어에서 나온 Head First Design Patterns을 사용하였습니다.

Written by NHN NEXT 2기 조현호 For study with NHN NEXT 2기 조현호 서울시립대 이희태

공부를 위해 간단한 시나리오를 생각해봅시다.

오리(Duck)을 시뮬레이트 하는 게임을 개발하기 위해 오리들을 만들게 되었다고

한 번 가정을 하고 시작을 해봅시다.

객체 지향의 꽃은 상속이라는 말을 많이들 하던데 상속으로 만들면 되겠지?

quack() swim() display()

display()display()

Duck

MallardDuck RedheadDuck

Inheritance

quack은 꽥하고 오리가 소리를 내도록 하는 메소드입니다. 그리고 swim은 오리가 수영을 하도록 하는 메소드죠.

quack() swim() display()

display()display()

Duck

MallardDuck RedheadDuck

Inheritance

고무로 만든 RubberDuck을 추가하려는데 Duck을 상속받아 사용한다면??

꽥… 소리도 못내고…

고무오리는 꽥 소리를 못내죠… 그러면 RubberDuck에는 quack() 메소드를 Overiding해줘야 겠군요…

quack() swim() display()

display()display()

Duck

MallardDuck RedheadDuck

Inheritance

display() quack() //쉿 //적당한 코드 구현

RubberDuck

그런데 고민이 생겼습니다. 세상에는 오리들이 너무도 많은데…

가령 꽥꽥 우는 오리도 있고..

우웩~ 우는 오리도 있고…

그러면 우리는 매번 오리가 우는 소리가 다를 때 마다 Overriding을 해줘야만 하는걸까요…

객체지향 프로그래밍에서는 Interface 많이 쓴다더만?

Interface 요녀석은 어떠려나??

//quack() 삭제 swim() display()

display() fly() quack()

display() fly() quack()

Duck

MallardDuck RedheadDuck

Inheritance

display() fly() quack()

RubberDuck

fly() quack()

Flyable Quackable

그럼 나는 방식을 조금 수정하고 싶으면?? 하나하나 다 수정하라고?? ㅡㅡ;;

이쯤되면 백마탄 왕자가 구해주러 와야하는데…

첫번째 디자인 원칙.

애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리시킨다.

그러면 바뀌는 부분과 바뀌지 않는 부분을 가정해봅시다.

바뀌지 않는 것 : swim

바뀌는 것 : quack, fly

그리고 바뀌는 부분을 분리합니다.

이렇게 말이죠.

fly() quack()

FlyBehavior QuackBehavior

fly(){ //적절히 //구현 }

FlyWithWings

fly(){ //적절히 //구현 }

FlyNoWay

quack(){ //적절히 //구현 }

Quack

quack(){ //적절히 //구현 }

Mute

fly() quack()

FlyBehavior QuackBehavior

fly(){ //적절히 //구현 }

FlyWithWings

fly(){ //적절히 //구현 }

FlyNoWay

quack(){ //적절히 //구현 }

Quack

quack(){ //적절히 //구현 }

Mute

그리고 fly방법이 이후에 추가되거나 새로운 목소리를 내는 오리가 별견된다면!

그저 우리는 FlyBehavior, QuackBehavior를 구현하는 클래스를 추가하면 됩니다.

두번째 디자인 원칙.

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

즉, 상위 형식에 맞춰서 프로그래밍 한다.

처음에 보았던 상속의 약점 기억나시나요?

fly방식이 다른 오리를 추가하기 위해서는 overridng을 일일이 해줘야했죠.

이제는 새로운 오리를 생성할 때, implements FlyaBehavior, QuackBehavior를 하고 FlyBehavior, QuackBehavior 인터페이스 변수에

그 오리에 맞는 fly, quack방식을 찾아 인터페이스 변수에 객체를 생성만 해주면 됩니다.

즉, 상위 형식의 변수에 하위 클래스의 인스턴스를 담아주는겁니다.

이렇게 하면 fly, quack방식에 관한 코드들을 모두 재사용 할 수 있으므로

인터페이스의 약점도 해결이 되는군요!

세번째 디자인 원칙.

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

우리가 변화하는 부분과 변화하지 않는 부분을 분리했죠?

그럼 오리에게 fly, quack행동을 부여하기위해서는 상속이나 인터페이스를 이용해야할텐데,

이때 인터페이스를 사용해 오리에게 필요한 부분들을 하나씩 하나씩 가져와 조립하는걸 ‘구성(composition)’ 이라고 합니다.

자바는 단일상속만을 지원하기 때문에, 이렇게 ‘구성’을 이용하면 오리에게 필요한 다양한 행동을 추가할 수 있어서

유연성이 대폭 증가하겠죠!

에잉.. 그게 뭔 말이야???

잘 이해가 안되네??

그럼 이제 직접 코드를 보겠습니다.

이렇게 fly 행동에 관한 것을 떼어냅니다.

이런걸 ‘캡슐화’ 라고 하지요.

이렇게 하면 fly에 관한 새로운 행동이

추가되면

마찬가지로 FlyBehavior를 구현하여 만들어주면 되겠죠?

서브 클래스에서는 요 변수에적절한 fly, quack행동에 맞는 인스턴스를 생성해줍니다

main 함수에서는 오리의 종류에 신경 쓸 필요 없이 그저 performQuack, performFly만 호출하면 그 오리에 알맞는 quack() 메소드가 호출이 됩니다.

적절한 인스턴스를 생성해 담아 줍니다.

이제 한 번 실행 시켜 봅시다.

잘되네 ㅎㅎ

날지 못하는 RubberDuck이 너무 불쌍하네요.

로켓엔진을 한 번 달아줘 볼까요?

흠.. 그런데 날지 못하던 러버덕에게 어떻게 갑자기 로켓엔진을 달아주죠??

일단 Duck 클래스에 setter를 만듭니다.

로켓 추진은 fly방법 중 하나니깐 FlyBehavior를 구현하는

FlyRocketPowered라는 클래스를 만들어줍시다.

앞에서 만든 setter에 FlyRocketPowered 객체를 파라미터로 보내줍니다.

확인 해볼까요~

잘 되는군요!!

아하, 오늘 공부한 방법을 사용하면,

fly 방법에 대한 알고리즘과 quack 방법에 대한 알고리즘

즉, 바뀌는 부분에 대한 알고리즘은 따로 떼어내 관리함으로써

Duck에서는 그 전략을 신경 쓰지 않아도 그저 performFly, performQuack으로

사용만 하면 되는군요!!

게다가 그 전략들은 다른 코드에서도 얼마든지 재사용 할 수 있겠군요!!

가령, 오리가 아니라 오리소리가 나는 알람을 만든다고해도 QuackBehavior를 사용한다면

얼마든지 코드를 재사용 할 수 있을테죠.

여러분은 지금 Strategy pattern을 모두 공부하셨습니다.

다음시간엔 데코레이터 패턴을 공부하도록 합시다~

감사합니다!