面向對象的5個設計原則

在面向對象中饺著,類是基本單位箫攀,各種設計都是圍繞著類來進行的∮姿ィ可以說匠童,類與類之間的關系,構成了設計模式的大部分內(nèi)容塑顺。

經(jīng)典的設計模式有23種汤求,學習設計模式推薦GOF經(jīng)典以及《敏捷軟件開發(fā)——原則、方法與實踐》

1.單一職責原則

背景故事:亞當.斯密 對制針業(yè)做過一個分工生產(chǎn)效率的例子(《國富論》第一章)严拒。每個工人專學一種專門的業(yè)務扬绪,相比每個人獨立完成整個工作流程,達到數(shù)千倍的效率裤唠。在面向對象設計中挤牛,分工理論就是單一職責原則(Single Pesponsibility Prineiple, SRP)。 就一個類而言种蘸,應該只有一個引起它變化的原因墓赴。此原則容易理解卻不容易做到,主要是怎樣設計類與類的方法界定問題航瞭。

單一職責的兩個含義:

1. 避免相同的職責分散到不同的類中
2. 避免一個類承擔太多職責

為什么要遵循SRP:

  1. 可以減少類之間的耦合:當需求變化時诫硕,只修改一個類,從而隔離了變化刊侯。
  2. 提高類的復用性

單一職責使得組件可以方便的拆卸和組裝

SPR實際開發(fā)中的應用:用工廠模式來實現(xiàn)不同數(shù)據(jù)庫操作類章办。

工廠模式(Factory) 允許你在代碼執(zhí)行時實例化對象。之所以被稱為工廠模式,是因為它負責“生產(chǎn)”對象藕届。

下面案例是使用工廠模式實現(xiàn)一個數(shù)據(jù)庫操作類挪蹭,主要是講解工廠模式。

Db_Adapter.php

//先定義一個適配器接口
interface Db_Adapter
{
    /**
     * 數(shù)據(jù)庫連接
     * @param $config 數(shù)據(jù)庫配置
     * @return resource
     */
    public function connection($config);
    
    /**
     * 執(zhí)行數(shù)據(jù)庫查詢
     * @param string $query 數(shù)據(jù)庫查詢SQL字符串
     * @param mixed $handle 連接對象
     * @return resource
     */
    public function query($query, $handle);
}

Db_Adapter_Mysql.php

// 定義mysql 操作類
require_once 'Db_Adapter.php';

class Db_Adapter_Mysql implements Db_Apdater
{
    private $_dbLink;       //數(shù)據(jù)庫連接標識
    
    /**
     * 數(shù)據(jù)庫連接函數(shù)
     * 
     * @param $config 數(shù)據(jù)庫配置
     * @throws Db_Exception
     * @return resource
     */
    public function connection($config)
    {
        if ($this->_dbLink = @mysql_connect($config->host . (empty($config->port) ? '' : ':' . $config->port), $config->user, $config->password, true)) {
            if (@mysql_select_db($config->database, $this->_dbLink)) {
                if ($config->charset) {
                    mysql_query("SET NAMES '{$config->charset}'", $this->_dbLink);
                }
                
                return $this->_dbLink;
            }
        }
        
        /** 數(shù)據(jù)庫異常 */
        throw new Db_Exception(@mysql_error($this->_dbLink));
    }
    
    /**
     * 執(zhí)行數(shù)據(jù)庫查詢
     * 
     * @param string $query 數(shù)據(jù)庫查詢SQL 字符串
     * @param mixed $handle 連接對象
     * @return resource
    public function query($query, $handle) 
    {
        if ($resource = @mysql_query($query, $handle) {
            return $resource;
        }
    }
}

Db_Adapter_sqlite.php

// 定義sqlite 操作類
require_once 'Db_Adapter.php';

class Db_Adapter_sqlite implements Db_Adapter
{
    private $_dbLink;       // 數(shù)據(jù)庫連接標識
    
    public function connection($config)
    {
        if ($this->_dbLink = sqlite_open($config->file, 0666, $error)) {
            return $this->_dbLink;
        }
        
        /** 數(shù)據(jù)庫異常 */
        throw new Db_Exception($error);
    }
    
    /**
     * 執(zhí)行數(shù)據(jù)庫查詢
     * 
     * @param string $query 數(shù)據(jù)庫查詢 SQL 字符串
     * @param mixed $handle 連接對象
     * @return resource
     */
    public function query($query, $handle)
    {
        if ($resource = @sqlite_query($query, $handle)) {
            return $resource;
        }
    }
}

sqlFactory.php

// 定義工廠類
class sqlFactory
{
    public static function factory($type)
    {
        if (incluede_once '/Db_Adapter_' . $type . '.php') {
            $classname = 'Db_Adapter_' . $type;
            return new $classname;
        } else {
            throw new Exception ('Driver not found');
        }
    }
}

test.php

// 調(diào)空上面的庫
require 'sqlFactory.php';

$dn = sqlFactory::factory('Mysql');
$db = sqlFactory::factory('sqlite');

print_r($dn);
print_r($db);

在上面的例子中休偶,sqlFactory類是一個工廠類梁厉,用戶可以通過調(diào)用sqlFactory類中的factory方法和它的參數(shù)來動態(tài)的初始化不同類型的數(shù)據(jù)庫的操作類,Db_Adapter.php定義了數(shù)據(jù)庫操作類的規(guī)范踏兜,這樣讓所有數(shù)據(jù)庫的操作完全一樣词顾,如果更換了數(shù)據(jù)庫,只需要用工廠類生成不同的對象就可以實現(xiàn)不同數(shù)據(jù)庫的操作庇麦,而不需要去修改數(shù)據(jù)庫操作的業(yè)務代碼计技。

SRP是最簡單的原則之一,也是最難做好的原則之一山橄。

一些應該遵循的做法:

  1. 根據(jù)業(yè)務流程垮媒,把業(yè)務對象提煉出來。 如果業(yè)務流程鏈路太復雜航棱,就把這個業(yè)務對象分離為多個單一業(yè)務對象睡雇。當業(yè)務鏈標準化后,對業(yè)務對象的內(nèi)部情況做進一部處理饮醇。把第一次標準化視為最高層抽象它抱,第二次視為次高層抽象,以此類推朴艰,直到“恰如其分”的設計層次观蓄。
  2. 職責的分類需要注意。 有業(yè)務職責祠墅,還要脫離業(yè)務的抽象職責侮穿,從認識業(yè)務到抽象算法是一個層層遞進的過程。就好比命令模式中的顧客毁嗦,服務員和廚師的職責亲茅,作為老板的你需要規(guī)劃好各自的職責范圍,既要防止越俎代庖狗准,也要防止互相推諉克锣。

2.接口隔離原則

設計應用程序的時候,如果一個模塊包含多個子模塊腔长,那么我們應該小心對該模塊做出抽象袭祟。

接口隔離原則(Interface Segregation Principle, ISP): 客戶端不應該被強迫實現(xiàn)不會使用的接口,應該把胖接口中的方法分組饼酿,然后用多個接口代替它榕酒,每個接口服務于一個子模塊胚膊。簡單的說故俐,就是使用多個專門的接口比使用單個接口要好得多想鹰。

ISP主要觀點:

  1. 一個類對另外一個類的依賴性應當是建立在最小的接口上。
  • ISP 可以達到不強迫客戶依賴于他們不使用的方法药版,接口的實現(xiàn)類應該只呈現(xiàn)為單一職責的角色(遵守SRP原則)辑舷。
  • ISP 還可降低客戶端之間的相互影響——當某個客戶程序要求提供新的職責(需求變更)而迫使接口發(fā)生改變時,影響到其他客戶程序的可能性會是最小的槽片。
  1. 客戶端程序不應該依賴它不需要的接口方法何缓。

ISP強調(diào)的是接口對客戶端的承諾越少越好,并且要做到專一还栓。

接口污染

過于臃腫的接口設計是對接口的污染碌廓。接口污染就是為接口添加不必要的職責,如果開發(fā)人員在接口中增加一個新功能的主要目的只是減少接口實現(xiàn)類的數(shù)目剩盒,則此設計導致接口被不斷的“污染” 并 “變胖”谷婆。

上面的結構就是一個標準的胖接口,客戶A需要服務A辽聊、客戶B需要服務B纪挎、客戶C需要服務C,當客戶A的需求變化后跟匆,也會影響到客戶B和C异袄。

使用接口隔離后如下:


這里寫圖片描述

使用接口隔離原則后,雖然接口數(shù)量變多玛臂,但是當客戶A的需求變更后烤蜕,就不會再影響到客戶B和C。

考慮下如何實現(xiàn)禽類動物的接口迹冤?他們都有翅膀讽营,都是卵生動物,都有羽毛叁巨,但是鳥會飛斑匪,鴨子不會飛。

可以把它們共性設計為一個基礎類锋勺,針對部分特有的屬性蚀瘸,可以另外定義一個接口,讓具有這些特殊屬性的同時實現(xiàn)基礎接口和特殊接口庶橱。

對于接口污染贮勃,可以考慮以下兩種處理方式:

  • 利用委托分離接口
  • 利用多繼承分離接口

3.開放 - 封閉原則

  • Open(Open for extension) 模塊的行為必須是開放的、支持擴展的苏章,而不是僵化的寂嘉。
  • Closed (Closed for modification) 在模塊的功能進行擴展時奏瞬,不應該影響或大規(guī)模影響已有的程序模塊。

一個模塊在擴展性方面應該是開放的泉孩,在更改性方面應該是封閉的硼端。

在生活中,開放 - 封閉原則最簡單的例子就是電腦寓搬,你可以很容易對電腦功能進行擴展灼捂,并且不對現(xiàn)有功能或者只是少量影響現(xiàn)有功能售碳。

該原則的核心是想是對抽象編程至耻,而不是具體編程渔期,因為抽象相對穩(wěn)定。讓類依賴于固定的抽象唾琼,這樣的修改就是封閉的兄春,通過面向對象的繼承和多態(tài)機制,可以實現(xiàn)對抽象體的繼承锡溯,通過覆寫其方法改變固有行為赶舆,實現(xiàn)新的擴展方法,所以對于擴展就是開放的趾唱。

  • 在設計方面充分應用 抽象 和 封裝 的思想涌乳。
  • 在系統(tǒng)功能編程實現(xiàn)方面應用面向接口的編程。

4.替換原則

87年 Liskov女士在OOPSLA大會上發(fā)表《Data Abstraction and Hierarchy》中提出甜癞,主要闡述有關繼承的一些原則夕晓,也稱為里氏替換原則。02年Robert C.Martin出版的《Agile Software Development Principles Pattems and Practices》中對里氏替換原則進行簡化為子類必須能夠替換成它們的基類悠咱。

里氏替換原則(Liskov Substiution Principle, LSP)定義以及主要思想:子類型必須能夠替換掉它們的父類型蒸辆、并出現(xiàn)在父類能夠出現(xiàn)的任何地方。

LSP解決如果正確進行繼承設計和合理地應用繼承機制:

  • 如何正確地進行繼承方面的設計?
  • 最佳的繼承層次如何獲得?
  • 怎樣避免所設計的類層次陷入不符合OCP原則的狀況?

如何遵守替換原則:

  • 父類的方法都要在子類中實現(xiàn)或者重寫, 派生類只實現(xiàn)其抽象類中聲明的方法, 而不應當給出多余的方法定義或實現(xiàn)析既。
  • 在客戶端程序中只應該出現(xiàn)父類對象躬贡,而不是直接使用子類對象, 這樣可以實現(xiàn)運行期綁定(多態(tài)綁定)。

php對LSP的支持不如其它語言, 因為其沒有向上轉型等概念, 只能通過一些其它手段來實現(xiàn)眼坏。

5.依賴倒置原則

將依賴關系倒置為依賴接口:

  • 上層模塊不應該依賴下層模塊, 它們共同依賴于一個抽象拂玻。
  • 抽象不能依賴于具體, 具體應該要依賴于抽象

employee.php

<?php
interface employee
{
    public function working();
}

class teacher implements employee
{
    public function working()
    {
        echo 'teaching...';
    }
}

class cooder implements employee
{
    public function working()
    {
        echo 'coding...';
    }
}

class workA
{
    public function work()
    {
        $teacher = new teacher();
        $teacher->working();
    }
}

class workB
{
    private $e;
    
    public function set(employee $e)
    {
        $this->e = $e;
    }

    public function work()
    {
        $this->e->working();
    }
}

$worka = new workA();
$worka->work();

$workb = new workB();
$workb->set(new teacher());
$workb->work();

classA中,work方法依賴teacher實現(xiàn); classB中宰译,通過工廠模式實現(xiàn)了一定的解耦檐蚜,但是這還是硬編碼,進一步擴展可以將依賴寫入配置文件中沿侈,專門由一個程序檢測配置是否正確以及加載配置中所依賴的實現(xiàn)闯第,這個檢測程序,就叫IOC容器缀拭。

IOC(Inversion of Control) 是依賴倒置原則(Dependence Inversion Principle, DIP)的同義詞咳短。

  • DI: 依賴注入
  • DS: 依賴查找

以上幾個概念都是IOC的兩種實現(xiàn)填帽。

依賴倒置的核心原則是解耦,如果脫離這個最原始的原則咙好,那就是本末倒置篡腌。

如何滿足DIP:

  1. 每個較高層次類都為它所需要的服務提出一個接口聲明, 較低層次實現(xiàn)這個接口
  2. 每個高層類都通過該抽象接口使用服務
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市敷扫,隨后出現(xiàn)的幾起案子哀蘑,更是在濱河造成了極大的恐慌诚卸,老刑警劉巖葵第,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異合溺,居然都是意外死亡卒密,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門棠赛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哮奇,“玉大人,你說我怎么就攤上這事睛约《Ψ” “怎么了?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵辩涝,是天一觀的道長贸伐。 經(jīng)常有香客問我,道長怔揩,這世上最難降的妖魔是什么捉邢? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮商膊,結果婚禮上伏伐,老公的妹妹穿的比我還像新娘。我一直安慰自己晕拆,他們只是感情好藐翎,可當我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著实幕,像睡著了一般吝镣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茬缩,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天赤惊,我揣著相機與錄音,去河邊找鬼凰锡。 笑死未舟,一個胖子當著我的面吹牛圈暗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裕膀,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼员串,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昼扛?” 一聲冷哼從身側響起寸齐,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抄谐,沒想到半個月后渺鹦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡蛹含,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年毅厚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浦箱。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡吸耿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酷窥,到底是詐尸還是另有隱情咽安,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布蓬推,位于F島的核電站妆棒,受9級特大地震影響,放射性物質發(fā)生泄漏拳氢。R本人自食惡果不足惜募逞,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望馋评。 院中可真熱鬧放接,春花似錦、人聲如沸留特。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜕青。三九已至苟蹈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間右核,已是汗流浹背慧脱。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贺喝,地道東北人菱鸥。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓宗兼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氮采。 傳聞我的和親對象是個殘疾皇子殷绍,可洞房花燭夜當晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 單一職責原則 (SRP) 全稱 SRP , Single Responsibility Principle 單一職...
    米莉_L閱讀 1,769評論 2 5
  • 前言 設計模式六大原則網(wǎng)上資料比較多比較亂,本文將網(wǎng)上的一些好的資料做一下整理鹊漠,以便隨時翻閱主到。友情提示,設計模式雖...
    簡單的土豆閱讀 1,438評論 0 10
  • 面向對象的3個基本要素: 封裝躯概、繼承登钥、多態(tài) 面向對象的5個基本設計原則: 單一職責原則(Single-Respos...
    badcyc閱讀 866評論 0 4
  • 程序設計的6大原則: 單一職責原則里氏替換原則依賴倒置原則接口隔離原則迪米特法則開閉原則 從根本學好,理解為什么要...
    silencefun閱讀 2,414評論 1 4
  • 設計模式六大原則 設計模式六大原則(1):單一職責原則 定義:不要存在多于一個導致類變更的原因楞陷。通俗的說怔鳖,即一個類...
    viva158閱讀 771評論 0 1