>백엔드 개발 >PHP 튜토리얼 >PHP 마스터 | 널 객체 패턴 - 도메인 모델의 다형성

PHP 마스터 | 널 객체 패턴 - 도메인 모델의 다형성

Christopher Nolan
Christopher Nolan원래의
2025-02-25 14:53:08453검색

PHP Master | The Null Object Pattern - Polymorphism in Domain Models

코어 포인트

    빈 객체 패턴은 다형성을 사용하여 조건부 코드를 줄여 코드를보다 간결하고 유지 관리하기 쉽게하는 설계 패턴입니다. 실제 객체를 대체 할 수있는 비 기능 객체를 제공하므로 널 값 점검이 필요하지 않습니다.
  • 빈 객체 모드는 빈 개체 생성 및 반환 및 반환과 같은 다른 설계 모드와 함께 사용하거나 런타임시 객체의 동작을 변경하는 정책 모드와 함께 사용할 수 있습니다.
  • 빈 객체 패턴의 잠재적 단점은 불필요한 객체의 생성으로 이어지고 메모리 사용량을 증가시킬 수 있다는 것입니다. 또한 추가 클래스와 인터페이스가 필요하기 때문에 코드를 더욱 복잡하게 만들 수 있습니다.
  • 빈 객체 패턴을 구현하려면 실제 객체와 동일한 인터페이스를 구현하는 빈 개체 클래스를 생성해야합니다. 이 빈 객체는 인터페이스의 모든 메소드에 대한 기본 구현을 제공하여 실제 객체를 교체 할 수 있습니다. 이로 인해 코드가 더욱 강력하고 오류가 덜 발생합니다.
  • 사양을 완전히 준수하지는 않지만, 직교는 "좋은 디자인"의 원리를 기반으로 한 소프트웨어 시스템의 본질이라고 말할 수 있습니다. 견고하고 깨지기 쉬운 문제가 발생하기 쉽습니다. 물론, 실제로 생산 시스템을 실행하는 것보다 직교 시스템의 장점에 대해 더 쉽게 이야기하는 것이 더 쉽습니다. 그럼에도 불구하고 시스템에서 고도로 분리 된 구성 요소를 달성하는 것은 유토피아 개념이 아닙니다. 다형성과 같은 다양한 프로그래밍 개념은 런타임에 부품을 전환 할 수있는 유연한 프로그램의 설계를 허용하며, 그 종속성은 구체적인 구현이 아닌 추상 형태로 표현 될 수 있습니다. 나는 "인터페이스 지향 프로그래밍"의 오래된 모토가 인프라 또는 응용 프로그램 논리를 구현하든 시간이 지남에 따라 널리 채택되었다고 말합니다. 그러나 도메인 모델의 영역을 밟을 때 상황은 매우 다릅니다. 솔직히 이것은 예측 가능한 시나리오입니다. 결국, 상호 관련된 객체 네트워크 (잘 정의 된 비즈니스 규칙에 의해 제한된 데이터와 동작이있는)가 다형성이어야하는 이유는 무엇입니까? 그 자체로는 의미가 없습니다. 그러나이 규칙에는 몇 가지 예외가 있으며 결국이 상황에 적용될 수 있습니다. 첫 번째 예외는 가상 프록시를 사용하는 것입니다. 이는 실제 도메인 객체 구현과 효과적으로 동일한 인터페이스입니다. 또 다른 예외는 소위 "NULL CASE"이며, 이는 조작이 잘 가득 찬 엔티티를 채우는 대신 NULL 값을 할당하거나 반환 할 수있는 특별한 경우입니다. 전통적인 비 폴리 흉부 접근법에서 모델 사용자는 이러한 "유해한"널 값을 확인하고 조건을 우아하게 처리하여 코드 전체에서 조건부 진술이 폭발적으로 발생해야합니다. 다행 스럽게도이 혼란스러운 상황은 단순히 해당 객체와 동일한 인터페이스를 구현할 도메인 객체의 다중 브랜치 구현을 만들어서 쉽게 해결할 수 있습니다. 작업을 수행하는 동안 추악한 널 값을 반복적으로 확인합니다. 당연히,이 접근법은 다형성의 장점을 극단적으로 가져 오는 빈 개체라는 설계 패턴입니다. 이 기사에서는 여러 경우 에이 패턴의 이점을 보여주고 다형성 방법에 어떻게 밀접하게 붙어 있는지 보여줍니다.
  • 비 폴리 흉부 조건을 치료하십시오
예상대로, 빈 객체 패턴의 장점을 보여줄 때 시도하는 몇 가지 방법이 있습니다. 내가 찾은 특히 간단한 접근 방식은 일반 목적 파인더에서 널 값을 반환 할 수있는 데이터 맵퍼를 구현하는 것입니다. 우리가 하나의 사용자 엔티티로 구성된 스켈레톤 도메인 모델을 성공적으로 작성했다고 가정 해 봅시다. 인터페이스와 클래스는 다음과 같습니다

사용자 클래스는 일부 뮤지터/액세서가 일부 사용자 데이터와 동작을 정의하기 위해 일부 뮤지터/액세서를 구현하는 반응성 구조입니다. 이 구성된 도메인 클래스를 사용하면 이제 한 걸음 더 나아가 도메인 모델과 데이터 액세스 계층을 분리하는 기본 데이터 맵퍼를 정의 할 수 있습니다.
<code class="language-php"><?php namespace Model;

interface UserInterface
{
    public function setId($id);
    public function getId();

    public function setName($name);
    public function getName();

    public function setEmail($email);
    public function getEmail();
}</code>
<code class="language-php"><?php namespace Model;

class User implements UserInterface
{
    private $id;
    private $name;
    private $email;

    public function __construct($name, $email) {
        $this->setName($name);
        $this->setEmail($email);
    }

    public function setId($id) {
        if ($this->id !== null) {
            throw new BadMethodCallException(
                "The ID for this user has been set already.");
        }
        if (!is_int($id) || $id             throw new InvalidArgumentException(
              "The ID for this user is invalid.");
        }
        $this->id = $id;
        return $this;
    }

    public function getId() {
        return $this->id;
    }

    public function setName($name) {
        if (strlen($name)  30) {
            throw new InvalidArgumentException(
                "The user name is invalid.");
        }
        $this->name = $name;
        return $this;
    }

    public function getName() {
        return $this->name;
    }

    public function setEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                "The user email is invalid.");
        }
        $this->email = $email;
        return $this;
    }

    public function getEmail() {
        return $this->email;
    }
}</code>
가장 먼저 나타나야하는 것은 Mapper의 FetchById () 메소드가 블록의 장난 꾸러기라는 것입니다. 데이터베이스의 사용자가 주어진 ID와 일치 할 때 효과적으로 NULL을 반환하기 때문입니다. 명백한 이유로,이 서투른 조건은 클라이언트 코드가 맵퍼 파인더를 호출 할 때마다 널 값을 확인하기 위해 열심히 노력해야합니다.

언뜻보기에는 문제가 없으며 한 곳에서 확인하십시오. 그러나 여러 페이지 컨트롤러 나 서비스 계층에 동일한 라인이 나타나면 벽돌 벽에 부딪칩니다. 당신이 그것을 깨닫기 전에, 맵퍼에 의해 반환 된 무고한 널은 많은 반복적 인 조건을 만들어 낼 것입니다.
<code class="language-php"><?php namespace ModelMapper;
use LibraryDatabaseDatabaseAdapterInterface,
    ModelUser;

class UserMapper implements UserMapperInterface
{   
    private $adapter;

    public function __construct(DatabaseAdapterInterface $adapter) {
        $this->adapter = $adapter;
    }

    public function fetchById($id) {
        $this->adapter->select("users", array("id" => $id));
        if (!$row = $this->adapter->fetch()) {
            return null;
        }
        return $this->createUser($row);
    }

    private function createUser(array $row) {
        $user = new User($row["name"], $row["email"]);
        $user->setId($row["id"]);
        return $user;
    }
}</code>

클라이언트 코드에서 조건 문을 제거하십시오 그러나 걱정할 필요는 없습니다. 왜냐하면 빈 객체 패턴이 왜 다형성이 신의 선물인지 보여주는 경우이기 때문입니다. 성가신 조건부 진술을 한 번에 제거하려면 이전 사용자 클래스의 다형성 버전을 구현할 수 있습니다.

<code class="language-php"><?php use LibraryLoaderAutoloader,
    LibraryDatabasePdoAdapter,
    ModelMapperUserMapper;

require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$adapter = new PdoAdapter("mysql:dbname=test", "myusername", "mypassword");

$userMapper = new UserMapper($adapter);

$user = $userMapper->fetchById(1);

if ($user !== null) {
    echo $user->getName() . " " . $user->getEmail();
}</code>
완전한 엔티티 클래스가 모든 종류의 장식을 포장 할 것으로 예상되면 아마도 실망했을 것입니다. 엔티티의 "NULL"버전은 해당 인터페이스를 준수하지만 메소드는 빈 래퍼이며 실제 구현이 없습니다. Nulluser 클래스의 존재는 분명히 우리에게 칭찬을받을 가치가있는 것을 가져 오지 않지만, 그것은 우리가 이전의 모든 조건 진술을 쓰레기에 던질 수있게하는 간결한 구조입니다. 그것이 어떻게 구현되는지보고 싶습니까? 먼저, 사전 작업을 수행하고 데이터 맵퍼를 재구성하여 Finder가 빈 값 대신 빈 사용자 객체를 반환하도록해야합니다.

Mapper의 CreateUser () 메소드는 Finder에 전달 된 ID가 유효한 사용자를 반환하지 않으면 빈 사용자를 생성 할 책임이 있기 때문에 작은 조건을 숨 깁니다. 그럼에도 불구하고,이 미묘한 비용은 클라이언트 코드의 작업을 많이 절약 할뿐만 아니라 반복적 인 수표를 수행 할뿐만 아니라 빈 사용자를 다루어야 할 때 불만을 제기하지 않는 느슨한 소비자로 전환합니다.

<code class="language-php"><?php namespace Model;

interface UserInterface
{
    public function setId($id);
    public function getId();

    public function setName($name);
    public function getName();

    public function setEmail($email);
    public function getEmail();
}</code>
이 다형성 접근법의 주요 단점은 사용하는 응용 프로그램이 잘못된 엔티티를 다룰 때 절대 충돌하지 않기 때문에 너무 느슨해집니다. 최악의 경우, 사용자 인터페이스에는 빈 줄 만 표시되지만 실제로 시끄러운 것은 우리를 역겨운 것은 없습니다. 이것은 초기 NullUser 클래스의 현재 구현을 스캔 할 때 특히 분명합니다. 권장되는 것은 말할 것도없이 실현 가능하더라도 빈 객체에서 논리를 캡슐화하면서 다형성을 변경하지 않도록 할 수도 있습니다. 빈 객체는 몇 가지 특별한 경우에만 클라이언트 코드에 노출되어야하는 기본 데이터 및 동작을 캡슐화하는 데 완벽하다고 말할 수 있습니다. 당신이 충분히 야심적이고 간단한 빈 사용자 객체 로이 개념을 시도하고 싶다면, 현재 NullUser 클래스는 다음과 같이 리팩토링 될 수 있습니다 :

.

Nulluser의 향상된 버전은 조용한 전임자보다 약간 표현력이 뛰어납니다. Getter는 유효하지 않은 사용자를 요청할 때 일부 기본 메시지를 반환하기위한 몇 가지 기본 구현을 제공하기 때문입니다. 사소한 반면,이 변경 사항은 클라이언트 코드가 빈 사용자를 처리하는 방식에 긍정적 인 영향을 미칩니다. 이번에는 사용자가 스토리지에서 존재하지 않는 사용자를 추출하려고 할 때 일부 문제가 발생한다는 것을 알고 있기 때문에. 이것은 실제로 비어 있지 않은 빈 개체를 구현하는 방법을 보여줄뿐만 아니라 특정 요구에 따라 관련 객체 내부의 논리를 이동하는 것이 얼마나 쉬운지를 보여줍니다.
<code class="language-php"><?php namespace Model;

class User implements UserInterface
{
    private $id;
    private $name;
    private $email;

    public function __construct($name, $email) {
        $this->setName($name);
        $this->setEmail($email);
    }

    public function setId($id) {
        if ($this->id !== null) {
            throw new BadMethodCallException(
                "The ID for this user has been set already.");
        }
        if (!is_int($id) || $id             throw new InvalidArgumentException(
              "The ID for this user is invalid.");
        }
        $this->id = $id;
        return $this;
    }

    public function getId() {
        return $this->id;
    }

    public function setName($name) {
        if (strlen($name)  30) {
            throw new InvalidArgumentException(
                "The user name is invalid.");
        }
        $this->name = $name;
        return $this;
    }

    public function getName() {
        return $this->name;
    }

    public function setEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                "The user email is invalid.");
        }
        $this->email = $email;
        return $this;
    }

    public function getEmail() {
        return $this->email;
    }
}</code>
결론

일부는 빈 객체를 구현하는 것이 특히 PHP (예 : 다형성)의 핵심 개념이 상당히 과소 평가된다고 말할 수 있습니다. 그들은 어느 정도 옳습니다. 그럼에도 불구하고, 신뢰할 수있는 프로그래밍 원칙과 디자인 패턴의 점진적 채택뿐만 아니라 언어 객체 모델이 도달 한 성숙 수준은 꾸준히 앞으로 나아가서 복잡하고 비현실적인 개념으로 여겨지는“고급 상품”을 사용하기 시작하는 것입니다. 얼마 전까지 만해도 필요한 모든 기준을 제공합니다. NULL 객체 패턴은이 범주에 속하지만 구현은 매우 간단하고 우아하여 클라이언트 코드에서 중복 NULL 검사를 지울 때 매력적이지 않기가 어렵습니다. Fotolia의 사진

(공간 제한으로 인해 원본 텍스트의 FAQ 부분은 여기에서 생략됩니다.)

위 내용은 PHP 마스터 | 널 객체 패턴 - 도메인 모델의 다형성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.