친절한


1. 상속을 적게 사용하고 조합을 더 많이 사용하세요

2. 일관성 있는 인터페이스를 피하세요

1.

앞서 Four의 Design Patterns에서 언급한 Gang of와 마찬가지로 상속보다 구성을 선호해야 합니다. 상속과 구성을 모두 사용하면 많은 이점이 있습니다. 이 지침의 주요 요점은 본능적으로 상속을 사용할 때 구성이 요구 사항을 더 잘 모델링할 수 있는지 생각해 보라는 것입니다. 어떤 경우에는 이것이 사실입니다. 다음으로 생각할 수 있는 것은 "언제 상속을 사용해야 할까요?"입니다. 대답은 질문에 따라 다르지만 상속이 구성보다 나은 경우에 대한 몇 가지 지침은 다음과 같습니다.

상속은 "예"를 표현합니다. "관계 있음" 대신(인간 -> 동물, 사용자 -> 사용자 세부 정보)

기본 클래스의 코드를 재사용할 수 있습니다(인간은 동물처럼 움직일 수 있음)

모든 파생 클래스에 대한 기본 클래스를 수정하고 싶습니다. 전역 수정(동물이 움직일 때 에너지 소비 수정)

나쁨:

 class Employee
{
    private $name;
    private $email;
 
    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
 
    // ...
}
 
 
// 不好,因为 Employees "有" taxdata
// 而 EmployeeTaxData 不是 Employee 类型的
 
 
class EmployeeTaxData extends Employee
{
    private $ssn;
    private $salary;
    
    public function __construct(string $name, string $email, string $ssn, string $salary)
    {
        parent::__construct($name, $email);
 
        $this->ssn = $ssn;
        $this->salary = $salary;
    }
 
    // ...
}

좋음:

class EmployeeTaxData
{
    private $ssn;
    private $salary;
 
    public function __construct(string $ssn, string $salary)
    {
        $this->ssn = $ssn;
        $this->salary = $salary;
    }
 
    // ...
}
 
class Employee
{
    private $name;
    private $email;
    private $taxData;
 
    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
 
    public function setTaxData(string $ssn, string $salary)
    {
        $this->taxData = new EmployeeTaxData($ssn, $salary);
    }
 
    // ...
}

2. 유창한 인터페이스를 피하세요

연속 인터페이스 유창한 인터페이스 객체지향 프로그래밍에서 코드 가독성을 향상시키기 위해 설계된 API 디자인 패턴입니다. 메소드 체이닝PHPUnit Mock Builder와 같이 컨텍스트가 있는 경우 코드 복잡성을 줄일 수 있습니다. Doctrine Query Builder에는 이 패턴이 코드의 장황함을 줄이는 일부 컨텍스트(주로 빌더 개체)가 있을 수 있지만(예: PHPUnit Mock Builder 또는 Doctrine Query Builder) 다음과 같은 비용이 드는 경우가 더 많습니다.

1. 객체 캡슐화를 깨뜨립니다Fluent interface是一种 旨在提高面向对象编程时代码可读性的API设计模式,他基于方法链Method chaining

有上下文的地方可以降低代码复杂度,例如PHPUnit Mock Builder 和Doctrine Query Builder ,更多的情况会带来较大代价:

While there can be some contexts, frequently builder objects, where this pattern reduces the verbosity of the code (for example the PHPUnit Mock Builder or the Doctrine Query Builder), more often it comes at some costs:

1. 破坏了 对象封装

2. 破坏了 装饰器模式

3. 在测试组件中不好做mock

4. 导致提交的diff不好阅读

5. 了解更多请阅读 连贯接口为什么不好 ,作者 Marco Pivetta.

坏:

class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';
 
    public function setMake(string $make): self
    {
        $this->make = $make;
 
        // NOTE: Returning this for chaining
        return $this;
    }
 
    public function setModel(string $model): self
    {
        $this->model = $model;
 
        // NOTE: Returning this for chaining
        return $this;
    }
 
    public function setColor(string $color): self
    {
        $this->color = $color;
 
        // NOTE: Returning this for chaining
        return $this;
    }
 
    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}
 
$car = (new Car())
  ->setColor('pink')
  ->setMake('Ford')
  ->setModel('F-150')
  ->dump();

好:

 class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';
 
    public function setMake(string $make): void
    {
        $this->make = $make;
    }
 
    public function setModel(string $model): void
    {
        $this->model = $model;
    }
 
    public function setColor(string $color): void
    {
        $this->color = $color;
    }
 
    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}
 
$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();

3. 推荐使用 final 类

能用时尽量使用 final

2. 데코레이터 패턴을 깨뜨립니다

3. 테스트 구성 요소에서 mock을 수행하기 어렵습니다

4. 제출된 diff는 다음과 같습니다. 읽기 쉽지 않음

5. 자세한 내용은 Marco Pivetta의 왜 일관성 있는 인터페이스가 나쁜지 읽어보세요.

Bad:

final class Car
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * @return string The color of the vehicle
     */
    public function getColor()
    {
        return $this->color;
    }
}

Good:

 interface Vehicle
{
    /**
     * @return string The color of the vehicle
     */
    public function getColor();
}
 
final class Car implements Vehicle
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getColor()
    {
        return $this->color;
    }
}

3 .최종 클래스를 사용하는 것이 좋습니다

가능한 경우 최종 키워드를 사용하세요.

🎜1. 통제되지 않는 상속 체인을 방지하세요.🎜🎜2. 단일 책임 모델을 장려하세요. 🎜4. 개발자가 상속된 클래스를 통해 보호된 메서드에 액세스하는 대신 공용 메서드를 사용하도록 권장합니다. 🎜🎜5. 애플리케이션을 중단하지 않고 클래스를 사용할 수 있도록 합니다. 유일한 조건은 클래스가 인터페이스를 구현해야 한다는 것입니다. 공개 메소드가 정의됩니다.🎜🎜자세한 내용은 Marco Pivetta(Ocramius)가 작성한 이 주제에 대한 블로그 게시물을 참조하세요. 🎜🎜Bad:🎜rrreee🎜Good:🎜rrreee🎜 🎜