摘要:是推薦的便于記憶的首字母簡寫,它代表了命名的最重要的五個面對對象編碼設計原則單一職責原則開閉原則里氏替換原則接口隔離原則依賴反轉原則單一職責原則修改一個類應該只為一個理由。別寫重復代碼這條原則大家應該都是比較熟悉了。
SOLID 是Michael Feathers推薦的便于記憶的首字母簡寫,它代表了Robert Martin命名的最重要的五個面對對象編碼設計原則
S: 單一職責原則 (SRP)
O: 開閉原則 (OCP)
L: 里氏替換原則 (LSP)
I: 接口隔離原則 (ISP)
D: 依賴反轉原則 (DIP)
單一職責原則 Single Responsibility Principle (SRP)"修改一個類應該只為一個理由"。人們總是易于用一堆方法塞滿一個類,如同我們在飛機上只能攜帶一個行李箱(把所有的東西都塞到箱子里)。這樣做的問題是:從概念上這樣的類不是高內聚的,并且留下了很多理由去修改它。將你需要修改類的次數(shù)降低到最小很重要。這是因為,當有很多方法在類中時,修改其中一處,你很難知曉在代碼庫中哪些依賴的模塊會被影響到。
Bad:
class UserSettings { private $user; public function __construct($user) { $this->user = $user; } public function changeSettings($settings) { if ($this->verifyCredentials()) { // ... } } private function verifyCredentials() { // ... } }
Good:
class UserAuth { private $user; public function __construct($user) { $this->user = $user; } public function verifyCredentials() { // ... } } class UserSettings { private $user; private $auth; public function __construct($user) { $this->user = $user; $this->auth = new UserAuth($user); } public function changeSettings($settings) { if ($this->auth->verifyCredentials()) { // ... } } }開閉原則 Open/Closed Principle (OCP)
正如Bertrand Meyer所述,"軟件的實體(類, 模塊, 函數(shù),等)應該對擴展開放,對修改關閉。"這個原則是在說明應該允許用戶在不改變已有代碼的情況下增加新的功能。
Bad:
abstract class Adapter { protected $name; public function getName() { return $this->name; } } class AjaxAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = "ajaxAdapter"; } } class NodeAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = "nodeAdapter"; } } class HttpRequester { private $adapter; public function __construct($adapter) { $this->adapter = $adapter; } public function fetch($url) { $adapterName = $this->adapter->getName(); if ($adapterName === "ajaxAdapter") { return $this->makeAjaxCall($url); } elseif ($adapterName === "httpNodeAdapter") { return $this->makeHttpCall($url); } } private function makeAjaxCall($url) { // request and return promise } private function makeHttpCall($url) { // request and return promise } }
在上面的代碼中,對于HttpRequester類中的fetch方法,如果我新增了一個新的xxxAdapter類并且要在fetch方法中用到的話,就需要在HttpRequester類中去修改類(如加上一個elseif 判斷),而通過下面的代碼,就可很好的解決這個問題。下面代碼很好的說明了如何在不改變原有代碼的情況下增加新功能。
Good:
interface Adapter { public function request($url); } class AjaxAdapter implements Adapter { public function request($url) { // request and return promise } } class NodeAdapter implements Adapter { public function request($url) { // request and return promise } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch($url) { return $this->adapter->request($url); } }里氏替換原則 Liskov Substitution Principle (LSP)
對這個概念最好的解釋是:如果你有一個父類和一個子類,在不改變原有結果正確性的前提下父類和子類可以互換。這個聽起來讓人有些迷惑,所以讓我們來看一個經(jīng)典的正方形-長方形的例子。從數(shù)學上講,正方形是一種長方形,但是當你的模型通過繼承使用了"is-a"的關系時,就不對了。
Bad:
class Rectangle { protected $width = 0; protected $height = 0; public function render($area) { // ... } public function setWidth($width) { $this->width = $width; } public function setHeight($height) { $this->height = $height; } public function getArea() { return $this->width * $this->height; } } class Square extends Rectangle { public function setWidth($width) { $this->width = $this->height = $width; } public function setHeight(height) { $this->width = $this->height = $height; } } function renderLargeRectangles($rectangles) { foreach ($rectangles as $rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); $area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20. $rectangle->render($area); } } $rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles($rectangles);
Good:
abstract class Shape { protected $width = 0; protected $height = 0; abstract public function getArea(); public function render($area) { // ... } } class Rectangle extends Shape { public function setWidth($width) { $this->width = $width; } public function setHeight($height) { $this->height = $height; } public function getArea() { return $this->width * $this->height; } } class Square extends Shape { private $length = 0; public function setLength($length) { $this->length = $length; } public function getArea() { return pow($this->length, 2); } } function renderLargeRectangles($rectangles) { foreach ($rectangles as $rectangle) { if ($rectangle instanceof Square) { $rectangle->setLength(5); } elseif ($rectangle instanceof Rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); } $area = $rectangle->getArea(); $rectangle->render($area); } } $shapes = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles($shapes);接口隔離原則
接口隔離原則:"客戶端不應該被強制去實現(xiàn)于它不需要的接口"。
有一個清晰的例子來說明示范這條原則。當一個類需要一個大量的設置項,為了方便不會要求客戶端去設置大量的選項,因為在通常他們不需要所有的設置項。使設置項可選有助于我們避免產(chǎn)生"胖接口"
Bad:
interface Employee { public function work(); public function eat(); } class Human implements Employee { public function work() { // ....working } public function eat() { // ...... eating in lunch break } } class Robot implements Employee { public function work() { //.... working much more } public function eat() { //.... robot can"t eat, but it must implement this method } }
上面的代碼中,Robot類并不需要eat()這個方法,但是實現(xiàn)了Emplyee接口,于是只能實現(xiàn)所有的方法了,這使得Robot實現(xiàn)了它并不需要的方法。所以在這里應該對Emplyee接口進行拆分,正確的代碼如下:
Good:
interface Workable { public function work(); } interface Feedable { public function eat(); } interface Employee extends Feedable, Workable { } class Human implements Employee { public function work() { // ....working } public function eat() { //.... eating in lunch break } } // robot can only work class Robot implements Workable { public function work() { // ....working } }依賴反轉原則 Dependency Inversion Principle (DIP)
這條原則說明兩個基本的要點:
高階的模塊不應該依賴低階的模塊,它們都應該依賴于抽象
抽象不應該依賴于實現(xiàn),實現(xiàn)應該依賴于抽象
這條起初看起來有點晦澀難懂,但是如果你使用過php框架(例如 Symfony),你應該見過依賴注入(DI)對這個概念的實現(xiàn)。雖然它們不是完全相通的概念,依賴倒置原則使高階模塊與低階模塊的實現(xiàn)細節(jié)和創(chuàng)建分離。可以使用依賴注入(DI)這種方式來實現(xiàn)它。更多的好處是它使模塊之間解耦。耦合會導致你難于重構,它是一種非常糟糕的的開發(fā)模式。
Bad:
class Employee { public function work() { // ....working } } class Robot extends Employee { public function work() { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage() { $this->employee->work(); } }
Good:
interface Employee { public function work(); } class Human implements Employee { public function work() { // ....working } } class Robot implements Employee { public function work() { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage() { $this->employee->work(); } }別寫重復代碼 (DRY)
這條原則大家應該都是比較熟悉了。
盡你最大的努力去避免復制代碼,它是一種非常糟糕的行為,復制代碼通常意味著當你需要變更一些邏輯時,你需要修改不止一處。
Bad:
function showDeveloperList($developers) { foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } } function showManagerList($managers) { foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } }
Good:
function showList($employees) { foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } }
Very good:
function showList($employees) { foreach ($employees as $employee) { render([ $employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink() ]); } }
后記:雖然OOP設計需要遵守如上原則,不過實際的代碼設計一定要簡單、簡單、簡單。在實際編碼中要根據(jù)情況進行取舍,一味遵守原則,而不注重實際情況的話,可能會讓你的代碼變的難以理解!
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/31978.html
摘要:使用和在中,通過為屬性或方法設置和關鍵字可以實現(xiàn)對屬性或方法的可見性控制。你的繼承表達了一個對等比如人類是動物的關系,不是包含的關系比如用戶具有用戶詳情你能從基類中復用代碼你想通過修改全局類來對所有派生類進行修改。 使用getter和setter 在 PHP 中,通過為屬性或方法設置 public, protected 和 private 關鍵字可以實現(xiàn)對屬性或方法的可見性控制。不過,...
摘要:超過三個參數(shù)會導致參數(shù)之間的組合過多,你必須對每個單獨的參數(shù)測試大量不同的情況。拆分這些函數(shù),可以讓代碼可重用性更高且更易測試。 函數(shù)參數(shù)不要超過兩個 限制函數(shù)的參數(shù)數(shù)量是非常重要的,因為它使你的函數(shù)更容易測試。超過三個參數(shù)會導致參數(shù)之間的組合過多,你必須對每個單獨的參數(shù)測試大量不同的情況。 沒有參數(shù)是最理想的情況,一個或兩個參數(shù)是可以接受的,三個以上則是應該避免的。這很重要的。如果你...
摘要:代碼質量與其整潔度成正比。它讓你的代碼簡潔優(yōu)雅。是幾個單詞首字母組合而來,分別表示單一功能原則開閉原則里氏替換原則接口隔離原則以及依賴反轉原則。開閉原則開指的就是類模塊函數(shù)都應該具有可擴展性,閉指的是它們不應該被修改。 測試代碼質量的唯一方式:別人看你代碼時說 f * k 的次數(shù)。 代碼質量與其整潔度成正比。干凈的代碼,既在質量上較為可靠,也為后期維護、升級奠定了良好基礎。 本文并不是...
摘要:代碼質量與其整潔度成正比。它讓你的代碼簡潔優(yōu)雅。是幾個單詞首字母組合而來,分別表示單一功能原則開閉原則里氏替換原則接口隔離原則以及依賴反轉原則。開閉原則開指的就是類模塊函數(shù)都應該具有可擴展性,閉指的是它們不應該被修改。 測試代碼質量的唯一方式:別人看你代碼時說 f * k 的次數(shù)。 代碼質量與其整潔度成正比。干凈的代碼,既在質量上較為可靠,也為后期維護、升級奠定了良好基礎。 本文并不是...
閱讀 2476·2021-10-12 10:11
閱讀 1227·2021-10-11 10:58
閱讀 3265·2019-08-30 15:54
閱讀 707·2019-08-30 13:59
閱讀 675·2019-08-29 13:07
閱讀 1403·2019-08-26 11:55
閱讀 2140·2019-08-26 10:44
閱讀 2637·2019-08-23 18:25