PHP 物件導向設計的五個基準原則
S.O.L.I.D 是首個5 個物件導向設計(OOD) 準則的首字母縮寫 ,這些準則是由Robert C. Martin 提出的, 他更為人所熟知的名字是Uncle Bob。
這些準則使得開發出易擴充、可維護的軟體變得更容易。也使得程式碼更精簡、易於重構。同樣也是敏捷開發和自適應軟體開發的一部分。
備註: 這不是一篇簡單的介紹"歡迎來到_S.O.L.I.D" 的文章,這篇文章想要闡明S.O.L.I.D 是什麼。 (相關教程推薦:php影片教學)
S.O.L.I.D 意思是:
#擴展出來的首字母縮寫看起來可能很複雜,實際上它們很容易理解。
- S - 單一功能原則
- O - 開閉原則
- ##L - 里氏替換原則
- I - 介面隔離原則
- D - 依賴反轉原則
S.R.P ,該原則內容是:
一個類別有且只能有一個因素使其改變,意思是一個類別只應該有單一職責.例如,假設我們有一些圖形,並且想要計算這些圖形的總面積.是的,這很簡單對不對?
class Circle { public $radius; public function construct($radius) { $this->radius = $radius; } } class Square { public $length; public function construct($length) { $this->length = $length; } }
AreaCalculator 類,然後編寫計算指定圖形總面積的邏輯程式碼.
class AreaCalculator { protected $shapes; public function __construct($shapes = array()) { $this->shapes = $shapes; } public function sum() { // logic to sum the areas } public function output() { return implode('', array( "", "Sum of the areas of provided shapes: ", $this->sum(), "" )); } }
AreaCalculator 使用方法,我們只需簡單的實例化這個類,並且傳遞一個圖形數組,在頁面底部展示輸出內容.
$shapes = array( new Circle(2), new Square(5), new Square(6) ); $areas = new AreaCalculator($shapes); echo $areas->output();
AreaCalculator 處理了資料輸出邏輯.因此,如果使用者希望將資料以 json 或其他格式輸出呢?
所有邏輯都由AreaCalculator 類別處理,這只是違反了單一職責原則(SRP); AreaCalculator 類別應該只負責計算圖形的總面積,它不應該關心使用者是想要json還是HTML格式資料。
因此,要解決這個問題,可以建立一個SumCalculatorOutputter 類,並使用它來處理所需的顯示邏輯,以處理所有圖形的總面積該如何顯示。
SumCalculatorOutputter 類別的工作方式如下:
$shapes = array( new Circle(2), new Square(5), new Square(6) ); $areas = new AreaCalculator($shapes); $output = new SumCalculatorOutputter($areas); echo $output->JSON(); echo $output->HAML(); echo $output->HTML(); echo $output->JADE();
SumCalculatorOutputter 類別處理。
開閉原則物件和實體應該對擴展開放,但是對修改關閉.簡單的說就是,一個類別應該不用修改其本身就能很容易擴展其功能.讓我們來看看
AreaCalculator 類,特別是 sum 方法.
public function sum() { foreach($this->shapes as $shape) { if(is_a($shape, 'Square')) { $area[] = pow($shape->length, 2); } else if(is_a($shape, 'Circle')) { $area[] = pi() * pow($shape->radius, 2); } } return array_sum($area); }
sum 方法能計算更多圖形的面積,我們就不得不添加更多的if/else blocks ,然而這違背了開閉原則.
讓這個sum 方法變得更好的方式是將計算每個形狀面積的程式碼邏輯移出sum 方法,將其放進各個形狀類別:
class Square { public $length; public function __construct($length) { $this->length = $length; } public function area() { return pow($this->length, 2); } }
Circle 類別, 在類別中加入一個area 方法。 現在,計算任何形狀面積總和應該像下邊這樣簡單:
public function sum() { foreach($this->shapes as $shape) { $area[] = $shape->area(); } return array_sum($area); }
AreaCalculator 的物件實際上是一個形狀,或者形狀物件中有一個 area 方法?
介面編碼是實踐S.O.L.I.D 的一部分,例如下面的例子中我們建立一個介面類,每個形狀類別都會實作這個介面類別:
interface ShapeInterface { public function area(); } class Circle implements ShapeInterface { public $radius; public function __construct($radius) { $this->radius = $radius; } public function area() { return pi() * pow($this->radius, 2); } }
AreaCalculator 的sum 方法中,我們可以檢查提供的形狀類別的實例是否是ShapeInterface 的實現,否則我們就拋出一個例外:
public function sum() { foreach($this->shapes as $shape) { if(is_a($shape, 'ShapeInterface')) { $area[] = $shape->area(); continue; } throw new AreaCalculatorInvalidShapeException; } return array_sum($area); }
如果對每一個類型為T1的物件o1,都有一個類型為T2 的物件o2,使得以T1定義的所有程式P 在所有的物件o1 都代換成o2時,程式P 的行為沒有發生變化,那麼類型T2 是類型T1 的子類型。
这句定义的意思是说:每个子类或者衍生类可以毫无问题地替代基类/父类。
依然使用 AreaCalculator 类, 假设我们有一个 VolumeCalculator 类,这个类继承了 AreaCalculator 类:
class VolumeCalculator extends AreaCalulator { public function construct($shapes = array()) { parent::construct($shapes); } public function sum() { // logic to calculate the volumes and then return and array of output return array($summedData); } }
SumCalculatorOutputter 类:
class SumCalculatorOutputter { protected $calculator; public function __constructor(AreaCalculator $calculator) { $this->calculator = $calculator; } public function JSON() { $data = array( 'sum' => $this->calculator->sum(); ); return json_encode($data); } public function HTML() { return implode('', array( '', 'Sum of the areas of provided shapes: ', $this->calculator->sum(), '' )); } }
如果我们运行像这样一个例子:
$areas = new AreaCalculator($shapes); $volumes = new AreaCalculator($solidShapes); $output = new SumCalculatorOutputter($areas); $output2 = new SumCalculatorOutputter($volumes);
程序不会出问题, 但当我们使用$output2 对象调用 HTML 方法时 ,我们接收到一个 E_NOTICE 错误,提示我们 数组被当做字符串使用的错误。
为了修复这个问题,只需:
public function sum() { // logic to calculate the volumes and then return and array of output return $summedData; }
而不是让VolumeCalculator 类的 sum 方法返回数组。
$summedData
是一个浮点数、双精度浮点数或者整型。
接口隔离原则
使用方(client)不应该依赖强制实现不使用的接口,或不应该依赖不使用的方法。
继续使用上面的 shapes
例子,已知拥有一个实心块,如果我们需要计算形状的体积,我们可以在 ShapeInterface 中添加一个方法:
interface ShapeInterface { public function area(); public function volume(); }
任何形状创建的时候必须实现 volume 方法,但是【平面】是没有体积的,实现这个接口会强制的让【平面】类去实现一个自己用不到的方法。
ISP 原则不允许这么去做,所以我们应该创建另外一个拥有 volume
方法的SolidShapeInterface
接口去代替这种方式,这样类似立方体的实心体就可以实现这个接口了:
interface ShapeInterface { public function area(); } interface SolidShapeInterface { public function volume(); } class Cuboid implements ShapeInterface, SolidShapeInterface { public function area() { //计算长方体的表面积 } public function volume() { // 计算长方体的体积 } }
这是一个更好的方式,但是要注意提示类型时不要仅仅提示一个 ShapeInterface 或 SolidShapeInterface。
你能创建其它的接口,比如 ManageShapeInterface ,并在平面和立方体的类上实现它,这样你能很容易的看到有一个用于管理形状的api。例:
interface ManageShapeInterface { public function calculate(); } class Square implements ShapeInterface, ManageShapeInterface { public function area() { /Do stuff here/ } public function calculate() { return $this->area(); } } class Cuboid implements ShapeInterface, SolidShapeInterface, ManageShapeInterface { public function area() { /Do stuff here/ } public function volume() { /Do stuff here/ } public function calculate() { return $this->area() + $this->volume(); } }
现在在 AreaCalculator 类中,我们可以很容易地用 calculate替换对area 方法的调用,并检查对象是否是 ManageShapeInterface 的实例,而不是 ShapeInterface 。
依赖倒置原则
最后,但绝不是最不重要的:
实体必须依赖抽象而不是具体的实现.即高等级模块不应该依赖低等级模块,他们都应该依赖抽象.
这也许听起来让人头大,但是它很容易理解.这个原则能够很好的解耦,举个例子似乎是解释这个原则最好的方法:
class PasswordReminder { private $dbConnection; public function __construct(MySQLConnection $dbConnection) { $this->dbConnection = $dbConnection; } }
首先 MySQLConnection 是低等级模块,然而 PasswordReminder 是高等级模块,但是根据 S.O.L.I.D. 中 D 的解释:依赖于抽象而不依赖与实现, 上面的代码段违背了这一原则,因为 PasswordReminder 类被强制依赖于 MySQLConnection 类.
稍后,如果你希望修改数据库驱动,你也不得不修改 PasswordReminder 类,因此就违背了 Open-close principle.
此 PasswordReminder 类不应该关注你的应用使用了什么数据库,为了进一步解决这个问题,我们「面向接口写代码」,由于高等级和低等级模块都应该依赖于抽象,我们可以创建一个接口:
interface DBConnectionInterface { public function connect(); }
这个接口有一个连接数据库的方法,MySQLConnection 类实现该接口,在 PasswordReminder 的构造方法中不要直接将类型约束设置为 MySQLConnection 类,而是设置为接口类,这样无论你的应用使用什么类型的数据库,PasswordReminder 类都能毫无问题地连接数据库,且不违背 开闭原则.
class MySQLConnection implements DBConnectionInterface { public function connect() { return "Database connection"; } } class PasswordReminder { private $dbConnection; public function __construct(DBConnectionInterface $dbConnection) { $this->dbConnection = $dbConnection; } }
从上面一小段代码,你现在能看出高等级和低等级模块都依赖于抽象了。
总结
说实话,S.O.L.I.D 一开始似乎很难掌握,但只要不断地使用和遵守其原则,它将成为你的一部分,使你的代码易被扩展、修改,测试,即使重构也不容易出现问题。
相关PHP面向对象视频教程推荐:《PHP面向对象视频教程》

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。