PHP面向對象設計五大原則(SOLID)梳理總結

PHP設計原則梳理,參考《PHP核心技術與最佳實踐》、《敏捷開發原則、模式與實踐》,文章PHP面向對象設計的五大原則設計模式原則SOLIDphp

單一職責原則(Single Responsibility Principle, SRP)

定義/特性
  • 僅有一個引發類變化的緣由
  • 一個類只承擔一項職責(職責:變化的緣由)
  • 避免相同的職責分散到不一樣的類,功能重複
問題
  • 一個類承擔的職責過多,多個職責間相互依賴,一個職責的變換會影響這個類完成其餘職責的能力,當引發類變化的緣由發生時,會遭受到意想不到的破壞
遵照SPR原則優點
  • 減小類之間的耦合:當需求變化時,只修改一個類,從而隔離了變化帶來類對其餘職責的影響
  • 提升類的複用性:按需引用,一個類負責一個職責,需求的變更只須要修改對應的類或增長某一職責
  • 下降類的複雜度:職責單一,功能分散開下降一個類多個職責類的複雜度
代碼示例
class ParseText
{
    private $content;
    
    public function decodeText(String $content)
    {
        // TODO: decode content
    }
    
    public function saveText()
    {
        // TODO:: save $this->content;
    }
}
/*
問題思考:
解析的文本類型會有多種-html、xml、json
保存的文本也會有多種途徑-redis、mysql、file
客戶端只須要解析文本時必須會引入saveText不須要的方法
兩個職責之間沒有強烈的依賴關係存在
任意職責需求變化都須要更改這個類
*/

/*
符合SRP的設計
職責拆分
*/

class Decoder
{
    private $content;
    
    public function decodeText(String $content)
    {
    // TODO: decode content
    }
    
    public function getText()
    {
        return $this->content;
    }
}

class Store
{
    public function save($content)
    {
        // TODE: save
    }
}
總結

軟件設計所作的許多內容就是發現職責併合理的分離職責間的關係。若是應用程序的變化老是同時影響多個職責,就不必分離職責。html

接口隔離原則(Interface Segregation Principle ISP)

問題

設計應用程序時,類的接口不是內聚的。不一樣的客戶端只包含集中的部分功能,但系統會強制客戶端實現模塊中全部方法,而且還要編寫一些啞方法。這樣的接口成爲胖接口或者是接口污染,這樣的接口會給系統引入一些不當的行爲,資源浪費,影響其餘客戶端程序加強了耦合性等mysql

ISP定義/特性

  • 不該該強迫客戶端依賴與他們不須要的方法/功能
  • 一個類對一個類的依賴應該創建在最小的接口上
  • 接口的實現類應該只呈現爲單一職責原則
遵循ISP原則優點
  • 將胖接口分離,每一組接口提供特定功能服務於特定一組的客戶端程序
  • 對一組接口的更改不會/較小的影響到其餘的接口/客戶端程序,保證接口的純潔性

解決方式

  • 胖接口分解成多個特定客戶端的接口/多重接口分離繼承
  • 使用委託分離接口,兩個對象參與處理同一個請求,接受請求的對象將請求委託給另外一個對象處理
代碼示例
/*
* 公告接口
*/
interface Employee
{
    public function startWork();
    public function endWork();
}

/*
* 定義特定客戶端接口
*/
interface Coder
{
    public function writeCode();
}

interface Ui
{
    public function designPage();
}

class CoderClient implements Employee, Coder
{
    public function startWork()
    {
        //TODO:: start work time
    }
    public function endWork()
    {
        //TODO:: end work time
    }
    
    public function writeCode()
    {
        //TODO:: start write code
        return 'hellow world';
    }
}
$c = new CoderClient();
echo $c->writeCode();
總結

胖類會致使他們的客戶端程序之間產生不正常的而且有害的耦合關係。經過把胖客戶度分解成多個特定於客戶端的接口,客戶端牢牢依賴於他們實際調用的方法,從而解除了客戶端與他們沒有調用的方法之間的依賴關係。接口隔離應作的小而少。redis

SRP與ISP比較
  • 都是解決軟件設計中依賴關係原則
  • SRP 注重職責的劃分,主要約束類,實際上是接口和方法,是程序中的細節和實現。ISP 注重接口的隔離,約束的是接口,從更宏觀的角度對接口的抽象設計

開放-封閉原則(Open-Close Principle OCP)

問題

隨着軟件系統規模的不斷擴大,系統的維護和修改的複雜性不斷提升。系統一處的更改每每會影響到其餘模塊。正確的運用OCP原則能夠解決此類問題。sql

定義/特性
  • 一個模塊在擴展行爲方面應該是開放的而在更改性方面應該是封閉的
遵循OCP優點
  • 模塊的行爲是可擴展的,能夠方便的對現有模塊的行爲/功能進行擴展
  • 對於模塊行爲的擴展不會/較小的影響現有系統/模塊
代碼示例
/*
* 定義有固定行爲的抽象接口
*/
interface Process
{
    public function action(String $content);
}

/*
* 繼承抽象接口,擴展不一樣的行爲
*/
class WriteToCache implements Process
{
    public function action(String $content)
    {
        return 'write content to cache: '.$content;
    }
}

class ParseText
{
    private $content;
    public function decodeText($content)
    {
        $this->content = $content;
    }
    
    public function addAction(Process $process)
    {
        if ($process instanceof Process) {
            return $process->action($this->content);    
        }
    }
}

$p = new ParseText();
$p->decodeText('content');
echo $p->addAction(new WriteToCache());
總結

OCP核心思想就是抽象接口編程,抽象相對穩定。讓類依賴與固定的抽象,經過面向對象的繼承和多態讓類繼承抽象,複寫其方法或固有行爲,是想新的擴展方法/功能,實現擴展。編程

里氏替換原則(Liskov Substitution Principle LSP)

問題

面向對象中大量的繼承關係十分廣泛和簡單,這種繼承規則是什麼,最佳的繼承層次的規則又是什麼,怎樣優雅的設計繼承關係,子類能正確的對基類中的某些方法進行從新,這是LSP原則所要處理的問題。json

定義/特性
  • 子類必須可以替換掉他們的基類型:任何出現基類的地方均可以替換成子類而且客戶端程序不會改變基類行爲或者出現異常和錯誤,反之不行。
  • 客戶端程序只應該使用子類的抽象父類,這樣能夠實現動態綁定(php多態)
違反LSP原則

假設一個函數a,他的參數引用一個基類b,c是b的派生類,若是將c的對象做爲b類型傳遞給a,會致使a出現錯誤的行爲,那沒c就違法了LSP原則。segmentfault

/*
* 基類
*/
class Computer
{
    public function action($a, $b)
    {
        return $a+$b;
    }
}
/*
* 子類複習了父類方法,改變了action 的行爲
* 違反了LSP原則
*/
class Client extends Computer
{
    public function action($a, $b)
    {
        return $a-$b;
    }  
}

function run(Computer $computer, $a, $b) {
    return $computer->action($a, $b);
}

echo run((new Client()), 3, 5);
總結

LSP是OCP得以應用的最主要的原則之一,正是由於子類性的可替換行是的基類類型在無需修改的狀況下擴展功能。設計模式

依賴倒置原則(Depend Inversion Principle DIP)

問題

軟件開發設計中,老是傾向於建立一些高層模塊依賴底層模塊,底層模塊更改時直接影響到高層模塊,從而迫使他們改變。DIP原則描述了高層次模塊怎樣調用低層次模塊。函數

定義/特性
  • 高層模塊不該該依賴與底層模塊,兩者都應該依賴於抽象
  • 抽象不該該依賴與細節,細節應該依賴於抽象
代碼示例
interface Arithmetic
{
    //public function sub($a, $b);
}

class Client
{
    
    public function computer(Arithmetic $arithmetic, $a, $b)
    {
        return $arithmetic->add($a, $b);
    }
}

class Addition implements Arithmetic
{
    public function add($a, $b)
    {
        return $a + $b;
    }
}

$c = new Client();
echo $c->computer(new Addition(), 2, 3);

/*
client 高層類 依賴於Arithmetic,Addition底層實現細節類實現Arithmetic接口,達到兩者依賴於抽象接口的DIP設計原則
*/
總結

DIP原則就是每一個高層次模塊定義一個它所需服務的接口聲明,低層次模塊實現這個接口。每一個高層次類經過該抽象接口使用服務。

思考

面向對象軟件開發中合理的遵循設計原則能夠更好的設計代碼,減小沒必要要的錯誤,提升程序的可維護性,可擴展性和穩定性。

  • 單一職責(SRP)如何正確的劃分職責,類的職責單一提升代碼複用性,下降耦合性
  • 接口隔離(OCP)合理劃分接口功能,保證接口的專注性,純潔性,減小依賴關係
  • 里氏替換(LSP)合理利用類的繼承體系,保證真確的繼承關係不被破壞
  • 依賴倒置(DIP)抽象接口編程因爲抽象具體實現
  • 開放封閉(OCP)面向對象編程終極目標所達到的結果,類/模塊/系統的功能行爲可擴展,內部更改性是封閉的
相關文章
相關標籤/搜索