大話設(shè)計(jì)模式——六大原則(SOLID)

S:單一職責(zé)原則(Single responsibility principle)

  • 解釋:它規(guī)定一個(gè)類(lèi)應(yīng)該只有一個(gè)發(fā)生變化的原因。單一職責(zé)原則是最簡(jiǎn)單的面對(duì)對(duì)象設(shè)計(jì)原則,它用于控制類(lèi)的粒度大小。在軟件系統(tǒng)中,一個(gè)類(lèi)(大到模塊,小到方法)承擔(dān)的職責(zé)越多,它被復(fù)用的可能性就越小,而且一個(gè)類(lèi)承擔(dān)的職責(zé)過(guò)多,就相當(dāng)于將這些職責(zé)耦合在一起,當(dāng)其中一個(gè)職責(zé)變化時(shí),可能會(huì)影響其他職責(zé)的運(yùn)作,因此要將這些職責(zé)進(jìn)行分離,將不同的職責(zé)封裝在不同的類(lèi)中,即將不同的變化原因封裝在不同的類(lèi)中,如果多個(gè)職責(zé)總是同時(shí)發(fā)生改變則可將它們封裝在同一類(lèi)中。

  • 優(yōu)點(diǎn):類(lèi)的職責(zé)變得單一,復(fù)雜度降低、可讀性提高、可維護(hù)性提高、擴(kuò)展性提高、降低了變更引起的風(fēng)險(xiǎn)。在<<大話設(shè)計(jì)模式>>第三章中的情景中,手機(jī)的職責(zé)較多,可以拍照、打電話、玩游戲等等,但是如果單論拍照手機(jī)比不過(guò)單反、玩游戲也比不過(guò)游戲機(jī)...所以,單一職責(zé)往往更加優(yōu)秀,類(lèi)能夠職責(zé)分離,才能夠更容易維護(hù)、擴(kuò)展、復(fù)用...

  • 栗子:網(wǎng)上確實(shí)有一些例子,但總是感覺(jué)有點(diǎn)牽強(qiáng),或者說(shuō)不能說(shuō)服我,大部分例子其實(shí)就是為了舉例而舉例,和實(shí)際開(kāi)發(fā)有點(diǎn)脫節(jié)。這里貼一個(gè)網(wǎng)上常見(jiàn)例子:JAVA單一職責(zé)原則。當(dāng)然,還是舉一個(gè)我覺(jué)得不錯(cuò)的例子,在JavaWeb項(xiàng)目中,分包經(jīng)常是分出model、service、dao等等,這里以用戶登錄注冊(cè)功能舉例。用戶輸入賬戶密碼,提交驗(yàn)證數(shù)據(jù)庫(kù)的用戶表,或者提交注冊(cè)信息,數(shù)據(jù)庫(kù)進(jìn)行保存,實(shí)現(xiàn)這個(gè)功能,一般需要以下類(lèi),每個(gè)類(lèi)單獨(dú)承擔(dān)一個(gè)職責(zé)。UserDao只用于數(shù)據(jù)庫(kù)用戶的查詢和保存,UserService用于校驗(yàn)來(lái)登錄注冊(cè),User是實(shí)體對(duì)象。
    在實(shí)體對(duì)象層定義類(lèi)User:

    public class User {  
        private String username;  
        private String userpass;   
        private int role; 
        //…………各個(gè)屬性的get、set方法 
    }
    

    在業(yè)務(wù)邏輯層上定義類(lèi)UserService

    public class UserService {  
        private UserDao userDao = new UserDao();  
        //驗(yàn)證用戶名和密碼   
        public boolean CheckUser(String name,String pass) {   
            boolean flag=false;   
            User user =userDao.getUserByName(name);   
            if(user!=null&&user.getUsername().equals(pass)) {      
                flag=true;      
            }    
            return flag;   
        }      
        //注冊(cè)新用戶   
        public void registUser(User user) {        
            if(userDao.getUserByName(user.getUsername()) !=null) {
                System.out.println("用戶名已存在");    
                return;   
            }      
            if(UserDao.addUser(user)) { 
                //注冊(cè)成功    
            }else {    
                //注冊(cè)失敗   
            }      
        }   
    } 
    

    在數(shù)據(jù)訪問(wèn)層定義類(lèi)UserDao:

    public class UserDao extends BaseDao {  
        //返回所有用戶  
        public List<User> getAllUser() {       
            List<User> userList = new ArrayList<User>();   
            ......
            //訪問(wèn)數(shù)據(jù)庫(kù)    
            return userList;   
        }   
        //根據(jù)用戶名查找用戶   
        public User getUserByName(String name) {   
            User user=null;   
            String sql="SELECT * FROM userdetail WHERE username=?";    
            ...
            //查找相應(yīng)用戶名的用戶    
            return user;  
        }   
        //添加新用戶   
        public boolean addUser(User user) {    
            //返回true 表示成功   
        } 
    } 
    

O:開(kāi)放封閉原則(Open Close Principle)

  • 解釋:開(kāi)閉,對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。盡量通過(guò)擴(kuò)展軟件實(shí)體來(lái)解決需求變化,而不是通過(guò)修改已有的代碼來(lái)完成變化。在開(kāi)發(fā)軟件的過(guò)程中,因?yàn)樽兓?、升?jí)和維護(hù)等原因需要對(duì)軟件原有的代碼進(jìn)行修改,可能會(huì)將錯(cuò)誤引入原本已經(jīng)測(cè)試過(guò)的舊代碼中,破壞原有的系統(tǒng),因此,當(dāng)軟件需求變化時(shí),我們應(yīng)盡量運(yùn)用擴(kuò)展的方式來(lái)實(shí)現(xiàn)變化,而不是修改原來(lái)的代碼。
  • 優(yōu)點(diǎn):開(kāi)閉原則是面向?qū)ο笤O(shè)計(jì)的核心所在。遵循這個(gè)原則可以帶來(lái)面向?qū)ο蠹夹g(shù)所聲稱(chēng)的巨大好處,也就是可維護(hù)、可擴(kuò)展、可復(fù)用、靈活性好。開(kāi)閉原則可以說(shuō)是一種軟件設(shè)計(jì)的總綱, 一個(gè)軟件產(chǎn)品在生命周期內(nèi),都會(huì)發(fā)生變化,既然變化是一個(gè)既定的事實(shí),我們就應(yīng)該在設(shè)計(jì)的時(shí)候盡量適應(yīng)這些變化,以提高項(xiàng)目的穩(wěn)定性和靈活性。開(kāi)閉原則具有理想主義的色彩,它是面向?qū)ο笤O(shè)計(jì)的終極目標(biāo),其他原則可以認(rèn)為是開(kāi)閉原則的實(shí)現(xiàn)方法。
  • 栗子:在我們大話設(shè)計(jì)模式——簡(jiǎn)單工廠模式文章中(建議看一下),計(jì)算器程序的設(shè)計(jì)一開(kāi)始完全采用的是面向過(guò)程的思路,當(dāng)我們需要增加其他運(yùn)算時(shí),我們必須對(duì)客戶端代碼進(jìn)行修改,增加新的swith-case分支來(lái)實(shí)現(xiàn)需求的變化。這種方式就是修改,而我們采用工廠模式進(jìn)行重構(gòu)時(shí),當(dāng)需要進(jìn)行新的運(yùn)算時(shí),僅僅需要新建運(yùn)算類(lèi)繼承Operation抽象類(lèi),重寫(xiě)getResult()方法。具體代碼參考文章原文,這里貼一下UML圖便于理解。
    計(jì)算器工廠模式實(shí)現(xiàn)

L:迪米特法則(Law of Demeter)

  • 解釋:迪米特法則又叫作最少知道原則(Least Knowledge Principle 簡(jiǎn)寫(xiě)LKP),就是說(shuō)一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解,不和陌生人說(shuō)話。一個(gè)類(lèi)對(duì)自己依賴的類(lèi)知道的越少越好。自從我們接觸編程開(kāi)始,就知道了軟件編程的總的原則:低耦合,高內(nèi)聚。無(wú)論是面向過(guò)程編程還是面向?qū)ο缶幊?,只有使各個(gè)模塊之間的耦合盡量的低,才能提高代碼的復(fù)用率。

  • 優(yōu)點(diǎn)與缺點(diǎn):迪米特法則的初衷在于降低類(lèi)之間的耦合。由于每個(gè)類(lèi)盡量減少對(duì)其他類(lèi)的依賴,因此,很容易使得系統(tǒng)的功能模塊功能獨(dú)立,相互之間不存在(或很少有)依賴關(guān)系。迪米特法則不希望類(lèi)之間建立直接的聯(lián)系。如果真的有需要建立聯(lián)系,也希望能通過(guò)它的友元類(lèi)來(lái)轉(zhuǎn)達(dá)。因此,應(yīng)用迪米特法則有可能造成的一個(gè)后果就是:系統(tǒng)中存在大量的中介類(lèi),這些類(lèi)之所以存在完全是為了傳遞類(lèi)之間的相互調(diào)用關(guān)系——這在一定程度上增加了系統(tǒng)的復(fù)雜度。

  • 栗子:門(mén)面模式和中介模式是迪米特法則的倆個(gè)典型設(shè)計(jì)模式。其實(shí)在大話設(shè)計(jì)模式——策略模式一文中,引入策略模式的好處體現(xiàn)在客戶端代碼里,簡(jiǎn)單工廠需要認(rèn)識(shí)倆個(gè)類(lèi):Charge和ChargeFactory,而策略模式僅需要客戶端認(rèn)識(shí)ChargeContext一個(gè)類(lèi)即可,耦合度更低,這就符合了迪米特法則。這里也再舉一個(gè)網(wǎng)上簡(jiǎn)單的例子,我覺(jué)得一般,可以參考。迪米特有個(gè)重要法則,只和 朋友類(lèi) 交流。什么是朋友類(lèi)?出現(xiàn)在成員變量、方法的輸入輸出參數(shù)中的類(lèi)稱(chēng)為成員朋友類(lèi),而出現(xiàn)在方法體內(nèi)部的類(lèi)不屬于朋友類(lèi)。
    老師類(lèi)定義了一個(gè)方法,可以發(fā)號(hào)施令讓隊(duì)長(zhǎng)(GroupLeader)數(shù)人頭數(shù),中間在方法的實(shí)現(xiàn)中,突然需要依賴Girl類(lèi),這就違反了迪米特法則。

    public class Teacher {
        public void commond(GroupLeader groupLeader) {
            List<Girl> listGirls = new ArrayList<Girl>();
            for (int i = 0; i < 20; i++) {
                    listGirls.add(new Girl());
            }
            groupLeader.countGirls(listGirls);
        }
    }
    

    所以,我們進(jìn)行重寫(xiě),將添加20個(gè)Girl對(duì)象的事情在GroupLeader中去做。

    public class Teacher {
        public void commond(GroupLeader groupLeader) {
            groupLeader.countGirls();
        }
    }
    
    public class GroupLeader {
        private List<Girl> listGirls;
        public GroupLeader(List<Girl> _listGirls) {
            this.listGirls = _listGirls;
        }
          
        public void countGirls() {
            System.out.println("女生數(shù)量是:" + listGirls.size());
        }
    }
    

    這個(gè)例子怎么說(shuō)呢,我覺(jué)得不好或者牽強(qiáng)的地方就是這20個(gè)Girl對(duì)象構(gòu)造的有點(diǎn)別扭,正常實(shí)際開(kāi)發(fā)時(shí),都是從別處獲取到的比如調(diào)用Xxx類(lèi)的getAllGirls()方法來(lái)獲得的,而我們的GroupLeader完全可以充當(dāng)這個(gè)角色,所以自然而然代碼就會(huì)寫(xiě)成遵從迪米特法則的形式。So,作為單純講解迪米特法則的栗子,我覺(jué)得還OK,但考慮到實(shí)際開(kāi)發(fā),我覺(jué)得有失公正...

L:里氏替換原則(Liskov Substitution Principle)

待更新...

I:接口隔離原則(Interface Segregation Principle)

待更新...

D:依賴倒置原則(Dependence Inversion Principle)

待更新...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容