類別


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 PHPort Mock Builder Unit Mock Builder Unit Builder 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;
    }
}