Design pattern/행동 패턴

[디자인 패턴] 17장 체인 패턴

미스터로즈 2021. 5. 22. 09:26

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

 

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

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

 

체인 패턴은 객체 메시지의 송신과 수신을 분리해서 처리합니다.

다음 세가지 내용을 중심으로 살펴봅니다.

- 상태값과 처리 방식에 대해 알아봅니다.

- 상태값과 복수의 행동 객체를 연결하는 방법에 대해 알아봅니다.

- 객체를 연결하는 방법에 대해 알아봅니다.

 

제어문

프로그램은 순차적 절차에 따라 코드를 실행합니다. 그중 제어문은 상태값을 비교하여 코드의 실행 흐름을 변경합니다.

 

개발 언어는 코드의 동작을 제어할 수 있는 조건문을 지원합니다. 대표적으로 if문과 switch문이 있습니다.

 

<?php

$conf = true;

if ($conf) {
    // 조건이 참인 경우 코드 분기
    ordered();
} else {
    // 조건이 거짓인 경우 코드 분기
    cancel();
}

function ordered()
{
    echo "성공적으로 주문이 접수 되었습니다.";
}

function cancel()
{
    echo "주문을 취소합니다.";
}

조건문은 순차적으로 실행되는 코드를 상태값에 의해 선택적으로 제어합니다. 선택적으로 실행을 제어한다는 의미는 상태에 따라 동작을 분리한다는 것과 같습니다.

 

동작 조건

제어문은 상태값에 따라 동작을 분리하며, 상태값은 동작을 분리하는 조건의 기준값입니다.

 

순차적으로 실행되는 코드의 동작을 분기하기 위해 조건문과 상태값을 사용합니다. 조건문을 처리하려면 상태값이 반드시 필요한데 상태값은 대부분 내부 동작에 의해 발생하는 경우가 많으며 외부로부터 조건의 상태값을 전달 받기도 합니다.

 

조건의 상태값이 발생하는 방법에 따라 처리 동작이 두 가지로 나뉩니다.

- 외부 하드웨어로부터 발생한 조건을 처리하는 인터럽트

- 내부의 상태값을 처리하는 이벤트

 

인터럽트는 외부적인 하드웨어 신호를 입력 받아 상태를 처리합니다.

 

인터럽트가 발생하면 중앙처리 장치는 프로그램 실행을 잠시 중단하고 실행 제어권을 지정한 코드의 위치로 이동시킵니다.

인터럽트 동작이 완료되면 다시 이전의 실행 위치로 제어권을 되돌리고 다음 코드줄을 실행합니다.

 

인터럽트의 특징은 하드웨어적으로 프로그램 실행을 방해하여 외부로부터 입력 받은 조건 동작을 최우선적으로 처리합니다. 따라서 인터럽트는 하드웨어적으로 구성되어 있으며 개수와 동작 방식은 제한적입니다.

 

이벤트는 하드웨어적인 인터럽트와 달리 프로그램에 의해 동작을 분리하여 실행합니다. 이벤트는 내부의 상태값을 지속적으로 감시하고, 특정 상태값이 됐을 때 지정한 동작을 실행합니다.

 

 

이벤트는 사전에 정의된 상태값에 따라서 분리된 동작을 실행하며 상태값은 상수로 지정하여 사용합니다.

 

상태값에 대한 이벤트 처리 동작은 별도의 처리 로직으로 분리할 수 있습니다. 이렇게 분리된 로직을 디스패치라고 하며, 이벤트의 처리를 핸들러라고 하기도 합니다.

 

핸들러는 분리된 이벤트를 처리하는 로직입니다, 핸들러는 이벤트의 상태값을 지속적으로 감시하기 위해 반복 루프 구조로 되어 있는 경우가 많습니다.

 

핸들러는 크게 정적과 동적 동작으로 구분합니다. 정적 핸들러는 코드에서 미리 결정된 동작을 실행하며, 동적 핸들러는 실행 중에 결정되는 동작을 실행합니다.

<?php
/**
 * 클래스 선언
 */
class Handler
{
    public function event($message)
    {
        if($message == "action01") {
            return "버튼동작 01 입니다.";
        } else 
        if ($message == "action02") {
            return "버튼동작 02 입니다.";
        } else 
        if ($message == "action03") {
            return "버튼동작 03 입니다.";
        }
    
        return "동작이 없습니다.";
    }
}

// 객체 생성
$obj = new Handler;

// 이벤트를 실행
echo $obj->event("action02");

Handler 클래스는 Event 메서드를 갖고 있으며, 입력된 상태값에 따라 다른 동작을 수행합니다.

 

이벤트를 기반으로 동작을 분리하는 방식을 이벤트 주도 개발이라고 합니다. 이벤트는 멀티태스킹, 대화형 프로그래밍 개발 시 많이 사용되는 방식입니다.

 

행동 분리

핸들러는 이벤트의 처리 로직을 분리합니다. 이벤트 처리 로직을 분리하면 행동을 독립적으로 관리할 수 있습니다.

 

단일 책임은 객체지향 설계 원칙 중 하나입니다. 객체는 하나의 책임과 동작을 가지며, 잘못된 설계 객체는 하나의 객체에 여러 가지 행동과 책임이 포함됩니다.

 

핸들러는 분리된 이벤트의 처리 로직으로서 다양한 동작을 선택적으로 처리합니다. 이러한 점에서 핸들러 객체는 객체지향의 단일 책임 원칙이 위반되고, 체인 패턴은 단일 책임 원칙을 위반한 핸들러 객체를 보완합니다.

 

<?php
class Order
{
    public function execute()
    {
        return "주문을 처리합니다.";
    }
}
<?php
class Cancel
{
    public function execute()
    {
        return "취소 처리합니다.";
    }
}
<?php

require_once "Order.php";
require_once "Cancel.php";

class Handler
{
    public function event($message)
    {
        if($message == "order") {
            return (new Order)->execute();
        } else 
        if ($message == "cancel") {
            return (new Cancel)->execute();
        } 
    
        return "동작이 없습니다.";
    }
}

// 객체 생성
$obj = new Handler;

// 이벤트를 실행
echo $obj->event("order");

 

사슬 연결

이벤트 핸들러의 처리 동작을 단일 사슬 형태로 변경합니다. 사슬 연결을 통해 하나의 상태값에 따라 복수의 행동을 실행할 수 있습니다.

 

핸들러는 발생한 이벤트에 대해 조건을 검사합니다. 이벤트 조건은 작성한 코드 순서대로 검사가 이뤄집니다.

 

핸들러 안에는 여전히 조건을 판단하는 로직이 존재합니다. 핸들러의 조건을 판단하는 수신부를 분리하여 개선하겠습니다.

 

<?php

require_once "Order.php";
require_once "Cancel.php";

class Handler
{
    public function event($event)
    {
        if ( $message = (new Order)->execute($event) ) {
            return $message;
        }

        if ( $message = (new Cancel)->execute($event) ) {
            return $message;
        } 
    
        return "동작이 없습니다.";
    }
}

// 객체 생성
$obj = new Handler;

// 이벤트를 실행
echo $obj->event("cancel");

 

체인 패턴으로 동작 객체를 변경해봅니다. 추상 클래스를 상속함으로써 동작 객체가 복합 객체로 변경됩니다.

 

<?php

abstract class Chain
{
    protected $Next;
    public function setNext($obj)
    {
        $this->Next = $obj;
    }

    abstract function execute($event);
}
<?php
class Order extends Chain
{
    public function execute($event)
    {
        if ($event == "order") {
            return "주문을 처리합니다.";
        }

        return $this->Next->execute($event);
    }
}
<?php
class Order extends Chain
{
    public function execute($event)
    {
        if ($event == "order") {
            return "주문을 처리합니다.";
        }

        return $this->Next->execute($event);
    }
}
<?php

require_once "Chain.php";
require_once "Order.php";
require_once "Cancel.php";

class Handler
{
    public function event($event)
    {
        // 체인 설정
        $First = new Order;
        $First->setNext(new Cancel);

        return $First->execute($event);
    }
}

// 객체 생성
$obj = new Handler;

// 이벤트를 실행
echo $obj->event("cancel");

핸들러에 포함됏던 기존 조건문이 제거됐습니다. 사슬로 연결된 객체의 첫번째 객체만 실행합니다

 

체인 패턴

체인 패턴은 조건을 판별하는 코드를 포함하지 않습니다. 메시지를 전달 받으면 상태값이 만족할 때까지 복수의 이벤트를 처리합니다.

 

체인 패턴은 하나의 상태값에 여러 객체를 묶어 실행하는 방법입니다. 묶은 모양이 마치 체인의 연결 고리 같다고 해서 체인 패턴이라고 부릅니다.

 

체인 패턴은 처리로직을 요청하는 송신부와 처리를 검사하는 수신부를 분리하는 효과가 있습니다.