Design pattern/행동 패턴

[디자인패턴]18장 감시자 패턴

미스터로즈 2021. 6. 14. 19:53

공부하기 위해서 요약정리해놓은 것입니다..

 

정확하고 꼼꼼한 자료는 쉽게 배워 바로 써먹는 디자인 패턴을 확인하시고

코드는 github.com/infohojin/patterns 을 참고해 주세요.

 

감시자 패턴은 모던 언어에서 많이 응용되는 대표적인 패턴입니다. 몇몇 프로그래밍 언어는 자체적으로 감시자 패턴의 기본 구현체를 만들어 제공하기도 합니다.

 

감시자

프로그램 동작은 하나의 로직을 실행한 후 다음 로직을 실행하는 것처럼 순차적으로 이뤄집니다.

 

코드의 로직은 독립적이고 자체적인 동작입니다. 또는 외부의 값에 따라 실행되는 동작을 다르게 할 수도 있습니다. 코드는 값을 확인하는 동작이 필요합니다.

 

다음은 반복문과 조건문을 이용한 감시코드입니다.

 

<?php

// 상태값
$status = false;

while (1) {
    // 상태값 모니터
    if($status) {
        hello();
        // 상태값이 `참`인경우 탈출
        break;
    }
}

function hello()
{
    echo "안녕하세요.";
}

상태값을 지속적으로 모니터링하기 위해 무한 루프를 사용했습니다. 무한 루프는 지속적인 상태값을 모니터링할 수 있지만, 관찰하는 도중에 다른 동작을 처리할 수는 없습니다.

마치 프로그램이 어떤 상태를 대기하고 있는 것과 같습니다.

이처럼 상태값에 따라 동작을 처리하기 위해 지속적으로 관찰하는 것은 비효율적입니다.

 

프로그램 코드가 직접 상태값을 관찬하는 것이 아니라 값에 변화가 있을 때 이를 알리고 처리를 수행하면 상태를 기다리는 동안 다른 일을 처리할 수 있어 더욱 효율적입니다.

이처럼 직접 상태값을 관찰하는 것이 아니라 수동적으로 상태값을 전달 받아 처리하는 패턴을 감시자패턴이라고 합니다.

 

실제 동작하는 객체는 주체의 상태값을 직접 관찰하지 않고도 상태 변화를 알 수 있습니다. 감시자 패턴은 상태를 감시하는 행동과 실제 동작을 처리하는 행동을 분리해서 구현합니다.

 

할리우드 법칙는 영화 산업에서 이뤄지는 관련 경험을 객체지향 설계에 도입한 것이 할리우드 원칙이며, 감시자 패턴은 할리우드 원칙을 적용한 패턴 구현입니다.

 

구성

감시자 패턴은 4개의 클래스로 구성됩니다.

 

- 주체

- 실제 주체

- 감시자

- 실체 객체

 

이는 다시 2개의 그룹으로 구분할 수 있습니다.

- 통보를 위한 주체 - 실제 주체 클래스

- 처리를 위한 감시자 - 실체 객체 클래스

 

주체 - 실제 주체는 감시자 패턴에서 객체의 등록, 삭제, 통보를 담당하는 클래스입니다. 주체 클래스는 실제 처리하는 객체를 관리하고 관리를 담당하는 주체는 1개 이상의 감시자 객체를 갖고 있습니다.

 

감시자 - 실체 객체는 통보를 수신 받아 처리하는 객체입니다. 통보를 받으려면 주체 클래스에 수신 받는 객체를 등록해야 합니다.

 

관계

감시자 패턴은 구성 클래스 간에 관계를 형성하는데, 이는 주체와 감시자들 간의 관계를 말합니다.

 

객체가 관계를 가진다는 것은 의존성의 의미합니다.

메인 구성 요소인 주체는 처리해야 하는 복수의 서브 객체를 갖고 있습니다. 객체 - 감시자가 하나의 커다란 객체 덩어리고, 이러한 객체 덩어리를 복합 구조 객체라고 합니다.

 

감시자 패턴은 주체와 감시자 간 의존관계를 갖고 있습니다. 이러한 관계 형태의 패턴을 종속자 패턴이라고도 합니다.

 

단방향성을 가진 감시자 패턴을 게시 - 구독 패턴

어떤 UI 프레임워크에서는 감시자 패턴을 리스너 패턴이라고 부르기도 합니다.

 

시스템을 하나의 공통된 클래스 집합으로 처리할 때 객체 간 일관성이 유지돼야 하지만, 일관성 때문에 객체 간 결합도가 높아져서는 안된다.

 

주체

먼저 감시자 패턴을 구현하기 위해 주체를 설계해야 합니다. 주체는 인터페이스와 실제 주체로 이뤄집니다.


주체 인터페이스 - 주체를 설계하기 위한 공통 인터페이스

<?php
/**
 * 주체의 인터페이스를 선언합니다.
 */
interface Subject
{
    public function addObserver(Observer $o);
    public function delelteObserver(Observer $o);
    public function notiObserver();
}

실제 주체 - 인터페이스를 적용한 하위 클래스

 

<?php
/**
 * 실제적 주체구현
 */
class Members implements Subject
{
    // 옵저버 보관
    private $Objs = [];

    public function __construct()
    {
        echo __CLASS__." 실제 주체(concreteSubject)를 생성합니다.\n";
    }

    /**
     * 옵저버를 등록합니다.
     */
    public function addObserver(Observer $o)
    {
        echo "옵저버 객체를 추가합니다.\n";
        array_push($this->Objs, $o);
    }

    /**
     * 옵저버를 제거합니다.
     */
    public function delelteObserver(Observer $o)
    {
        for ($i=0; $i<count($this->Objs); $i++) {
            if ($this->Objs[$i]->name == $o->name) {
                unset($this->Objs[$i]);
            }
        }
    }
    
    /**
     * 모든 옵저버에게 통보를 합니다.
     */
    public function notiObserver()
    {
        foreach ($this->Objs as $obj) {
            echo "옵저버=> ";
            $obj->update();
        }
    }
}