글 / 시스템 연계

어댑터 패턴과 장비 연동

장비별로 다른 프로토콜은 어댑터 내부에 두고, 상위 코드는 공통 인터페이스와 공통 상태 모델만 참조한다.

작성일 · 읽는 시간 2분

어댑터의 필요성

장비에는 레지스터 값을 읽어야 하는 장비와 메시지를 구독해야 하는 장비가 있다. 이 차이가 작업 배정과 같은 상위 코드까지 노출되면 장비 종류마다 분기문이 생긴다.

공통 인터페이스와 공통 상태 모델

어댑터 패턴은 서로 다른 인터페이스를 하나의 공통 인터페이스로 감싸는 패턴이다. 장비가 제공하는 값도 공통 상태 모델로 변환하여 반환한다.

공통 인터페이스 DeviceAdapter 아래 장비별 어댑터 구조

pythonadapter.py
from dataclasses import dataclass
from typing import Protocol


@dataclass(frozen=True)
class Status:
    device_id: str
    state: str  # idle, busy, error
    battery: float | None = None


class DeviceAdapter(Protocol):
    def read_status(self) -> Status: ...
    def send_job(self, target: int) -> None: ...

신규 장비의 어댑터 추가

새 장비가 도입되면 그 장비의 어댑터만 추가한다. 프로토콜 코드와 장비 고유의 상태 값은 어댑터 외부로 노출되지 않는다.

pythonregister_adapter.py
from adapter import Status


class RegisterAdapter:
    STATES = {0: "idle", 1: "busy", 2: "error"}

    def __init__(self, device_id: str, client):
        self.device_id = device_id
        self.client = client

    def read_status(self) -> Status:
        # 레지스터 값을 공통 상태로 옮긴다
        code = self.client.read(100)
        return Status(self.device_id, self.STATES.get(code, "error"))

    def send_job(self, target: int) -> None:
        self.client.write(200, target)

레지스터 값이 공통 상태 모델 Status로 변환되는 대응

Protocol과 ABC

  • typing.Protocol: 메서드 시그니처가 일치하면 상속 없이도 그 타입으로 간주한다. 누락된 메서드는 타입 검사기가 검출한다.
  • abc.ABC: 상속이 필요하며, 추상 메서드를 모두 구현하지 않으면 객체 생성 시 TypeError가 발생한다.