码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式6个基本原则学习和总结

时间:2015-04-01 23:53:26      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:设计模式

网上这个相关内容有很多,但是大都说的太复杂了,所以这里我想用一篇来对这六个原则做以概括和总结,本文有部分内容摘自网络,由于本人水平有限,错误在所难免,如果我个人的理解有什么不对、不到位的地方,恳请各位高手指出。

1、单一职责原则(SRP:Single Responsibility Principle)

关键句:一个类只负责一个职责

看例子理解:

class Animal{  
    public void breathe(String animal){  
        System.out.println(animal+"用肺呼吸");  
    }  
}  
public class Client{  
    public static void main(String[] args){  
        Animal animal = new Animal();  
        animal.breathe("牛");  
        animal.breathe("羊");  
        animal.breathe("猪");  
    }  
}

运行结果不必多说,但是后面我们发现这个Animal类还会包括鱼,animal.breathe(“鱼”),显然常见的鱼并不是通过肺来呼吸的,这时我们就需要修改了。这里也就发生了职责扩散,职责扩撒就是由于某种原因,职责需要细分为职责1和职责2
我们可能会这样改:

(1)修改Animal类的breathe方法:(不建议的修改方式)

    public void breathe(String animal){  
        if("鱼".equals(animal)){  
            System.out.println(animal+"用鳃呼吸");  
        }else{  
            System.out.println(animal+"用肺呼吸");  
        }  
    } 

上面这种修改方式是很简单,但是存在进一步的风险,假如往后的某一天,我们知道了某些鱼还会用其他方式呼吸,这样我们又要进一步修改这个类,这时我们的修改可能就会影响到 牛 羊 等呼吸方式。这也违背了单一职责原则,所以这种方式不可取。

(2)增加Animal类新的breathe方法:(根据实际情况确定是否使用)

class Animal{  
    public void breathe(String animal){  
        System.out.println(animal+"用肺呼吸");  
    }  

    public void breathe2(String animal){  
        System.out.println(animal+"用鳃呼吸");  
    }  
}  

上面修改方式,在方法级别符合单一职责原则,因为它并没有动原来方法的代码。类级别是违背单一职责原则。这种方式在类中方法足够少的情况下可以考虑使用。

(3)细分Animal类:(标准的,不违反单一职责的方式)

class Terrestrial{  
    public void breathe(String animal){  
        System.out.println(animal+"用肺呼吸");  
    }  
}  
class Aquatic{  
    public void breathe(String animal){  
        System.out.println(animal+"用鳃呼吸");  
    }  
} 

上面修改的方式,修改花销是很大的,除了将原来的Animal类分解之外,还需要修改客户端。即:

        Terrestrial terrestrial = new Terrestrial();  
        terrestrial.breathe("牛");
        Aquatic aquatic = new Aquatic();  
        aquatic.breathe("鱼"); 

所以,上面(2)(3)修改方式需要综合项目的复杂程度等选择使用(如何选择使用上面已经有说到)

2、里氏替换原则(LSP:Liskov Substitution Principle)

关键句:子类可以扩展父类的功能,但不能改变父类原有的功能

这个原则主要是针对继承而言的,因为继承往往有这样的含义:父类中已经实现的方法,其实也就是针对该类部分行为的描述和定义。而若子类对这个方法进行任意修改,那么就会造成继承体系的破坏。
实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的代码的可复用性会比较差。

如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

3、依赖倒置原则(DIP:Dependency Inversion Principle)

关键句:细节应该依赖抽象,面向接口编程

看例子理解这个原则:

class Book{  
    public String getContent(){  
        return "很久很久以前有一个阿拉伯的故事……";  
    }  
}  

class Mother{  
    public void narrate(Book book){  
        System.out.println("妈妈开始讲故事");  
        System.out.println(book.getContent());  
    }  
}  

public class Client{  
    public static void main(String[] args){  
        Mother mother = new Mother();  
        mother.narrate(new Book());  
    }  
}

上面的例子有一个Mother类,有一个Book类,Book类作为一个参数传入到Mother类中,这样,Mother类就完成了对Book类的读取。
但是这时候,要增加一个需求,Mother要读报纸,与Book类相似,Newspaper类如下:

class Newspaper{  
    public String getContent(){  
        return "林书豪38+7领导尼克斯击败湖人……";  
    }  
}

这时候我们就发现,Mother类的narrate方法只接受Book的对象,并不会读Newspaper,所以我们考虑如下修改方式:
(1)Mother类增加narrate1方法,传入Newspaper。
绝对的坑爹设计,以后如果还有 杂志、小说要读,那是不是会有更多方法需要增加,Mother类需要不断修改。
(2)面向接口编程,引入接口IReader。

interface IReader{  
    public String getContent();  
}

Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了,代码修改为:

class Newspaper implements IReader {  
    public String getContent(){  
        return "林书豪17+9助尼克斯击败老鹰……";  
    }  
}  
class Book implements IReader{  
    public String getContent(){  
        return "很久很久以前有一个阿拉伯的故事……";  
    }  
}  

class Mother{  
    public void narrate(IReader reader){  
        System.out.println("妈妈开始讲故事");  
        System.out.println(reader.getContent());  
    }  
}  

public class Client{  
    public static void main(String[] args){  
        Mother mother = new Mother();  
        mother.narrate(new Book());  
        mother.narrate(new Newspaper());  
    }  
}

遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。

4、接口隔离原则(ISP:Interface Segregation Princeple)

关键句:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上

这个原则解决这样一个问题:
类A通过接口 I 依赖类B,类C通过同样的接口 I 依赖类D。这是接口 I 有类B和类D的方法,但是对于类B和类D他们彼此并不需要对方的方法。这时,接口 I 的就过于臃肿,因此需要拆分。

这个原则比较好理解,这里不再用例子解释。需要记住的就是建立单一接口,不要建立庞大臃肿的接口,适度细化接口,接口中的方法尽量少。

5、迪米特法则(Law of Demeter)/最少知道原则(LKP: Least Knowledge Principle)

关键句:一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合

这个原则也可以这样理解,类仅与直接的朋友通信。(直接的朋友包括:成员变量、方法参数、方法返回值)

举个例子,现在有如下程序:

//总公司员工
class Employee{
    private String id;
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return id;
    }
}

//分公司员工
class SubEmployee{
    private String id;
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return id;
    }
}

现在需要输出整个公司员工的Id,我们可能会这样写代码:

class SubCompanyManager{
    public List<SubEmployee> getAllEmployee(){
        List<SubEmployee> list = new ArrayList<SubEmployee>();
        for(int i=0; i<100; i++){
            SubEmployee emp = new SubEmployee();
            //为分公司人员按顺序分配一个ID
            emp.setId("分公司"+i);
            list.add(emp);
        }
        return list;
    }
}

class CompanyManager{
    public List<Employee> getAllEmployee(){
        List<Employee> list = new ArrayList<Employee>();
        for(int i=0; i<30; i++){
            Employee emp = new Employee();
            //为总公司人员按顺序分配一个ID
            emp.setId("总公司"+i);
            list.add(emp);
        }
        return list;
    }

    public void printAllEmployee(SubCompanyManager sub){
        List<SubEmployee> list1 = sub.getAllEmployee();
        for(SubEmployee e:list1){
            System.out.println(e.getId());
        }

        List<Employee> list2 = this.getAllEmployee();
        for(Employee e:list2){
            System.out.println(e.getId());
        }
    }
}

在分公司SubCompanyManager类中创建方法getAllEmployee()
在总公司CompanyManager类中创建方法getAllEmployee()和printAllEmployee()。
根据迪米特法则,ComanyManager与Employee是直接朋友,但是与SubEmployee并不是直接朋友。这样总公司与分公司逻辑上是耦合的了。所以这种方式不可取,需要修改:

class SubCompanyManager{
    public List<SubEmployee> getAllEmployee(){
        List<SubEmployee> list = new ArrayList<SubEmployee>();
        for(int i=0; i<100; i++){
            SubEmployee emp = new SubEmployee();
            //为分公司人员按顺序分配一个ID
            emp.setId("分公司"+i);
            list.add(emp);
        }
        return list;
    }
    public void printEmployee(){
        List<SubEmployee> list = this.getAllEmployee();
        for(SubEmployee e:list){
            System.out.println(e.getId());
        }
    }
}

class CompanyManager{
    public List<Employee> getAllEmployee(){
        List<Employee> list = new ArrayList<Employee>();
        for(int i=0; i<30; i++){
            Employee emp = new Employee();
            //为总公司人员按顺序分配一个ID
            emp.setId("总公司"+i);
            list.add(emp);
        }
        return list;
    }

    public void printAllEmployee(SubCompanyManager sub){
        sub.printEmployee();
        List<Employee> list2 = this.getAllEmployee();
        for(Employee e:list2){
            System.out.println(e.getId());
        }
    }
}

上面方法就有效的降低了类之间耦合,仅与直接的朋友进行了通信。

6、开闭原则(OCP:Open Closed Principle)

关键句:类、模块、功能应该对扩展开放,对修改关闭

开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;
开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。

之前提到的里氏代换原则、依赖倒置原则、接口隔离原则,还有我们知道的抽象类、接口等等,都可以看作是开闭原则的实现方法。

设计模式6个基本原则学习和总结

标签:设计模式

原文地址:http://blog.csdn.net/hnulwt/article/details/44787795

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!