類
1. 少用繼承多用組合
正如 the Gang of Four 所著的設計模式之前所說, 我們應該盡量優(yōu)先選擇組合而不是繼承的方式。使用繼承和組合都有很多好處。 這個準則的主要意義在于當你本能的使用繼承時,試著思考一下組合是否能更好對你的需求建模。 在一些情況下,是這樣的。
接下來你或許會想,“那我應該在什么時候使用繼承?” 答案依賴于你的問題,當然下面有一些何時繼承比組合更好的說明:
你的繼承表達了“是一個”而不是“有一個”的關系(人類-》動物,用戶-》用戶詳情)
你可以復用基類的代碼(人類可以像動物一樣移動)
你想通過修改基類對所有派生類做全局的修改(當動物移動時,修改她們的能量消耗)
壞:
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. 鼓勵開發(fā)者用你的公開方法而非通過繼承類獲取受保護方法的訪問權限.
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; } }