首页

java软件设计六大原则Principle

标签:java设计原则,六大原则,Principle,软件设计,开闭,里氏替换,依赖倒置,接口隔离,迪米特法则     发布时间:2015-12-15   

一、设计六大原则

六大原则分别是单一职责原则(Single Responsibility Principle,或者 SRP)、里氏替换原则(Liskov Substitution Principle , 或者 LSP)、依赖倒置原则(Dependence Inversion Principle ,或者DIP)、接口隔离原则(Interface Segregation Principle,或者ISP)、迪米特法则(Law Of Demeter,或者 LoD)、开闭原则(Open Close Principle、或者OCP)。

二、如何遵守

对于软件设计在遵循六大原则需要平衡,而不是是否问题(必须遵守),任何事情都是过犹不及,所以对于实际情况需要灵活运用,控制在一个合理的范围之内,就是一个不错的设计,如下图所示

java软件设计六大原则Principle

下面列出几种实际项目中常见的几种可能,如下图所示

java软件设计六大原则Principle

三、分别举例说明

1. 单一职责原则(Single Responsibility Principle,或者 SRP)

定义:一个类对应一项职责(避免修改一个多职能冗杂类,而是对单一类部分修改来的简单)

举例:动物园原来只有马牛羊,做一个标记吃草动物属性系统

class  Animal{@b@    public static void mark(String animal_name){@b@        System.out.println(animal_name+"是素食动物");@b@    }@b@}@b@public class  Test{@b@    public static void  main(String[] args){@b@        Animal.mark("马");@b@        Animal.mark("牛");@b@        Animal.mark("羊");@b@    }@b@}

结果是

   马是素食动物

   牛是素食动物

   羊是素食动物

举例:动物园新进肉食动物老虎,需扩展原标记系统

class VegAnimal{@b@    public static void mark(String animal_name){@b@        System.out.println(animal_name+"是素食动物");@b@    }@b@}@b@class CarAnimal{@b@   public static void mark(String animal_name){@b@        System.out.println(animal_name+"是肉食动物");@b@    }@b@}@b@public class  Test{@b@    public static void  main(String[] args){@b@        VegAnimal.mark("马");@b@        VegAnimal.mark("牛");@b@        VegAnimal.mark("羊");@b@        CarAnimal.mark("老虎");@b@    }@b@}

结果是

   马是素食动物

   牛是素食动物

    羊是素食动物

    老虎是肉食动物

如只修改原有方法判断逻辑,后期的三次、四次修改会造成Animal的mark标记方法异常的复杂,不利于后期的扩展延伸。该原则降低逻辑复杂度;增加类可读性,提高系统可维护性;降低系统各个模块功能间影响性。

2. 里氏替换原则(Liskov Substitution Principle)

定义:子类可以扩展父类的功能,但不能改变父类原有的功能(任何基类可以出现的地方,子类一定可以出现)

举例:设计一个BaseMath数学类计算两个数之和

public class BaseMath{@b@@b@    public static int  funAdd(int a,int b){@b@        return a+b;@b@    }@b@}

现在要扩展数学类方法,使它满足先加再减50的计算

public class ExtMath extends BaseMath{@b@  @b@    public  static int funAll(int a,int b){@b@        return funAdd(a,b)-50;@b@    }@b@}

对于上面二次需求我们继承扩展了基类方法,而不是重构funAdd方法,避免其他模块基于原来funAdd方法逻辑实现的重构

3. 依赖倒置原则(Dependence Inversion Principle)

定义:面向接口编程,上层和底层模块都应该依赖抽象定义(接口或抽象类),细节依赖抽象

举例:完成爸爸给儿子教育(读书、读报纸及各种杂志)

interface IReader{@b@    public String getContent();@b@}@b@@b@class Newspaper implements IReader {@b@    public String getContent(){@b@        return "xxx新开游乐场……";@b@    }@b@}@b@class Book implements IReader{@b@    public String getContent(){@b@        return "从前有个山……";@b@    }@b@}@b@@b@class Farther{@b@    public void narrate(IReader reader){@b@        System.out.println("爸爸开始讲故事");@b@        System.out.println(reader.getContent());@b@    }@b@}@b@@b@public class Client{@b@    public static void main(String[] args){@b@        Farther farther= new Farther();@b@        farther.narrate(new Book());@b@        farther.narrate(new Newspaper());@b@@b@    }@b@}

如果上面对于杂志、书不设计IReader接口,Farther类需针对每种类型的读物实现一个方法,设计实现特别的低级累赘,传递依赖关系有三种方式,以上的例子中使用的方法是接口传递,另外还有两种传递方式:构造方法传递和setter方法传递,相信用过Spring框架的,对依赖的传递方式一定不会陌生。    在实际编程中,我们一般需要做到如下3点:    a.低层模块尽量都要有抽象类或接口,或者两者都有。    b.变量的声明类型尽量是抽象类或接口。    c.使用继承时遵循里氏替换原则。总之,依赖倒置原则就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。

4. 接口隔离原则(Interface Segregation Principle)

定义:不同接口需要保证相对的独立性,不应该出现两个或以上接口方法有好多重合(降低依赖耦合)

java软件设计六大原则Principle

java软件设计六大原则Principle

5. 迪米特法则(Law Of Demeter)

定义:类与类之间的关系越密切,耦合度越大,尽量降低类与类之间关系密切程度,使其相对独立,从而降低类之间耦合度(解耦)

举例:现集团公司,下属单位有分公司和直属部门,要打印出所有下属单位的员工ID。先来看违反迪米特法则的设计

//总公司员工@b@class Employee{@b@    private String id;@b@    public void setId(String id){@b@        this.id = id;@b@    }@b@    public String getId(){@b@        return id;@b@    }@b@}@b@//分公司员工@b@class SubEmployee{@b@    private String id;@b@    public void setId(String id){@b@        this.id = id;@b@    }@b@    public String getId(){@b@        return id;@b@    }@b@}@b@class SubCompanyManager{@b@    public List<SubEmployee> getAllEmployee(){@b@        List<SubEmployee> list = new ArrayList<SubEmployee>();@b@        for(int i=0; i<100; i++){@b@            SubEmployee emp = new SubEmployee();@b@            //为分公司人员按顺序分配一个ID@b@            emp.setId("分公司"+i);@b@            list.add(emp);@b@        }@b@        return list;@b@    }@b@}@b@class CompanyManager{@b@@b@    public List<Employee> getAllEmployee(){@b@        List<Employee> list = new ArrayList<Employee>();@b@        for(int i=0; i<30; i++){@b@            Employee emp = new Employee();@b@            //为总公司人员按顺序分配一个ID@b@            emp.setId("总公司"+i);@b@            list.add(emp);@b@        }@b@        return list;@b@    }@b@    @b@    public void printAllEmployee(SubCompanyManager sub){@b@        List<SubEmployee> list1 = sub.getAllEmployee();@b@        for(SubEmployee e:list1){@b@            System.out.println(e.getId());@b@        }@b@@b@        List<Employee> list2 = this.getAllEmployee();@b@        for(Employee e:list2){@b@            System.out.println(e.getId());@b@        }@b@    }@b@}@b@@b@public class Client{@b@    public static void main(String[] args){@b@        CompanyManager e = new CompanyManager();@b@        e.printAllEmployee(new SubCompanyManager());@b@    }@b@}

根据迪米特法设计,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合,具体如下

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


6. 开闭原则(Open Close Principle)

定义:对扩展开放,对修改关闭

  • ◆ 相关内容