객체 디자인 스타일 가이드 요약

객체 작성 및 사용 방법


저는 지금 재미있는 책을 읽고 있습니다. 이름은Matthias Noback입니다. 가능한 한 대상을 가장 잘 만드는 방법에 관한 책입니다. 그래서 저는 그것을 여기에 가지고 와서 제가 발견한 더 재미있는 기교와 지시를 보여드리기로 했습니다.물론, 만약 네가 이 화제를 깊이 있게 탐구하고 싶다면, 나는 네가 책 전체를 읽는 것을 건의한다.

1: OOP 개념 소개


이 책에서 계승은 대상 프로그래밍의 지주 중 하나로 여겨지지만 작은 역할을 하고 있다.실제로 상속을 사용하면 디자인의 혼란을 초래할 수 있다.
이 책에서 상속은 주로 두 가지 상황에서 사용될 것이다.
  • 의존항 인터페이스를 정의할 때(의존항 주입과 반전).
  • 대상의 차원 구조를 정의할 때, 예를 들어 내장된 이상 클래스에서 확장된 사용자 정의 이상을 정의할 때.
    대부분의 다른 상황에서, 우리는 개발자가 우리의 클래스에서 확장되는 것을 적극적으로 막기를 바란다.클래스 앞에final 키워드를 추가해서 실현할 수 있습니다.잠시 후에 진일보한 설명을 드리겠습니다.
    이 작문은 유산보다 더 환영을 받는다.
  • 단원 테스트의 기본 구조는 다음과 같다.
    각 테스트 방법의 기본 구조는 배열-동작-단언:
    1. 스케줄링: 테스트 중인 객체(SUT 또는 테스트 대상)를 알려진 상태로 둡니다.
    제2막: 그중의 한 방법을 호출한다.
    3 단언: 최종 상태에 대해 단언을 한다.
    개똥이 현실이 됐어.두 가지 유형의 객체가 있습니다.
  • 작업을 수행하거나 정보를 반환하는 서비스 대상입니다.첫 번째 유형의 객체는 한 번 작성되고 여러 번 사용되지만 변경할 수는 없습니다.그것들의 생명 주기는 매우 간단하다.일단 만들어지면, 특정한 임무를 수행하는 소형 기계처럼 영원히 운행할 수 있다.이런 서비스는 변하지 않는다.
    서비스 대상은 집행기로서 통상적으로 그들의 업무를 지시하는 것으로 유명하다. 즉, 표현기, 계산기, 메모리 라이브러리, 스케줄러 등
  • 일부 데이터의 대상을 저장하고 일부 조작을 공개하거나 검색하는 행위를 선택할 수 있다. 첫 번째 유형은 이런 유형을 사용하여 임무를 완성한다.이러한 객체는 서비스에 사용되는 재료입니다.값 대상과 모델/실체는 두 가지 유형이 있지만 우리 자신을 뛰어넘지 마라.
  • 2. 서비스에 전념


    여기에는 서비스를 어떻게 해야 하는지에 대한 많은 건의가 있습니다. 저는 이 건의들을 열거할 것입니다.
  • 서비스 사용 의존항 주입을 창설하여 서비스를 실례화하고 테스트한 후 즉시 사용할 준비를 한다.따라서 의존관계는 명확하게 밝혀야 한다.
    다음은 매개 변수 $appConfig가 캐시를 가져오는 디렉터리에만 어떻게 사용되는지 보여 줍니다. 따라서, 전체 설정 대상을 주입하지 말고 서비스가 실제 필요로 하는 값만 주입하도록 하십시오.
  • interface Logger
    {
        public function log(string $message): void;
    }
    
    final class FileLogger implements Logger
    {
        private Formatter $formatter;
    
        // Formatter is a dependency of FileLogger
        public function __ construct(Formatter $formatter) 
        {
            $this->formatter = $formatter;
        }
    
        public function log(string $message): void
        {
            formattedMessage = $this->formatter->format($message) ;
            // ….
        }
    }
    
  • 가능하면 모든 관련 설정 값을 함께 놓아야 합니다.서비스는 전체 전역 설정 대상에 주입해서는 안 되고, 필요한 값만 주입해야 한다.
  • 길을 잘못 들다
    final class MySQLTableGateway
    {
        public function __construct(
            string $host,
            int $port,
            string $username,
            string $password,
            string $database,
            string $table
        ) {
            // ...
        }
    }
    
    좋은 방법
    final class MySQLTableGateway
    {
        public function __construct(
            ConnectionConfiguration $connectionConfiguration,
            string $table
        ) {
            // $table is the name of the table, It isn’t necessary to make the connection 
        }
    }
    
  • 서비스 포지셔닝 머신(다른 서비스를 검색할 수 있는 서비스)을 사용하지 않고 필요한 의존항을 현저하게 주입한다.
  • 모든 구조 함수 매개 변수는 필수적이어야 한다. 코드가 불필요하게 복잡해지기 때문이다.만약 그것을 선택할 수 있는 의존항으로 삼고 싶다면 null object 을 사용할 수 있다.
  • 서비스는 변할 수 없는 것이어야 한다. 즉, 완전히 실례화된 후에 변경할 수 없다. 왜냐하면 행위가 이렇게 예측할 수 없기 때문이다.
  • 그래서...이런 일은 피하자.
    final class EventDispatcher
    {
        private array $listeners = [];
    
        public function addListener(
            string $event,
            callable $listener
        ): void {
            $this->listeners[event][] = $listener;
        }
    }
    
  • 구조 함수 중의 일부 검증 오류로 인해 속성만 분배하거나 이상을 일으킨다.
  • final class FileLogger implements Logger
    {
        private string $logFilePath;
    
        public function __construct(string $logFilePath)
        {
            // $logFilePath should be properly set up, so we just need a safety check
            if (! is_writable($logFilePath)) {
                throw new InvalidArgumentException(
                    'Log file path  . $logFilePath .  should be writable
                );
            }
            $this->logFilePath = $logFilePath;
        }
    
        public function log(string message): void
        {
            // ...
        }
    }
    
  • 이상적인 상황에서 숨겨진 의존 관계를 피하기 위해 대상을 만든다. 예를 들어 함수 json encode() 또는 PHP에서 온 클래스, 예를 들어 DateTime
  • 길을 잘못 들다
    final class ResponseFactory
    {
        public function createApiResponse(array $data): Response
        {
            // json_encode is a hidden dependency
            return new Response(
            json_encode(
                $data,
                JSON_THROW_ON_ERROR | JSON_FORCE_OBJECT), ['Content-Type' => 'application/json']
            );
        }
    }
    
    좋은 방법
    final class JsonEncoder
    {
        / **
        * throws RuntimeException
        */
        public function encode(array $data): string
        {
            try {
                return json_encode(
                    $data,
                    JSON_THROW_ON_ERROR | JSON_FORCE_OBJECT
                );
            // we can throw our own exception, with more specific info
            } catch (RuntimeException previous) {
                throw new RuntimeException(
                    'Failed to encode data: ' . var_export($data, true),
                    0,
                    previous
                );
            }
        }
    }
    
    final class ResponseFactory
    {
        private JsonEncoder $jsonEncoder;
    
        // JsonEncoder can be injected as a dependency
        public function __construct(JsonEncoder $jsonEncoder)
        {
            $this->jsonEncoder = $jsonEncoder;
        }
    
        public function createApiResponse(data): Response
        {
            return new Response($this->jsonEncoder->encode($data));
        }
    }
    
    date () 와 언어의 대형 핵심 프로그램으로 같은 일을 할 수 있습니다. 응용 프로그램 층이 이렇게 결합됩니다.

    3. 기타 물체


    3.1 가치 대상 및 모델/실체


    주요 권장 사항은 다음과 같습니다.
  • 구조 함수의 대상을 검증합니다. 응용 프로그램에 유효한 대상만 있고 모든 대상이 예상된 대상만 있는지 확인합니다.데이터가 잘못되면 구조 함수에서 이상을 던져야 합니다.이 책에서는 다음과 같은 RuntimeExceptions가 표시하기 때문에 잘못된 매개 변수 이상에 대해 사용자 정의 이상을 사용하지 않는 것을 권장합니다.
  • final class Coordinates
    {
        public function _construct(float $latitude, float $longitude)
        {
            if ($latitude > 90 || $latitude < -90) {
                throw new InvalidArgumentException(
                    'Latitude should be between -90 and 90'
                );
            }
            $this->latitude = $latitude;
        }
    }
    
  • 속성 충전물을 사용하지 마세요. 이따가 예시를 통해 어디서 사용할 수 있는지 알아보겠습니다.
  • 실체/모델은 유일한 id로 표시할 수 있지만 값 대상은 하나 이상의 기본 유형 값만 포함하기 때문에 할 수 없다.
  • 구조 함수에 더 많은 의미를 추가하기 위해 명명된 구조 함수가 나타났습니다. 이것은 특정한 영역의 이름을 가진 정적 방법입니다. 코드가 전형적인 새로운 클래스 ()보다 더 좋은 이름을 가지도록 허용합니다.
  • 표준 방식
    $salesOrder = new SalesOrder();
    
    더 좋은 방법
    $salesOrder = SalesOrder::place();
    
    construct 방법을 private로 설정하여 사용하지 않도록 하고 place () 방법에서 구조 함수를 호출할 수 있습니다.
    final class DecimalValue
    {
        private int value;
        private int precision;
    
        private function __construct(int $value, int $precision)
        {
            this.value = value;
            Assertion.greaterOrEqualThan($precision, 0);
            $this->precision = $precision;
        }
    
        public static function fromInt(
            int $value,
            int $precision
        ): DecimalValue {
            return new DecimalValue($value, $precision);
        }
    
        public static function fromFloat(
            float $value,
            int $precision
        ): DecimalValue (
            return new DecimalValue(
                (int) round($value * pow(10, precision)),
                $precision
            ):
        }
    }
    
  • 값 대상의 가장 좋은 점 중 하나는 그 구조 함수에서 검증을 하면 값 대상을 볼 때 검증된 정보를 포함하고 코드의 다른 점에서 검증할 필요가 없다는 것이다.
  • 실패한 방식으로 대상과 구조 함수를 테스트하는 행위는 값이 정확한지 확인하기 위해 테스트를 만들지 마십시오.
  • public function it_can_be_constructed(): void
    {
        $coordinates = new Coordinates(60.0, 100.0);
        assertEquals(60.0, $coordinates->latitude());
        assertEquals(100.0, $coordinates->longitude());
    }
    
  • 한 마디로 하면 값 대상은 역 개념을 대표하는 것이 아니다.그것들은 응용 프로그램의 어느 곳에나 나타날 수 있다.값 대상은 봉인된 기원 유형 값의 불변 대상이다.
  • 3.2 DTO(데이터 전송 개체)


    3.1 규칙은 이러한 유형의 객체 DTO에 적합하지 않습니다.값 대상과 모델에서 우리는 데이터의 일치성과 유효성을 원하지만 DTO에서는 데이터를 한 점에서 다른 점으로 전송하기를 원한다.
  • 일반 구조 함수를 사용하여 DTO를 만들 수 있습니다.
  • 속성을 하나씩 설정할 수 있습니다.
  • 모든 속성이 공개되어 있기 때문에 Getter를 사용하지 않고 공개하고 직접 방문합니다.
  • 그 속성은 기원 유형 값만 포함한다.
  • 속성은 다른 DTO나 DTO를 포함하는 간단한 배열을 선택할 수 있습니다.
  • 필요할 때 속성 충전재를 사용할 수 있습니다.
  • final class ScheduleMeetup
    {
        public string $title;
        public string $date;
    
        public static function fromFormData(
            array $formData
        ): ScheduleMeetup {
            $scheduleMeetup = new ScheduleMeetup();
            $scheduleMeetup->title = $formData['title'];
            $scheduleMeetup->date = $formData['date'];
    
            return $scheduleMeetup;
        }
    }
    
    객체에는 비헤이비어를 포함하는 방법, 정보를 읽어들이는 질의와 작업을 수행하는 명령 등 두 가지 방법이 있지만 모두 동일한 템플릿으로 설계할 수 있습니다. 즉,
    1º 매개 변수를 검사하고 문제가 발생하면 오류를 던집니다.
    2:반드시 해야 할 일을 하고 필요할 때 실수를 던진다.
    3º 검사 후 조건 검사.만약 당신이 좋은 테스트를 한다면, 이것은 불필요한 것이지만, 예를 들어, 만약 당신이 코드를 남겼다면, 이것은 안전 검사에 유리할 수 있다.
    조회 방법이면 4º으로 돌아갑니다.
    이상은 코드에서 매우 좋은 부분이며, 어떤 경우에는 사용자 정의 이상을 사용하는 것이 매우 유용합니다.
    1º 더 높은 등급의 특정 이상 유형을 포착하고 싶다면
    try {
        // possibly throws ‘SomeSpecific’ exception
    } catch (SomeSpecific $exception) {
        // …
    }
    
    2º 단일 유형의 이상을 실례화하는 여러 방식이 있다면
    final class CouldNotDeliverOrder extends RuntimeException
    {
        public static function itWasAlreadyDelivered(): CouldNotDeliverOrder
        {
            // ...
        }
        public static function insufficientQuantitiesInStock(): CouldNotDeliverOrder
        {
             //...
        }
    }
    
    3º명명 구조 함수를 사용하여 예외를 실례화하려면
    final class CouldNotFindProduct extends RuntimeException
    {
        public static function withId(
            ProductId $productld
        ): CouldNotFindProduct (
            return new CouldNotFindProduct('Could not find a product with ID . $productld);
        }
    }
    throw CouldNotFindProduct .withId(/* ... */);
    
    Exception 클래스의 이름에 "Exception"을 추가하지 않고 Invalid Email Address 또는 Could NotFind Product와 같은 명시적인 이름을 사용합니다.
    이것이 바로 모든 사람들이다. 책에는 아직 너무 많은 예가 있기 때문에 나는 모두가 보도록 격려한다.만약 당신이 이 책의 두 번째 부분을 원한다면, 평론에서 저에게 알려 주세요.
    출처 및 추가 정보
  • The book

  • Blog of the book author
  • Named constructors
  • Composition over inheritance
  • 좋은 웹페이지 즐겨찾기