본문 바로가기
객체지향

[Design pattern] observer pattern(옵저버 패턴)

by clean_h 2021. 5. 1.
728x90

이번 시간에는 Design pattern의 종류 중 하나인 observer pattern을 공부해본다. 

 

정의 : 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.

위키피디아 설명

 

observer pattern의 목적은 상태를 표시하는 display와 개체 자체를 분리하는 것이다.

또한, observer pattern은 같은 데이터를 다양한 display로 나타낼 수 있고, object(객체)가 변경되면, 모든 display를 자동으로 업데이트시켜줄 수 있다. display는 나중에 추가될 수 도 있다.

display는 막대형, 꺽은선형, 원형, 분산형, 지도형, 히스토그램 등 다양하게 display 할 수 있다.

 

장점

  • 실시간으로 한 객체의 변경사항을 다른 객체에 전파할 수 있다. 
  • 상태를 표시하는 display와 객체 자체를 분리할 수 있다. 
  • 객체간의 의존성을 제거할 수 있다.
  • 알아서 타입에 맞게 display를 나타낼 수 있다. 

단점

  • 속도의 최적화가 어렵다. 상태 관리가 힘들다.
  • 모든 observer를 업데이트하기 때문에 필요 없는 observer도 업데이트하게 된다.

 

A UML model of the Observer pattern

UML 다이어그램으로는 다음과 같이 표현된다. 화살표는 상속관계를 나타내 준다. 

  • Subject: 모든 observer를 추적하고 observer를 추가(Attach)하거나 제거(Detach)할 수 있다. 또한 데이터가 변경되면 observer를 업데이트(Notify)할 수 있다. 
  • ConcreteSubject: 이 클래스는 subject를 구현하는 실제 클래스이다. 실제 데이터를 가져오고(GetState) 넘겨주며 Subject에 업데이트(Notify)를 알려준다. 
  • Observer: 변경이 있을 때마다 호출되어야하는 메소드를 정의하는 인터페이스를 나타낸다. 
  • ConcreteObserver: 변경 사항에 따라 자체적으로 업데이트해야 하는 클래스이다. observer를 구현하고 concretesubject에 등록하면 업데이트를 수신하도록 한다. 

 

C++ 구현

코드구현은 다음과 같이 할 수 있다.

 

  • Subject

//Header File
#pragma once
#include <vector>
#include <list>
#include "shop.h"

class ASubject
{
    //Lets keep a track of all the shops we have observing
    std::vector<Shop*> list;

public:
    void Attach(Shop *product);
    void Detach(Shop *product);
    void Notify(float price); 
};

//CPP File
#include "ASubject.h"
#include <algorithm>

using namespace std;

void ASubject::Attach(Shop *shop)
{
    list.push_back(shop);
}
void ASubject::Detach(Shop *shop)
{    
    list.erase(std::remove(list.begin(), list.end(), shop), list.end());    
}

void ASubject::Notify(float price)
{
    for(vector<Shop*>::const_iterator iter = list.begin(); iter != list.end(); ++iter)
    {
        if(*iter != 0)
        {
            (*iter)->Update(price);
        }
    }
}

 

  • concreteSubject

//Header File
#pragma once
#include "ASubject.h"

class DummyProduct : public ASubject
{
public:
    void ChangePrice(float price);
};

//CPP File
#include "DummyProduct.h"

void DummyProduct::ChangePrice(float price)
{
    Notify(price);
}

 

  • Observer

#pragma once

class IObserver
{
public:
    virtual void Update(float price) = 0;
};

 

  • Concrete Observer

//Header File
#pragma once
#include <iostream>
#include <string>
#include "IObserver.h"

class Shop : IObserver
{
    //Name of the Shop
    std::string name;
    float price;
public:
    Shop(std::string n); 
    void Update(float price);          
};

//CPP File
#include "Shop.h"

Shop::Shop(std::string name)
{
    this->name = name;
}

void Shop::Update(float price)
{
    this->price = price;

    //Lets print on console just to test the working
    std::cout << "Price at "<< name << " is now "<< price << "\n";
}

 

  • testcode

int main(int argc, char* argv[])
{
    DummyProduct product;
                    
    // We have four shops wanting to keep updated price set by product owner
    Shop shop1("Shop 1");
    Shop shop2("Shop 2");

    product.Attach(&shop1);
    product.Attach(&shop2);

    //Now lets try changing the products price, this should update the shops automatically
    product.ChangePrice(23.0f);

    //Now shop2 is not interested in new prices so they unsubscribe
    product.Detach(&shop2);            

    //Now lets try changing the products price again
    product.ChangePrice(26.0f);

    getchar();
    return 0;
}

 

위키피디아 - ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4

코드 - www.codeproject.com/Articles/328365/Understanding-and-Implementing-Observer-Pattern-2

728x90

댓글