摘要:六開(kāi)閉原則開(kāi)閉原則簡(jiǎn)介開(kāi)閉原則的英文名稱是,簡(jiǎn)稱。開(kāi)閉原則是面向?qū)ο笤O(shè)計(jì)中最基礎(chǔ)的設(shè)計(jì)原則,它指導(dǎo)我們?nèi)绾谓⒁粋€(gè)穩(wěn)定靈活的軟件系統(tǒng)。
面向?qū)ο蠡驹瓌t(3)- 最少知道原則與開(kāi)閉原則
面向?qū)ο蠡驹瓌t(1)- 單一職責(zé)原則與接口隔離原則
面向?qū)ο蠡驹瓌t(2)- 里式代換原則與依賴倒置原則
面向?qū)ο蠡驹瓌t(3)- 最少知道原則與開(kāi)閉原則
最少知識(shí)原則(Least KnowledgePrinciple,LKP)也稱為迪米特法則(Law of Demeter,LoD)。雖然名字不同,但描述的是同一個(gè)規(guī)則:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解。
通俗地講,一個(gè)類應(yīng)該對(duì)自己需要耦合或調(diào)用的類知道得最少,你(被耦合或調(diào)用的類)的內(nèi)部是如何復(fù)雜都和我沒(méi)關(guān)系,那是你的事情,我就知道你提供的這么多public方法,我就調(diào)用這么多,其他的我一概不關(guān)心。
2. 最少知道原則實(shí)現(xiàn) 只與直接關(guān)聯(lián)的類交流每個(gè)對(duì)象都必然會(huì)與其他對(duì)象有耦合關(guān)系,耦合關(guān)系的類型有很多,例如組合、聚合、依賴等。
出現(xiàn)在成員變量、方法的輸入輸出參數(shù)中的類稱為直接關(guān)聯(lián)的類,而出現(xiàn)在方法體內(nèi)部的類不屬于直接關(guān)聯(lián)的類。
下面舉例說(shuō)明如何才能做到只與直接關(guān)聯(lián)的類交流。
場(chǎng)景:老師想讓班長(zhǎng)清點(diǎn)女生的數(shù)量
Bad
/** * 老師類 * Class Teacher */ class Teacher { /** * 老師對(duì)班長(zhǎng)發(fā)布命令,清點(diǎn)女生數(shù)量 * @param GroupLeader $groupLeader */ public function command(GroupLeader $groupLeader) { // 產(chǎn)生一個(gè)女生群體 $girlList = new ArrayIterator(); // 初始化女生 for($i = 0; $i < 20; $i++){ $girlList->append(new Girl()); } // 告訴班長(zhǎng)開(kāi)始執(zhí)行清點(diǎn)任務(wù) $groupLeader->countGirls($girlList); } } /** * 班長(zhǎng)類 * Class GroupLeader */ class GroupLeader { /** * 清點(diǎn)女生數(shù)量 * @param ArrayIterator $girlList */ public function countGirls($girlList) { echo "女生數(shù)量是:", $girlList->count(), " "; } } /** * 女生類 * Class Girl */ class Girl { } $teacher= new Teacher(); //老師發(fā)布命令 $teacher->command(new GroupLeader()); // 女生數(shù)量是:20
上面實(shí)例中,Teacher類僅有一個(gè)直接關(guān)聯(lián)的類 -- GroupLeader。而Girl這個(gè)類就是出現(xiàn)在commond方法體內(nèi),因此不屬于與Teacher類直接關(guān)聯(lián)的類。
方法是類的一個(gè)行為,類竟然不知道自己的行為與其他類產(chǎn)生依賴關(guān)系,這是不允許的,違反了迪米特法則。
對(duì)程序進(jìn)行簡(jiǎn)單的修改,把 對(duì) $girlList 的初始化移出 Teacher 類,同時(shí)在 GroupLeader 中增加對(duì) Girl 的注入,避開(kāi) Teacher 類對(duì)陌生類 Girl 的訪問(wèn),降低系統(tǒng)間的耦合,提高系統(tǒng)的健壯性。
下面是改進(jìn)后的代碼:
Good
/** * 老師類 * Class Teacher */ class Teacher { /** * 老師對(duì)班長(zhǎng)發(fā)布命令,清點(diǎn)女生數(shù)量 * @param GroupLeader $groupLeader */ public function command(GroupLeader $groupLeader) { // 告訴班長(zhǎng)開(kāi)始執(zhí)行清點(diǎn)任務(wù) $groupLeader->countGirls(); } } /** * 班長(zhǎng)類 * Class GroupLeader */ class GroupLeader { private $_girlList; /** * 傳遞全班的女生進(jìn)來(lái) * GroupLeader constructor. * @param Girl[]|ArrayIterator $girlList */ public function __construct(ArrayIterator $girlList) { $this->_girlList = $girlList; } //清查女生數(shù)量 public function countGirls() { echo "女生數(shù)量是:", $this->_girlList->count(), " "; } } /** * 女生類 * Class Girl */ class Girl { } // 產(chǎn)生一個(gè)女生群體 $girlList = new ArrayIterator(); // 初始化女生 for($i = 0; $i < 20; $i++){ $girlList->append(new Girl()); } $teacher= new Teacher(); //老師發(fā)布命令 $teacher->command(new GroupLeader($girlList)); // 女生數(shù)量是:20關(guān)聯(lián)的類之間也要有距離
迪米特法則要求類“羞澀”一點(diǎn),盡量不要對(duì)外公布太多的public方法和非靜態(tài)的public變量,盡量?jī)?nèi)斂,多使用private、protected等訪問(wèn)權(quán)限。
一個(gè)類公開(kāi)的public屬性或方法越多,修改時(shí)涉及的面也就越大,變更引起的風(fēng)險(xiǎn)擴(kuò)散也就越大。因此,為了保持類間的距離,在設(shè)計(jì)時(shí)需要反復(fù)衡量:是否還可以再減少public方法和屬性,是否可以修改為private、protected等訪問(wèn)權(quán)限,是否可以加上final關(guān)鍵字等。
實(shí)例場(chǎng)景:實(shí)現(xiàn)軟件安裝的過(guò)程,其中first方法定義第一步做什么,second方法定義第二步做什么,third方法定義第三步做什么。
Bad
/** * 導(dǎo)向類 * Class Wizard */ class Wizard { /** * 第一步 * @return int */ public function first() { echo "執(zhí)行第一步安裝... "; // 模擬用戶點(diǎn)是或取消 return rand(0, 1); } /** * 第二步 * @return int */ public function second() { echo "執(zhí)行第二步安裝... "; // 模擬用戶點(diǎn)是或取消 return rand(0, 1); } /** * 第三步 * @return int */ public function third() { echo "執(zhí)行第三步安裝... "; // 模擬用戶點(diǎn)是或取消 return rand(0, 1); } } /** * 安裝軟件類 * Class InstallSoftware */ class InstallSoftware { /** * 執(zhí)行安裝軟件操作 * @param Wizard $wizard */ public function installWizard(Wizard $wizard) { $first = $wizard->first(); //根據(jù)first返回的結(jié)果,看是否需要執(zhí)行second if($first === 1){ $second = $wizard->second(); if($second === 1){ $third = $wizard->third(); if($third === 1){ echo "軟件安裝完成! "; } } } } } // 實(shí)例化軟件安裝類 $invoker = new InstallSoftware(); // 開(kāi)始安裝軟件 $invoker->installWizard(new Wizard()); // 運(yùn)行結(jié)果和隨機(jī)數(shù)有關(guān),每次的執(zhí)行結(jié)果都不相同
Wizard類把太多的方法暴露給InstallSoftware類,兩者的朋友關(guān)系太親密了,耦合關(guān)系變得異常牢固。如果要將Wizard類中的first方法返回值的類型由int改為boolean,就需要修改InstallSoftware類,從而把修改變更的風(fēng)險(xiǎn)擴(kuò)散開(kāi)了。因此,這樣的耦合是極度不合適的。
改進(jìn):在Wizard類中增加一個(gè)installWizard方法,對(duì)安裝過(guò)程進(jìn)行封裝,同時(shí)把原有的三個(gè)public方法修改為private方法。
/** * 導(dǎo)向類 * Class Wizard */ class Wizard { //第一步 private function first() { echo "執(zhí)行第1個(gè)方法... "; // 模擬用戶點(diǎn)是或取消 return rand(0, 1); } //第二步 private function second() { echo "執(zhí)行第2個(gè)方法... "; // 模擬用戶點(diǎn)是或取消 return rand(0, 1); } //第三個(gè)方法 private function third() { echo "執(zhí)行第3個(gè)方法... "; // 模擬用戶點(diǎn)是或取消 return rand(0, 1); } public function installWizard(){ $first = $this->first(); //根據(jù)first返回的結(jié)果,看是否需要執(zhí)行second if($first === 1){ $second = $this->second(); if($second === 1){ $third = $this->third(); if($third === 1){ echo "軟件安裝完成! "; } } } } } /** * 安裝軟件類 * Class InstallSoftware */ class InstallSoftware { /** * 執(zhí)行安裝軟件操作 * @param Wizard $wizard */ public function installWizard(Wizard $wizard) { $wizard->installWizard(); } } // 實(shí)例化軟件安裝類 $invoker = new InstallSoftware(); // 開(kāi)始安裝軟件 $invoker->installWizard(new Wizard()); // 運(yùn)行結(jié)果和隨機(jī)數(shù)有關(guān),每次的執(zhí)行結(jié)果都不相同
代碼改進(jìn)后,類間的耦合關(guān)系變?nèi)趿?,結(jié)構(gòu)也清晰了,變更引起的風(fēng)險(xiǎn)也變小了。
3. 最佳實(shí)踐在實(shí)際應(yīng)用中經(jīng)常會(huì)出現(xiàn)這樣一個(gè)方法:放在本類中也可以,放在其他類中也沒(méi)有錯(cuò),那怎么去衡量呢?
你可以堅(jiān)持這樣一個(gè)原則:如果一個(gè)方法放在本類中,既不增加類間關(guān)系,也對(duì)本類不產(chǎn)生負(fù)面影響,那就放置在本類中。
在實(shí)際應(yīng)用中,如果一個(gè)類跳轉(zhuǎn)兩次以上才能訪問(wèn)到另一個(gè)類,就需要想辦法進(jìn)行重構(gòu)了。
因?yàn)橐粋€(gè)系統(tǒng)的成功不僅僅是一個(gè)標(biāo)準(zhǔn)或是原則就能夠決定的,有非常多的外在因素決定,跳轉(zhuǎn)次數(shù)越多,系統(tǒng)越復(fù)雜,維護(hù)就越困難,所以只要跳轉(zhuǎn)不超過(guò)兩次都是可以忍受的,這需要具體問(wèn)題具體分析。
迪米特法則要求類間解耦,但解耦是有限度的,除非是計(jì)算機(jī)的最小單元——二進(jìn)制的0和1。那才是完全解耦,在實(shí)際的項(xiàng)目中,需要適度地考慮這個(gè)原則,別為了套用原則而做項(xiàng)目。
原則只是供參考,如果違背了這個(gè)原則,項(xiàng)目也未必會(huì)失敗,這就需要大家在采用原則時(shí)反復(fù)度量,不遵循是不對(duì)的,嚴(yán)格執(zhí)行就是“過(guò)猶不及”。
開(kāi)閉原則的英文名稱是 Open-Close Principle,簡(jiǎn)稱OCP。
開(kāi)閉原則是面向?qū)ο笤O(shè)計(jì)中最基礎(chǔ)的設(shè)計(jì)原則,它指導(dǎo)我們?nèi)绾谓⒁粋€(gè)穩(wěn)定、靈活的軟件系統(tǒng)。
開(kāi)閉原則的英文定義是
Software entities like classes,modules and functions should be open for extension but closed for modifications.
一個(gè)軟件實(shí)體如類、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。 其含義是說(shuō)一個(gè)軟件實(shí)體應(yīng)該通過(guò)擴(kuò)展來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)變化。
軟件實(shí)體包括以下幾個(gè)部分:
項(xiàng)目或軟件產(chǎn)品中按照一定的邏輯規(guī)則劃分的模塊。
抽象和類。
方法。
一個(gè)軟件產(chǎn)品只要在生命期內(nèi),都會(huì)發(fā)生變化,既然變化是一個(gè)既定的事實(shí),我們就應(yīng)該在設(shè)計(jì)時(shí)盡量適應(yīng)這些變化,以提高項(xiàng)目的穩(wěn)定性和靈活性,真正實(shí)現(xiàn)“擁抱變化”。開(kāi)閉原則告訴我們應(yīng)盡量通過(guò)擴(kuò)展軟件實(shí)體的行為來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有的代碼來(lái)完成變化,它是為軟件實(shí)體的未來(lái)事件而制定的對(duì)現(xiàn)行開(kāi)發(fā)設(shè)計(jì)進(jìn)行約束的一個(gè)原則。
2. 開(kāi)閉原則的優(yōu)點(diǎn) 提高復(fù)用率在面向?qū)ο蟮脑O(shè)計(jì)中,所有的邏輯都是從原子邏輯組合而來(lái)的,而不是在一個(gè)類中獨(dú)立實(shí)現(xiàn)一個(gè)業(yè)務(wù)邏輯。只有這樣代碼才可以復(fù)用,粒度越小,被復(fù)用的可能性就越大。
復(fù)用可以減少代碼量,避免相同的邏輯分散在多個(gè)角落,避免日后的維護(hù)人員為了修改一個(gè)微小的缺陷或增加新功能而要在整個(gè)項(xiàng)目中到處查找相關(guān)的代碼。
那怎么才能提高復(fù)用率呢?縮小邏輯粒度,直到一個(gè)邏輯不可再拆分為止。
一款軟件投產(chǎn)后,維護(hù)人員的工作不僅僅是對(duì)數(shù)據(jù)進(jìn)行維護(hù),還可能要對(duì)程序進(jìn)行擴(kuò)展,維護(hù)人員最樂(lè)意做的事情就是擴(kuò)展一個(gè)類,而不是修改一個(gè)類,甭管原有的代碼寫(xiě)得多么優(yōu)秀還是多么糟糕,讓維護(hù)人員讀懂原有的代碼,然后再修改,是一件很痛苦的事情,不要讓他在原有的代碼海洋里游弋完畢后再修改,那是對(duì)維護(hù)人員的一種折磨和摧殘。
面向?qū)ο箝_(kāi)發(fā)的要求萬(wàn)物皆對(duì)象,我們需要把所有的事物都抽象成對(duì)象,然后針對(duì)對(duì)象進(jìn)行操作,但是萬(wàn)物皆運(yùn)動(dòng),有運(yùn)動(dòng)就有變化,有變化就要有策略去應(yīng)對(duì)。怎么快速應(yīng)對(duì)呢?這就需要在設(shè)計(jì)之初考慮到所有可能變化的因素,然后留下接口,等待“可能”轉(zhuǎn)變?yōu)椤艾F(xiàn)實(shí)”。
2. 變化的三種類型 邏輯變化只變化一個(gè)邏輯,而不涉及其他模塊,比如原有的一個(gè)算法是 a*b+c,現(xiàn)在需要修改為 a*b*c,可以通過(guò)修改原有類中的方法的方式來(lái)完成,前提條件是所有依賴或關(guān)聯(lián)類都按照相同的邏輯處理。
子模塊變化一個(gè)模塊變化,會(huì)對(duì)其他的模塊產(chǎn)生影響,特別是一個(gè)低層次的模塊變化必然引起高層模塊的變化,因此在通過(guò)擴(kuò)展完成變化時(shí),高層次的模塊修改是必然的,剛剛的書(shū)籍打折處理就是類似的處理模塊,該部分的變化甚至?xí)鸾缑娴淖兓?/p> 視圖變化
可見(jiàn)視圖是提供給客戶使用的界面,該部分的變化一般會(huì)引起連鎖反應(yīng)。如果僅僅是界面上按鈕、文字的重新排布倒是簡(jiǎn)單,最司空見(jiàn)慣的是業(yè)務(wù)耦合變化,例如一個(gè)展示數(shù)據(jù)的列表,按照原有的需求是6列,突然有一天要增加1列,而且這一列要跨N張表,處理M個(gè)邏輯才能展現(xiàn)出來(lái),這樣的變化是比較恐怖的,但還是可以通過(guò)擴(kuò)展來(lái)完成變化,這就要看我們?cè)械脑O(shè)計(jì)是否靈活。
3. 開(kāi)閉原則的使用 抽象約束抽象是對(duì)一組事物的通用描述,沒(méi)有具體的實(shí)現(xiàn),也就表示它可以有非常多的可能性,可以跟隨需求的變化而變化。因此,通過(guò)接口或抽象類可以約束一組可能變化的行為,并且能夠?qū)崿F(xiàn)對(duì)擴(kuò)展開(kāi)放,其包含三層含義:
第一,通過(guò)接口或抽象類約束擴(kuò)展,對(duì)擴(kuò)展進(jìn)行邊界限定,不允許出現(xiàn)在接口或抽象類中不存在的public方法;
第二,參數(shù)類型、引用對(duì)象盡量使用接口或者抽象類,而不是實(shí)現(xiàn)類;
第三,抽象層盡量保持穩(wěn)定,一旦確定即不允許修改。
對(duì)變化的封裝包含兩層含義:
第一,將相同的變化封裝到一個(gè)接口或抽象類中;
第二,將不同的變化封裝到不同的接口或抽象類中,不應(yīng)該有兩個(gè)不同的變化出現(xiàn)在同一個(gè)接口或抽象類中。
封裝變化,也就是受保護(hù)的變化(protected variations),找出預(yù)計(jì)有變化或不穩(wěn)定的點(diǎn),我們?yōu)檫@些變化點(diǎn)創(chuàng)建穩(wěn)定的接口,準(zhǔn)確地講是封裝可能發(fā)生的變化,一旦預(yù)測(cè)到或“第六感”發(fā)覺(jué)有變化,就可以進(jìn)行封裝。
代碼使用PHP7.2語(yǔ)法編寫(xiě)
書(shū)籍接口
/** * Interface IBook * 書(shū)籍接口 */ interface IBook { /** * 書(shū)籍名稱 * @return mixed */ public function getName() : string ; /** * 書(shū)籍價(jià)格 * 這里把價(jià)格定義為int類型并不是錯(cuò)誤, * 在非金融類項(xiàng)目中對(duì)貨幣處理時(shí),一般取2位精度, * 通常的設(shè)計(jì)方法是在運(yùn)算過(guò)程中擴(kuò)大100倍,在需要展示時(shí)再縮小100倍,減少精度帶來(lái)的誤差。 * @return mixed */ public function getPrice() : int ; /** * 書(shū)籍作者 * @return mixed */ public function getAuthor() : string ; }
小說(shuō)類
/** * 小說(shuō)類 * Class NovelBook */ class NovelBook implements IBook { /** * 書(shū)籍名稱 * @var string $_name */ private $_name; /** * 書(shū)籍價(jià)格 * @var int $_price */ private $_price; /** * 書(shū)籍作者 * @var string $_author */ private $_author; /** * 通過(guò)構(gòu)造函數(shù)傳遞書(shū)籍信息 * @param string $name * @param int $price * @param string $author */ public function __construct(string $name, int $price, string $author) { $this->_name = $name; $this->_price = $price; $this->_author = $author; } /** * 獲取書(shū)籍名稱 * @return string */ public function getName() : string { return $this->_name; } /** * 獲取書(shū)籍價(jià)格 * @return int */ public function getPrice() : int { return $this->_price; } /** * 獲取書(shū)籍作者 * @return string */ public function getAuthor() : string { return $this->_author; } }
售書(shū)場(chǎng)景
// 產(chǎn)生一個(gè)書(shū)籍列表 $bookList = new ArrayIterator(); // 始化數(shù)據(jù) $bookList->append(new NovelBook("天龍八部",3200,"金庸")); $bookList->append(new NovelBook("巴黎圣母院",5600,"雨果")); echo "------書(shū)店賣出去的書(shū)籍記錄如下:-------- "; foreach($bookList as $book){ $price = $book->getPrice() / 100; echo <<getName()} 書(shū)籍作者: {$book->getAuthor()} 書(shū)籍價(jià)格: {$price} 元 --- TXT; }
------書(shū)店賣出去的書(shū)籍記錄如下:-------- 書(shū)籍名稱: 天龍八部 書(shū)籍作者: 金庸 書(shū)籍價(jià)格: 32 元 --- 書(shū)籍名稱: 巴黎圣母院 書(shū)籍作者: 雨果 書(shū)籍價(jià)格: 56 元 ---
一段時(shí)間之后,書(shū)店決定對(duì)小說(shuō)類書(shū)籍進(jìn)行打折促銷:所有40元以上的書(shū)籍9折銷售,其他的8折銷售。面對(duì)需求的變化,我們有兩種解決方案。
修改實(shí)現(xiàn)類NovelBook
直接修改NovelBook類中的getPrice()方法實(shí)現(xiàn)打折處理。該方法在項(xiàng)目有明確的章程(團(tuán)隊(duì)內(nèi)約束)或優(yōu)良的架構(gòu)設(shè)計(jì)時(shí),是一個(gè)非常優(yōu)秀的方法,但是該方法還是有缺陷的。例如采購(gòu)書(shū)籍人員也是要看價(jià)格的,由于該方法已經(jīng)實(shí)現(xiàn)了打折處理價(jià)格,因此采購(gòu)人員看到的也是打折后的價(jià)格,會(huì)因信息不對(duì)稱而出現(xiàn)決策失誤的情況。
通過(guò)擴(kuò)展實(shí)現(xiàn)變化
增加一個(gè)子類OffNovelBook,覆寫(xiě)getPrice方法,高層次的模塊通過(guò)OffNovelBook類產(chǎn)生新的對(duì)象,完成業(yè)務(wù)變化對(duì)系統(tǒng)的最小化開(kāi)發(fā),修改少,風(fēng)險(xiǎn)也小。
打折銷售的小說(shuō)類
/** * 打折銷售的小說(shuō)類 * Class OffNovelBook */ class OffNovelBook extends NovelBook { /** * 覆寫(xiě)獲取銷售價(jià)格方法 * * @return int */ public function getPrice() : int { //原價(jià) $originPrice = parent::getPrice(); if($originPrice > 40){ //原價(jià)大于40元,則打9折 $discountPrice = $originPrice * 90 / 100; }else{ $discountPrice = $originPrice * 80 / 100; } return $discountPrice; } }
打折售書(shū)場(chǎng)景
// 產(chǎn)生一個(gè)書(shū)籍列表 $bookList = new ArrayIterator(); // 始化數(shù)據(jù),實(shí)際項(xiàng)目中一般是由持久層完成 $bookList->append(new OffNovelBook("天龍八部",3200,"金庸")); $bookList->append(new OffNovelBook("巴黎圣母院",5600,"雨果")); echo "------書(shū)店賣出去的書(shū)籍記錄如下:------ "; foreach($bookList as $book){ $price = $book->getPrice() / 100; echo <<getName()} 書(shū)籍作者: {$book->getAuthor()} 書(shū)籍價(jià)格: {$price} 元 --- TXT; }
------書(shū)店賣出去的書(shū)籍記錄如下:------ 書(shū)籍名稱: 天龍八部 書(shū)籍作者: 金庸 書(shū)籍價(jià)格: 28.8 元 --- 書(shū)籍名稱: 巴黎圣母院 書(shū)籍作者: 雨果 書(shū)籍價(jià)格: 50.4 元 ---
又過(guò)了一段時(shí)間,書(shū)店新增加了計(jì)算機(jī)書(shū)籍,它不僅包含書(shū)籍名稱、作者、價(jià)格等信息,還有一個(gè)獨(dú)特的屬性:面向的是什么領(lǐng)域,也就是它的范圍,比如是和編程語(yǔ)言相關(guān)的,還是和數(shù)據(jù)庫(kù)相關(guān)的,等等。
增加一個(gè)IComputerBook接口,它繼承自IBook
/** * 計(jì)算機(jī)類書(shū)籍接口 * Interface IComputerBook */ interface IComputerBook extends IBook { /** * 計(jì)算機(jī)書(shū)籍增加一個(gè)范圍屬性 * @return string */ public function getScope() : string ; }
計(jì)算機(jī)書(shū)籍類
/** * 計(jì)算機(jī)書(shū)籍類 * Class ComputerBook */ class ComputerBook implements IComputerBook { /** * 書(shū)籍名稱 * @var string $_name */ private $_name; /** * 書(shū)籍價(jià)格 * @var int $_price */ private $_price; /** * 書(shū)籍作者 * @var string $_author */ private $_author; /** * 書(shū)籍范圍 * @var string $_scope */ private $_scope; /** * 通過(guò)構(gòu)造函數(shù)傳遞書(shū)籍信息 * ComputerBook constructor. * @param string $name * @param int $price * @param string $author * @param string $scope */ public function __construct(string $name, int $price, string $author, string $scope) { $this->_name = $name; $this->_price = $price; $this->_author = $author; $this->_scope = $scope; } /** * 獲取書(shū)籍名稱 * @return string */ public function getName() : string { return $this->_name; } /** * 獲取書(shū)籍價(jià)格 * @return int */ public function getPrice() : int { return $this->_price; } /** * 獲取書(shū)籍作者 * @return string */ public function getAuthor() : string { return $this->_author; } /** * 獲取書(shū)籍范圍 * @return string */ public function getScope() : string { return $this->_scope; } }
增加計(jì)算機(jī)書(shū)籍銷售
//產(chǎn)生一個(gè)書(shū)籍列表 $bookList = new ArrayIterator(); // 始化數(shù)據(jù),實(shí)際項(xiàng)目中一般是由持久層完成 $bookList->append(new OffNovelBook("天龍八部",3200,"金庸")); $bookList->append(new OffNovelBook("巴黎圣母院",5600,"雨果")); $bookList->append(new ComputerBook("高性能MySQL",4800,"Baron", "數(shù)據(jù)庫(kù)")); echo "------書(shū)店賣出去的書(shū)籍記錄如下:------ "; foreach($bookList as $book) { $price = $book->getPrice() / 100; echo <<getName()} 書(shū)籍作者: {$book->getAuthor()} 書(shū)籍價(jià)格: {$price} 元 --- TXT; }
------書(shū)店賣出去的書(shū)籍記錄如下:------ 書(shū)籍名稱: 天龍八部 書(shū)籍作者: 金庸 書(shū)籍價(jià)格: 28.8 元 --- 書(shū)籍名稱: 巴黎圣母院 書(shū)籍作者: 雨果 書(shū)籍價(jià)格: 50.4 元 --- 書(shū)籍名稱: 高性能MySQL 書(shū)籍作者: Baron 書(shū)籍價(jià)格: 48 元 ---
開(kāi)閉原則對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉,并不意味著不做任何修改,低層模塊的變更,必然要有高層模塊進(jìn)行耦合,否則就是一個(gè)孤立無(wú)意義的代碼片段。
參考文獻(xiàn):《設(shè)計(jì)模式之禪》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/31601.html
摘要:面向?qū)ο蠡驹瓌t單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t里式代換原則與依賴倒置原則面向?qū)ο蠡驹瓌t最少知道原則與開(kāi)閉原則一單一職責(zé)原則單一職責(zé)原則簡(jiǎn)介單一職責(zé)原則的英文名稱是,簡(jiǎn)稱。 面向?qū)ο蠡驹瓌t(1)- 單一職責(zé)原則與接口隔離原則 面向?qū)ο蠡驹瓌t(1)- 單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t(2)- 里式代換原則與依賴倒置原則面...
摘要:四依賴倒置原則依賴倒置原則簡(jiǎn)介依賴倒置原則的英文名稱是,簡(jiǎn)稱。依賴倒置原則的表現(xiàn)其實(shí)就是面向接口編程。依賴倒置原則的優(yōu)點(diǎn)減少類間的耦合性,提高系統(tǒng)的穩(wěn)定性。結(jié)合里氏替換原則使用接口負(fù)責(zé)定義屬性和方法,并且聲明與其他對(duì)象的依賴關(guān)系。 面向?qū)ο蠡驹瓌t(2)- 里式代換原則與依賴倒置原則 面向?qū)ο蠡驹瓌t(1)- 單一職責(zé)原則與接口隔離原則面向?qū)ο蠡驹瓌t(2)- 里式代換原則與依賴倒置原...
摘要:?jiǎn)我宦氊?zé)原則開(kāi)閉原則里氏替換原則依賴倒置原則接口隔離原則迪米特法則組合聚合復(fù)用原則單一職責(zé)原則高內(nèi)聚低耦合定義不要存在多于一個(gè)導(dǎo)致類變更的原因。建議接口一定要做到單一職責(zé),類的設(shè)計(jì)盡量做到只有一個(gè)原因引起變化。使用繼承時(shí)遵循里氏替換原則。 單一職責(zé)原則 開(kāi)閉原則 里氏替換原則 依賴倒置原則 接口隔離原則 迪米特法則 組合/聚合復(fù)用原則 單一職責(zé)原則(Single Responsi...
摘要:依賴倒置原則是個(gè)設(shè)計(jì)原則中最難以實(shí)現(xiàn)的原則,它是實(shí)現(xiàn)開(kāi)閉原則的重要途徑,依賴倒置原則沒(méi)有實(shí)現(xiàn),就別想實(shí)現(xiàn)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。 1、單一職能原則(Single Responsibility Principle, SRP) 定義 There should never be more than one reason for a class to change.應(yīng)該有且僅有一個(gè)原因引起類的...
摘要:里氏替換原則里氏代換原則面向?qū)ο笤O(shè)計(jì)的基本原則之一。里氏代換原則中說(shuō),任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。里氏代換原則是對(duì)開(kāi)閉原則的補(bǔ)充。而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn),所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。 showImg(https://segmentfault.com/img/bVbuXAu?w=640&h=361); 本文為本次系列文章的第一篇,接下...
閱讀 1221·2021-09-26 09:55
閱讀 3178·2019-08-30 15:55
閱讀 959·2019-08-30 15:53
閱讀 2291·2019-08-30 13:59
閱讀 2375·2019-08-29 13:08
閱讀 1102·2019-08-29 12:19
閱讀 3298·2019-08-26 13:41
閱讀 416·2019-08-26 13:24