成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

S.O.L.I.D: PHP 面向?qū)ο笤O(shè)計(jì)的五個(gè)基準(zhǔn)原則

JayChen / 1091人閱讀

摘要:是首個(gè)個(gè)面向?qū)ο笤O(shè)計(jì)準(zhǔn)則的首字母縮寫,這些準(zhǔn)則是由提出的他更為人所熟知的名字是。單一功能原則開閉原則里氏替換原則接口隔離原則依賴反轉(zhuǎn)原則接下來讓我們看看每個(gè)原則,來了解為什么可以幫助我們成為更好的開發(fā)人員。

S.O.L.I.D?是?首個(gè) 5 個(gè)面向?qū)ο笤O(shè)計(jì)(OOD) 準(zhǔn)則的首字母縮寫 ,這些準(zhǔn)則是由 Robert C. Martin 提出的, 他更為人所熟知的名字是?Uncle Bob。

這些準(zhǔn)則使得開發(fā)出易擴(kuò)展、可維護(hù)的軟件變得更容易。也使得代碼更精簡(jiǎn)、易于重構(gòu)。同樣也是敏捷開發(fā)和自適應(yīng)軟件開發(fā)的一部分。

備注:?這不是一篇簡(jiǎn)單的介紹 "歡迎來到 _S.O.L.I.D" 的文章,這篇文章想要闡明?S.O.L.I.D?是什么。

S.O.L.I.D 意思是:

擴(kuò)展出來的首字母縮略詞看起來可能很復(fù)雜,實(shí)際上它們很容易理解。

S?- 單一功能原則

O?- 開閉原則

L?- 里氏替換原則

I?- 接口隔離原則

D?- 依賴反轉(zhuǎn)原則

接下來讓我們看看每個(gè)原則,來了解為什么 S.O.L.I.D 可以幫助我們成為更好的開發(fā)人員。

單一職責(zé)原則

縮寫是 S.R.P ,該原則內(nèi)容是:

一個(gè)類有且只能有一個(gè)因素使其改變,意思是一個(gè)類只應(yīng)該有單一職責(zé).

例如,假設(shè)我們有一些圖形,并且想要計(jì)算這些圖形的總面積.是的,這很簡(jiǎn)單對(duì)不對(duì)?

class Circle {
    public $radius;

    public function construct($radius) {
        $this->radius = $radius;
    }
}

class Square {
    public $length;

    public function construct($length) {
        $this->length = $length;
    }
}

首先,我們創(chuàng)建圖形類,該類的構(gòu)造方法初始化必要的參數(shù).接下來,創(chuàng)建AreaCalculator 類,然后編寫計(jì)算指定圖形總面積的邏輯代碼.

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?使用方法,我們只需簡(jiǎn)單的實(shí)例化這個(gè)類,并且傳遞一個(gè)圖形數(shù)組,在頁面底部展示輸出內(nèi)容.

$shapes = array(
    new Circle(2),
    new Square(5),
    new Square(6)
);

$areas = new AreaCalculator($shapes);

echo $areas->output();

輸出方法的問題在于,AreaCalculator 處理了數(shù)據(jù)輸出邏輯.因此,假如用戶希望將數(shù)據(jù)以 json 或者其他格式輸出呢?

所有邏輯都由 AreaCalculator 類處理,這恰恰違反了單一職責(zé)原則(SRP); AreaCalculator 類應(yīng)該只負(fù)責(zé)計(jì)算圖形的總面積,它不應(yīng)該關(guān)心用戶是想要json還是HTML格式數(shù)據(jù)。

因此,要解決這個(gè)問題,可以創(chuàng)建一個(gè) 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();

現(xiàn)在,無論你想向用戶輸出什么格式數(shù)據(jù),都由 SumCalculatorOutputter 類處理。

開閉原則
對(duì)象和實(shí)體應(yīng)該對(duì)擴(kuò)展開放,但是對(duì)修改關(guān)閉.

簡(jiǎn)單的說就是,一個(gè)類應(yīng)該不用修改其自身就能很容易擴(kuò)展其功能.讓我們看一下?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?方法能計(jì)算更多圖形的面積,我們就不得不添加更多的?if/else blocks?,然而這違背了開閉原則.

讓這個(gè) sum 方法變得更好的方式是將計(jì)算每個(gè)形狀面積的代碼邏輯移出 sum 方法,將其放進(jìn)各個(gè)形狀類中:

class Square {
    public $length;

    public function __construct($length) {
        $this->length = $length;
    }

    public function area() {
        return pow($this->length, 2);
    }
}

相同的操作應(yīng)該被用來處理?Circle?類, ?在類中添加一個(gè) area 方法。? 現(xiàn)在,計(jì)算任何形狀面積之和應(yīng)該像下邊這樣簡(jiǎn)單:

public function sum() {
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}

接下來我們可以創(chuàng)建另一個(gè)形狀類并在計(jì)算總和時(shí)傳遞它而不破壞我們的代碼。 然而現(xiàn)在又出現(xiàn)了另一個(gè)問題,我們?cè)趺茨苤纻魅??AreaCalculator?的對(duì)象實(shí)際上是一個(gè)形狀,或者形狀對(duì)象中有一個(gè)?area 方法?

接口編碼是實(shí)踐 S.O.L.I.D 的一部分,例如下面的例子中我們創(chuàng)建一個(gè)接口類,每個(gè)形狀類都會(huì)實(shí)現(xiàn)這個(gè)接口類:

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 方法中,我們可以檢查提供的形狀類的實(shí)例是否是 ShapeInterface 的實(shí)現(xiàn),否則我們就拋出一個(gè)異常:

public function sum() {
    foreach($this->shapes as $shape) {
        if(is_a($shape, "ShapeInterface")) {
            $area[] = $shape->area();
            continue;
        }

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}
里氏替換原則
如果對(duì)每一個(gè)類型為 T1的對(duì)象 o1,都有類型為 T2 的對(duì)象o2,使得以 T1定義的所有程序 P 在所有的對(duì)象 o1 都代換成 o2 時(shí),程序 P 的行為沒有發(fā)生變化,那么類型 T2 是類型 T1 的子類型。

這句定義的意思是說:每個(gè)子類或者衍生類可以毫無問題地替代基類/父類。

依然使用?AreaCalculator?類, 假設(shè)我們有一個(gè) VolumeCalculator?類,這個(gè)類繼承了 ?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(),
            ""
        ));
    }
}

如果我們運(yùn)行像這樣一個(gè)例子:

$areas = new AreaCalculator($shapes);
$volumes = new AreaCalculator($solidShapes);

$output = new SumCalculatorOutputter($areas);
$output2 = new SumCalculatorOutputter($volumes);

程序不會(huì)出問題, 但當(dāng)我們使用$output2 對(duì)象調(diào)用?HTML?方法時(shí) ,我們接收到一個(gè)?E_NOTICE?錯(cuò)誤,提示我們 數(shù)組被當(dāng)做字符串使用的錯(cuò)誤。

為了修復(fù)這個(gè)問題,只需:

public function sum() {
    // logic to calculate the volumes and then return and array of output
    return $summedData;
}

而不是讓VolumeCalculator?類的 sum 方法返回?cái)?shù)組。

$summedData 是一個(gè)浮點(diǎn)數(shù)、雙精度浮點(diǎn)數(shù)或者整型。

接口隔離原則
使用方(client)不應(yīng)該依賴強(qiáng)制實(shí)現(xiàn)不使用的接口,或不應(yīng)該依賴不使用的方法。

繼續(xù)使用上面的 shapes 例子,已知擁有一個(gè)實(shí)心塊,如果我們需要計(jì)算形狀的體積,我們可以在 ShapeInterface 中添加一個(gè)方法:

interface ShapeInterface {
    public function area();
    public function volume();
}

任何形狀創(chuàng)建的時(shí)候必須實(shí)現(xiàn) volume 方法,但是【平面】是沒有體積的,實(shí)現(xiàn)這個(gè)接口會(huì)強(qiáng)制的讓【平面】類去實(shí)現(xiàn)一個(gè)自己用不到的方法。

ISP?原則不允許這么去做,所以我們應(yīng)該創(chuàng)建另外一個(gè)擁有 volume 方法的SolidShapeInterface 接口去代替這種方式,這樣類似立方體的實(shí)心體就可以實(shí)現(xiàn)這個(gè)接口了:

interface ShapeInterface {
    public function area();
}

interface SolidShapeInterface {
    public function volume();
}

class Cuboid implements ShapeInterface, SolidShapeInterface {
    public function area() {
        //計(jì)算長(zhǎng)方體的表面積
    }

    public function volume() {
        // 計(jì)算長(zhǎng)方體的體積
    }
}

這是一個(gè)更好的方式,但是要注意提示類型時(shí)不要僅僅提示一個(gè) ShapeInterfaceSolidShapeInterface。
你能創(chuàng)建其它的接口,比如 ManageShapeInterface ,并在平面和立方體的類上實(shí)現(xiàn)它,這樣你能很容易的看到有一個(gè)用于管理形狀的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();
    }
}

現(xiàn)在在 AreaCalculator 類中,我們可以很容易地用 calculate替換對(duì)area 方法的調(diào)用,并檢查對(duì)象是否是 ManageShapeInterface 的實(shí)例,而不是 ShapeInterface 。

依賴倒置原則

最后,但絕不是最不重要的:

實(shí)體必須依賴抽象而不是具體的實(shí)現(xiàn).即高等級(jí)模塊不應(yīng)該依賴低等級(jí)模塊,他們都應(yīng)該依賴抽象.

這也許聽起來讓人頭大,但是它很容易理解.這個(gè)原則能夠很好的解耦,舉個(gè)例子似乎是解釋這個(gè)原則最好的方法:

class PasswordReminder {
    private $dbConnection;

    public function __construct(MySQLConnection $dbConnection) {
        $this->dbConnection = $dbConnection;
    }
}

首先?MySQLConnection?是低等級(jí)模塊,然而 PasswordReminder?是高等級(jí)模塊,但是根據(jù) S.O.L.I.D. 中?D?的解釋:依賴于抽象而不依賴與實(shí)現(xiàn), 上面的代碼段違背了這一原則,因?yàn)?PasswordReminder?類被強(qiáng)制依賴于?MySQLConnection?類.

稍后,如果你希望修改數(shù)據(jù)庫驅(qū)動(dòng),你也不得不修改?PasswordReminder?類,因此就違背了?Open-close principle

此?PasswordReminder?類不應(yīng)該關(guān)注你的應(yīng)用使用了什么數(shù)據(jù)庫,為了進(jìn)一步解決這個(gè)問題,我們「面向接口寫代碼」,由于高等級(jí)和低等級(jí)模塊都應(yīng)該依賴于抽象,我們可以創(chuàng)建一個(gè)接口:

interface DBConnectionInterface {
    public function connect();
}

這個(gè)接口有一個(gè)連接數(shù)據(jù)庫的方法,MySQLConnection?類實(shí)現(xiàn)該接口,在?PasswordReminder?的構(gòu)造方法中不要直接將類型約束設(shè)置為?MySQLConnection?類,而是設(shè)置為接口類,這樣無論你的應(yīng)用使用什么類型的數(shù)據(jù)庫,PasswordReminder?類都能毫無問題地連接數(shù)據(jù)庫,且不違背 開閉原則.?

class MySQLConnection implements DBConnectionInterface {
    public function connect() {
        return "Database connection";
    }
}

class PasswordReminder {
    private $dbConnection;

    public function __construct(DBConnectionInterface $dbConnection) {
        $this->dbConnection = $dbConnection;
    }
}

從上面一小段代碼,你現(xiàn)在能看出高等級(jí)和低等級(jí)模塊都依賴于抽象了。

總結(jié)

說實(shí)話,S.O.L.I.D 一開始似乎很難掌握,但只要不斷地使用和遵守其原則,它將成為你的一部分,使你的代碼易被擴(kuò)展、修改,測(cè)試,即使重構(gòu)也不容易出現(xiàn)問題。

文章轉(zhuǎn)自:https://learnku.com/php/t/28922 

更多文章:https://learnku.com/php/c/tra...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31593.html

相關(guān)文章

  • 深入理解JavaScript系列8:S.O.L.I.D五大原則之里氏替換原則

    摘要:前言本章我們要講解的是五大原則語言實(shí)現(xiàn)的第篇,里氏替換原則。因此,違反了里氏替換原則。與行為有關(guān),而不是繼承到現(xiàn)在,我們討論了和繼承上下文在內(nèi)的里氏替換原則,指示出的面向?qū)ο蟆? 前言 本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實(shí)現(xiàn)的第3篇,里氏替換原則LSP(The Liskov Substitution Principle )。英文原文:http://fre...

    susheng 評(píng)論0 收藏0
  • 初中級(jí)PHP面試基礎(chǔ)匯總

    摘要:如何實(shí)現(xiàn)持久化持久化,將在內(nèi)存中的的狀態(tài)保存到硬盤中,相當(dāng)于備份數(shù)據(jù)庫狀態(tài)持久化,持久化是通過保存服務(wù)器鎖執(zhí)行的寫狀態(tài)來記錄數(shù)據(jù)庫的。 showImg(https://segmentfault.com/img/bVbejmD?w=643&h=404); 這是我整理的一套面試題,老鐵們看看就當(dāng)復(fù)習(xí)了哦 概述 感覺現(xiàn)在發(fā)面試題有些冷門,就跟昨天德國那場(chǎng)似的,不過看看當(dāng)提前復(fù)習(xí)了。提前備戰(zhàn)。...

    B0B0 評(píng)論0 收藏0
  • 深入理解JavaScript系列6:S.O.L.I.D五大原則之單一職責(zé)

    摘要:,開始我們的第一篇單一職責(zé)。通過解耦可以讓每個(gè)職責(zé)工更加有彈性地變化。關(guān)于本文本文轉(zhuǎn)自大叔的深入理解系列。深入理解系列文章,包括了原創(chuàng),翻譯,轉(zhuǎn)載,整理等各類型文章,原文是大叔的一個(gè)非常不錯(cuò)的專題,現(xiàn)將其重新整理發(fā)布。 前言 Bob大叔提出并發(fā)揚(yáng)了S.O.L.I.D五大原則,用來更好地進(jìn)行面向?qū)ο缶幊蹋宕笤瓌t分別是: The Single Responsibility Princi...

    walterrwu 評(píng)論0 收藏0
  • 深入理解JavaScript系列9:S.O.L.I.D五大原則之接口隔離原則

    摘要:前言本章我們要講解的是五大原則語言實(shí)現(xiàn)的第篇,接口隔離原則。接口隔離原則和單一職責(zé)有點(diǎn)類似,都是用于聚集功能職責(zé)的,實(shí)際上可以被理解才具有單一職責(zé)的程序轉(zhuǎn)化到一個(gè)具有公共接口的對(duì)象。與我們下面討論的一些小節(jié)是里關(guān)于違反接口隔離原則的影響。 前言 本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實(shí)現(xiàn)的第4篇,接口隔離原則ISP(The Interface Segreg...

    piglei 評(píng)論0 收藏0
  • 深入理解JavaScript系列10:S.O.L.I.D五大原則之依賴倒置原則

    摘要:前言本章我們要講解的是五大原則語言實(shí)現(xiàn)的第篇,依賴倒置原則。當(dāng)應(yīng)用依賴倒置原則的時(shí)候,關(guān)系就反過來了。在當(dāng)靜態(tài)類型語言的上下文里討論依賴倒置原則的時(shí)候,耦合的概念包括語義和物理兩種。依賴倒置原則和依賴注入都是關(guān)注依賴,并且都是用于反轉(zhuǎn)。 前言 本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實(shí)現(xiàn)的第5篇,依賴倒置原則LSP(The Dependency Invers...

    chenjiang3 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<