Design patterns observerpattern

Preview:

Citation preview

Design Patterns2. Observer Pattern

옵저버패턴의정의

• 옵저버패턴(Observer Pattern) 에서는한객체의상태

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

기상정보스테이션구축프로젝트를시작하게되었음.

WeatherData라는객체를지원받음여기엔기온습도기압을추적하고있음이객체를통해화면을보여주는애플리케이션을만들어야함.

온도센서

습도센서

압력센서

기상스테이션WeatherData

객체디스플레이장비

데이터취득 화면에표시

WeatherData Class

WeatherData

getTemperatur()

getHumidity()

getPressure()

measurementsChanged()

//기타메소드

기상관측값이갱신될때마다알려주기위한메소드

measurementsChanged()를현재조건, 기상통계, 기상예측세가지디스플레이를갱실할수있도록구현해야함

지금까지알고있는것들

WeatherData 클레스에는세가지측정값을알아내기위한게터메소드가있다.

새로운기상측정데이터가나올때마다 measurementsChanged()메소드가호출됨.

기상데이터를사용하는세개의디스플레이항목을구현해야함(WeatherData에서새로운측정값이들어올때마다디스플레이를갱신해야함.)

시스템이확장가능해야함. 다른사람들이별도의디스플레이항목을만들수있도록해야하고사용자들이애플리케이션에마음대로디스플레이항목을추가/제거할수있도록해야함.

getTemperature()

getHumidity()

getPressure()

measurementsChanged()

기상통계

현재조건

기상예보

추가항목

추가항목

코드를대충만들어봤음

디스플레이갱신

WeatherData의게터메소드를써서최신측정값을가져옴

각디스플레이항목을불러서디스플레이를갱신하도록함.

문제점

바뀔수있는부분.

캡슐화해야함.

구체적인구현에맞춰서코딩했기때문에프로그램을고치지않고는다른디스플레이항목을추가/제거할수없슴.

옵저버패턴?

1. 신문사가사업을시작하고신문을찍어냄

2. 독자가특정신문사에구독신청을하면새신문을매번받음

3. 신문을더이상보고싶지않으면해지신청을하면됨

4. 신문사는계속해서구독및해지를하게됨

출판사 + 구독자 = 옵저버패턴• 출판사를주제(subject), 구독자를옵저버(observer)라고부른다는것으로함.

주제객체

Dog 객체

Cat

객체

Mouse

객체

주제객체에서일부데이터를관리

주제의데이터가달라지면옵저버한테그소식이전해집니다.

주제의데이터가달라지면옵저버한테그소식이전해집니다.

옵저버객체

옵저버객체들은주제객체를구독하고있으며(주제객체에등록되어있으면)

주제의데이터가바뀌면갱신내용을전달받음.

Duck

객체

이객체는옵저버가아니기때문에주제객체의데이터가바뀌어도아무런연락을받을수없음.

옵저버패턴클래스다이어그램

<<인터페이스>>

SubjectregisterObserver(

)

removeObserver(

)

notifyObserver()

<<인터페이스>>

Observer

update()

주제를나타내는 Subject인터페이스.

객체에서옵저버로등록, 탈퇴시에인터페이스메소드사용

옵저버

각주제마다여러개의옵저버가있을수있음

옵저버가될가능성이있는객체에서는반드시 Observer인터페이스를구현해야함이인터페이스에는주제의상태가바뀌었을때호출되는 update() 메소드밖에없음.

SubjectregisterObserver(

)

removeObserver(

)

notifyObserver()

getState()

setState()

주제역할을하는구상클래스에서는항상 Subject인터페이스를구현해야함주제클래스에서는등록및해지를위한메소드외에상태가바뀔때마다모든업저버들에게연락을하기위한notifyObserver()메소드도구현해야함

주제클래스에는상태를설정하고알아내기위한게터/세터메소드가있을수도있음

Observerupdate()

//기타옵저버용메소드

Observer인터페이스만구현한다면무엇이든옵저버클래스가될수있다.

각옵저버는특정주제객체에등록을해서연락을받을수가있다.

주제

도움되는내용

Q: 클래스다이어그램

하고일대다의관계?

A: 옵저버패턴에서상태를저장하고지배하는것은

주제객체임. 따라서상태가들어있는객체는하나만있을수있다.

하지만옵저버는사용하긴하지만반드시상태를가지고있어야하는것은아님. 따라서옵저버는여러개가있을수있으며, 주제객체에서상태가바뀌었다는것을알려주기를기다리는, 주제에의존적인성질을가지게됨. 하나의주제와여러개의옵저버가연관된일대다(one-to-many) 관계가성립된다.

Q: 이내용이랑의존성이랑무슨상관?

A: 데이터의주인은주제이다. 옵저버는데이터가변경되었을때

주제에서갱신해주기를기다리는입장이기에의존성을가진다고할수있다.

이런방법을사용하면여러객체에서동일한데이터를제어하도록하는것에비해더깔끔한객체지향디자인을만들수있다.

느슨한결합(Loose Coupling)의위력• 주제가옵저버에대해서아는것은옵저버가특정인터페이스(Observer 인터페이스)를구현한다는것뿐이다.

• 옵저버는언제든지새로추가할수있다.

• 새로운형식의옵저버를추가하려고할때도주제를전혀변경할필요가없다.

• 주제와옵저버는서로독립적으로재사용할수있다.

• 주제나옵저버가바뀌더라도서로한테영향을미치지는않는다.

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

객체사이의상호의존성을최소화할수있기에느슨하게결합하는디자인을사용하면변경사항이생겨도무난히처리할수있는유연한객체지향시스템을구축할수있다.

기상스테이션설계

<<인터페이스>>

SubjectregisterObserver(

)

removeObserver(

)

notifyObserver()

<<인터페이스>>

Observerupdate()

<<인터페이스>>

DisplayElementdisplay()

WeatherData

registerObserver()

removeObserver()

notifyObserver()

getTemperature()

getHumidity()

getPressure()

measurementsChange

d()

CurrentCondition

s

update()

display(){

// 구현}

StatisticsDisplay

update()

display(){

// 구현}

ThirdPartDisplay

update()

display(){

// 구현}

ForecastDisplay

update()

display(){

// 구현}

옵저버

주제

모든기상구성요소에서 Observer인터페이스를 구현.

이인터페이스는주제객체에서옵저버한테갱신된정보를전달할수있는방법을제공

모든디스플레이항목에서구현하는인터페이스를하나더만들어서디스플레이항목에서는 display()

메소드만구현하면된다.

가상스테이션구현

이메소드는 Observer를인자로받아서각각옵저버를등록하고제거하는역할을함.

주제객체의상태가변경되었을때모든옵저버들한테알리기위해호출되는메소드

Observer인터페이스는모든옵저버클래스에서구현해야함.

디스플레이항목을화면에표시해야하는경우에이메소드를호출하면됨.

WeatherData에서 Subject인터페이스구현

디스플레이항목

실행

자바내장옵저버패턴자바 Observable클래스와 Observer인터페이스가존재함.

이는앞서본 Subject클래스와 Observer인터페이스와비슷함.

차이점은 WeatherData(주제객체)에서 Observable클래스를확장하면서 addObserver, deleteObserver, notifyObserver

같은메소드를상속받음.

자바내장옵저버패턴작동방식(1)

객체가옵저버가되는방법Observer인터페이스를구현하고 Observable객체의 addObserver() 메소드호출옵저버목록에서탈퇴하고싶을때는 deleteObserver() 메소드호출.

Observable에서연락을돌리는방법java.util.Observable수퍼클래스를확장하여 Observable 클래스를만들어야함.

그다음 setChanged()메소드를호출해서객체의상태가바뀌었다는것을알려줌그다음은다음중하나의메소드를호출해야함.

notifyObservers() or notifyObservers(Object arg)

옵저버객체한테전달함임의의데이터객체를받아들임

자바내장옵저버패턴작동방식(2)

옵저버가연락을받는방법update()메소드를구현하지만메소드서명이조금다름

update(Observable o, Object arg)

연락을보내는주제객체가인자로전달

notifyObservers()메소드에서인자로전달된데이터객체.

데이터객체가지정되지않은경우는 null

데이터를옵저버들한테보낼때는 (push방식)notifyObservers(arg)메소드의인자로전달하는데이터객체형대로전달.

아니면옵저버쪽에서전달받은 Observable객체로부터원하는데이터를가져가는방식(pull방식)을써야됨.

자바내장기능을활용한기상스테이션구현코드(pull방식)

자바내장기능을활용한기상스테이션구현코드(pull방식)

유의사항

옵저버한테연락이가는순서에의존하면절대안됨.

java.util.Observable에서 notifyObservers() 메소드를구현할때Observer객체들한테연락을돌리는순서가직접구현했던것과다르다.

어느쪽이올바르다고할수는없지만특정연락순서에의존하도록코드를만들었다면그방법이잘못된것임. 특정연락순서에의존하도록만들었다면 Observable/Observer구현을변경했을때, 연락순서가바뀌면다른결과가나올수있다. 순서가바뀐다고다른결과가나온다면“느슨한결합”이라고할수없다.

java.util.Observable의단점

• Observable은클래스- Observable이클래스가때문에서브클래스를만들어야한다는점이문제. 이미다른수퍼클래스를확장하고있는클래스에 Observable의기능을추가할수가없음.(재사용성에제약이생김.)

- Observable인터페이스라는것이없기때문에자바에내장된 Observer API하고잘맞는클래스를직접구현하는것이불가능. java.util구현을다른구현으로바꾸는것도불가능.(예로멀티스레드로구현한다거나하는일이불가능)

• Observable클래스의핵심메소드를외부에서호출할수없다.- 내부를살펴보면 setChanged()메소드가 protected로선언되어있다. Observable의서브클래스에서만 setChanged()를호출할수있다. 결국직접어떤클래스를만들고 Observable의서브클래스를인스턴스변수로사용하는방법도써먹을수가없다. 이런디자인은상속보다는구성을사용한다는디자인원칙에도위배됨.

java.util.Observable을확장한클래스를쓸수있는상황이라면 Observable

API를쓰는것도괜찮을것이다. 허나앞에서했던것처럼직접구현해야할수도있다. 둘중어느방법을쓰든옵저버패턴만제대로알고있으면그패턴을활용하는 API는어떤것이든잘활용할수있다.