设计原则

  1. 单一责任原则[Single Responsibility Principle--SRP]java

    定义:There should never be more than one reason for a class to change.编程

    意译:不该该有超过一个因素引发类的改变设计模式

    优势:框架

           一个类负责一个职责,复杂度下降ide

           提升可读性,可维护性工具

           若是类能很好的遵循单一责任原则,将会显著减小变动引发的联动变化,下降了变动引起的风险学习

    缺点:this

           由于代码颗粒度变细,将可能引发类膨胀
    spa

    理解:"定义描述很简单,责任划分很困难",这个应该是比较大众的对这一原则的见解,"责任"并非一个量化的指标,"单一责任"也彻底是依靠人为划分的,而影响这一行为的又有不少因素,如何在相应的项目环境中准确的定义出"单一责任"的概念是咱们须要一直努力学习的,这不是一触而就的,须要大量的设计经验,但起码,咱们应当作到:面向接口编程,因此,尽管在不少条件下类不可能作到单一责任,但咱们应该尽量的把接口设计成符合单一责任原则....net

    示例:考虑这样一个场景,一个责任混杂的工具类,其中方法A依赖于外部类库L,那么,当把这个工具类做为模块提供给外部模块使用时,那些不须要用到方法A的的模块也将必需要依赖外部类库L才行,使用单一责任原则,隔离出包含方法A的职责封装成另外一个工具类,便可以解决问题.

  2. 里氏替换原则[Liskov Substitution Principle--LSP]

    定义1:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

    意译1:若是对每个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的全部程序P在全部的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型.

    定义2:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

    意译2:全部使用基类的地方必须能透明地使用其派生类.

    优势:

           代码共享,减小类的建立

           提升代码的重用性

           提升代码的可扩展性

    缺点:

           继承是入侵的

           加强了耦合

    理解:从优缺点能够看出,使用里氏替换原则的优缺点就是继承的优缺点,由于这一原则的目标就是为了放大继承体系的优势,缩小其缺点,它的使用前提就是继承...它包含了四层含义:①子类必须彻底实现父类的方法;②子类能够有个性[但咱们应当尽量的减小子类的个性];③子类重载继承自父类的方法时,输入参数(方法形参)能够被放大;④子类重写方法时,输出结果(返回值)能够被缩小.

    示例:使用继承体系时遵循里氏替换原则能帮咱们避免一些隐藏在其中的隐患,如下代码了违背第三层含义

    public class Father {
        public void doSth(Map map){
            System.out.println("Father");
        }
    }
    public class Son extends Father{
        public void doSth(HashMap map){    //子类缩小了输入参数
            System.out.println("Son");
        }
    }
    public static void main(String[] args) {
        Father temp = new Father();        //Father
        Son temp = new Son();              //Son
        HashMap map = new HashMap();
        temp.doSth(map);
    }

    能够看到,当咱们用子类替换父类时,代码逻辑发生了变化,咱们应当避免相似的问题发生.

  3. 依赖倒置原则[Dependence Inversion Principle--DIP]

    定义:High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.

    意译:高层模块不该该依赖低层模块,二者都应该依赖于抽象,抽象不该该依赖细节,细节应该依赖抽象.

    优势:

           下降类间耦合

           提升系统稳定性

           提升代码的可读性和可维护性

           利于并行开发

    缺点:

           项目中的类文件确定会增多(有了上述的优势,这点代价都不肯意付出么?)

    理解:不要被名字唬住,这也许只是为了显示出高端(just kidding)...这一原则的核心思想就是"面向接口编程",一切皆依赖抽象,抽象是对实现的约束,同时也是与外部沟通的约束,当咱们使用抽象传递依赖[构造器注入,Setter注入,接口注入],搭建系统骨骼,其稳定性一定要远远强于直接使用具体实现的方式,而要使代码遵循该原则,能够经过如下几点:①每一个类都尽可能有接口或抽象类;②变量的表面类型尽可能被声明为接口或抽象类③任何类都尽量避免派生自具体类;④子类尽可能不要重写父类已经实现的方法;⑤结合里氏替换原则使用.

    示例:一个只能开BWM的老司机.....

    public class Driver {
    
        // 司机的主要职责就是驾驶汽车
        public void drive(BMW bmw) {
            bmw.run();
        }
    }
    public class Benz {
    
        // 汽车确定会跑
        public void run() {
            System.out.println("奔驰汽车开始运行...");
        }
    }
    public class BMW {
    
        // 宝马车固然也能够开动了
        public void run() {
            System.out.println("宝马汽车开始运行...");
        }
    }
    public static void main(String[] args) {
        Driver zhangSan = new Driver();
        BMW bmw = new BMW();
        zhangSan.drive(bmw);
    }

    本例中有C驾照的老司机居然只能开宝马,而不能开奔驰,这显然是不科学的....咱们对程序作以下修改以知足依赖倒置原则

    public class Driver {
    
        // 司机的主要职责就是驾驶汽车
        public void drive(ICar car) {
            car.run();
        }
    }
    public class Benz implements ICar{
    
        // 汽车确定会跑
        public void run() {
            System.out.println("奔驰汽车开始运行...");
        }
    }
    public class BMW implements ICar{
    
        // 宝马车固然也能够开动了
        public void run() {
            System.out.println("宝马汽车开始运行...");
        }
    }
    public static void main(String[] args) {
        Driver zhangSan = new Driver();
        zhangSan.drive(new BMW());
        zhangSan.drive(new Benz());
    }

    这才是真正的老司机,无论你什么车,信手拈来~~~

  4. 接口隔离原则[Interface Segregation Principle--ISP]

    定义1:Clients should not be forced to depend upon interfaces that they don't use.

    意译1:客户端不该该被强迫依赖于它所不须要的接口.

    定义2:The dependency of one class to another one should depend on the smallest possible interface.

    意译2:类与类之间的依赖应该创建在尽量小的接口上.

    优势:

           接口细化后,可自由"组合",易于应对变化,提升了维护性和扩展性

    缺点:

           会增长代码结构复杂度

    理解:这一原则和单一责任原则有些类似,但咱们应当了解它们的意图是不一样的:单一责任原则的关注点在于职责,这是从业务逻辑上的划分[难],而接口隔离原则的关注点是接口中的方法数量要尽量的少[易];另外一方面,严格来讲单一责任原则的原始定义是针对类,对接口和方法的原则应用是其扩展形式.因为存在上述的类似性,咱们在设计接口时可能会碰见只能符合二者中的一个的状况(鱼与熊掌不可兼得),这时候全凭我的的设计偏好了,我更愿意优先遵循单一责任原则....下面说说它具体包含的四层含义:①接口尽可能小;②高内聚;③定制服务(经过将细小的接口自由组合来为不一样的个体提供定制服务);④设计不能过分(很显然,一个接口一个方法确定能知足接口隔离原则,可是同样显然的,你毫不会那么设计~~~).

    示例:考虑以下场景,警务系统有个内部信息查询平台,它提供给客户两个接口,查询基本信息,查询秘密信息,前者面向整个警务系统开放,后者只针对领导层

    咱们来看第一种实现方式:

    public interface IPersonInfoService {
    
        public void gainBaseInfo();
    
        public void gainSecretInfo();
    }
    public class PersonInfoService implements IPersonInfoService {
    
        @Override
        public void gainBaseInfo() {
            System.out.println("调用DAO层获取基本信息接口");
        }
    
        @Override
        public void gainSecretInfo() {
            System.out.println("调用DAO层获取秘密信息接口");
        }
    
    }

    能够看到,咱们提供了一个完整的接口给外部调用模块,而后经过口头约束来告诉外部模块能不能查询秘密信息

    而后是第二种方式:

    public interface IBaseInfoService {
    
        public void gainBaseInfo();
    
    }
    public interface ISecretInfoService {
    
        public void gainSecretInfo();
    
    }
    public class BaseInfoService implements IBaseInfoService {
    
        @Override
        public void gainBaseInfo() {
            System.out.println("调用DAO层获取基本信息接口");
        }
    
    }
    public class WholeInfoService implements IBaseInfoService, ISecretInfoService {
    
        @Override
        public void gainBaseInfo() {
            System.out.println("调用DAO层获取基本信息接口");
        }
    
        @Override
        public void gainSecretInfo() {
            System.out.println("调用DAO层获取秘密信息接口");
        }
    
    }

    这里咱们将查询接口拆分红两个独立的接口,实现类经过自由拼装所须要实现的接口来为外部两个调用模块提供单独的定制服务

    *现实场景中的业务远远不是像示例所描述的那般简单的,咱们能够想象,在具体场景中,这两种方式的差距是巨大的.

  5. 最少知识原则[Least Knowlegde Principle--LKP]

    定义:Only talk to your immediate friends.

    意译:只和你的朋友直接交谈.(一个对象应该对其余对象有最少的了解)

    优势:

           下降耦合,提升系统灵活性

    缺点:

           产生的中介类会致使系统结构复杂度增长

    理解:要理解这个原则,首先须要解释出如今定义中的"朋友"一词的含义,它指类的成员变量,输入参数,返回值.言归正传,最少知识原则包含了四层含义:①只和朋友交谈(类中不该该出现对"朋友"以外的访问,JAVA,框架 API除外,毕竟通常状况下它们是不变的);②朋友也要适度保持距离(尽可能减小public的使用);③是本身的就是本身的(若是一个方法放在本类中,既不增长类间的关系,也不会对本类产生负面影响,那就放在本类中);④减小Serializable的使用....另外,须要了解的是,这货还有另外一个名字:"迪米特法则".

    示例:体育老师:"体育委员,你去把女生清一下",体育委员:"哎呀,亲哪一个".....

    咱们来看实现:

    public class Teacher {
    
        // 老师对学生发布命令,清一下女生
        public void commond(GroupLeader groupLeader) {
            List listGirls = new ArrayList();
            // 初始化女生
            for (int i = 0; i < 20; i++) {
                listGirls.add(new Girl());
            }
            // 告诉体育委员开始执行清查任务
            groupLeader.countGirls(listGirls);
        }
    }
    public class GroupLeader {
    
        // 清查女生数量
        public void countGirls(List<Girl> listGirls) {
            System.out.println("女生数量是:" + listGirls.size());
        }
    }
    public class Girl {
    
    }
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        // 老师发布命令
        teacher.commond(new GroupLeader());
    }

    观察示例代码,发现Teacher类中包含了对非朋友类Girl的引用,这意味着,若是类Girl发生改变,将有可能影响到Teacher类,这样的代码无疑加大了变动所带来的风险,咱们应该只和朋友交谈,对代码作出修改:

    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("女生数量是:" + this.listGirls.size());
        }
    }
    public class Girl {
    
    }
    public static void main(String[] args) {
        // 产生一个女生群体
        List<Girl> listGirls = new ArrayList<Girl>();
        // 初始化女生
        for (int i = 0; i < 20; i++) {
            listGirls.add(new Girl());
        }
        Teacher teacher = new Teacher();
        // 老师发布命令
        teacher.commond(new GroupLeader(listGirls));
    }

    经过上述修改,咱们解除了Teacher类对陌生类Girl的依赖,下降了系统的耦合度,使得变动引发的风险下降.

  6. 开放关闭原则[Open Closed Principle--OCP]

    定义:Software entities like classes,modules and functions should be open for extension but closed for modifications.

    意译:软件实体好比类,模块,方法应该对扩展开放对修改关闭.

    理解:开放关闭原则是软件开发最基础的原则,同时也是最根本的原则,它就像一个抽象类,而上述五大原则就像是它的具体实现.咱们对开闭原则的遵循程度,直接反应了系统的稳定性,灵活性.它指导咱们用抽象来搭建系统框架,用实现来扩展细节.它是"总则".

  • 总结

    上述全部的设计原则都是一种编程建言,它们都是为了开发出高稳定性,易于扩展维护的项目产品这一目标而建议采用的设计思想,而不是教条(这也是上述第五条采用"最少知识原则"而不使用另外一个名字"迪米特法则"的缘由),它们并无要求咱们规规矩矩的严格按照它们的定义来编写代码,事实上,在实际的开发中,咱们也很难(其实我更相信不可能,由于它们自己的定义也并不是是量化的指标)作到彻底遵循六大设计原则.当咱们仔细回忆上述法则的描述,不难发现其实不少状况下它们都要求咱们细化代码颗粒度以到达代码复用,进而实现系统的灵活性,然而细化代码是有代价的,它将使得代码复杂度增长,因此,咱们再实现系统灵活性的同时,也在加大代码结构复杂度,而找到其中的平衡点,才是设计原则真正但愿咱们作到的,咱们应当始终了解:设计原则,设计模式都只是手段,开发出高稳定性,易于扩展维护的项目产品才是咱们的最终目标,若是你有信心本身开发系统具有高稳定性,又易于扩展维护,那么你彻底能够不懂设计原则,设计模式(若是你真的能在这样的前提下完成这样的系统,那么,也仅仅是你不知道你使用了设计原则/模式,而它们实际上一定已经充斥再你的代码中~~~,只能说,你天生是设计大师,孩子,想一想就算了),缺乏它们可能仅仅只是让你不能在同事中装逼~~~~而已

  • 相关推荐

    http://www.uml.org.cn/sjms/201211023.asp

    http://my.oschina.net/u/1047712/blog/150173

    <<HeadFirst 设计模式>>,<<设计模式之禅>>,<<设计模式 - 可复用面向对象软件的基础>>

  • 备注

    接口并不只仅指Interface,在设计模式中接口每每指代超类型,抽象...

相关文章
相关标签/搜索