1. 少用继承多用组合

2. 避免连贯接口

3. 推荐使用 final 类

1. 少用继承多用组合

正如 the Gang of Four 所著的设计模式之前所说, 我们应该尽量优先选择组合而不是继承的方式。使用继承和组合都有很多好处。 这个准则的主要意义在于当你本能的使用继承时,试着思考一下组合是否能更好对你的需求建模。 在一些情况下,是这样的。

接下来你或许会想,“那我应该在什么时候使用继承?” 答案依赖于你的问题,当然下面有一些何时继承比组合更好的说明:

你的继承表达了“是一个”而不是“有一个”的关系(人类-》动物,用户-》用户详情)

你可以复用基类的代码(人类可以像动物一样移动)

你想通过修改基类对所有派生类做全局的修改(当动物移动时,修改她们的能量消耗)

坏:

 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. 避免连贯接口

连贯接口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 关键字:

1. 阻止不受控的继承链

2. 鼓励 组合.

3. 鼓励 单一职责模式.

4. 鼓励开发者用你的公开方法而非通过继承类获取受保护方法的访问权限.

5. 使得在不破坏使用你的类的应用的情况下修改代码成为可能.

The only condition is that your class should implement an interface and no other public methods are defined.

For more informations you can read the blog post on this topic written by Marco Pivetta (Ocramius).

坏:

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;
    }
}

好:

 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;
    }
}