六大设计原则

单一职责:一个类只负责一件事
开闭原则:当对扩展开放,对修改关闭。尽量在不修改原有代码的情况下进行扩展。
里氏替换:方法参数用基类,可以传子类一定不会报错(java编译期也会检查)
依赖倒置:针对接口编程,变量声明、传参、方法返回、类型转换等用高层次的抽象层类

开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。

接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口,细分接口。
迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。

额外的:
合成复用原则:复用时要尽量使用组合/聚合关系(关联关系),少用继承。







里氏替换原则:

里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是
我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。例如有两个类,一个类为BaseClass,另一个是SubClass类,那么一个方法如果可以接受一个BaseClass类型的话,如:method1(base),那么method1(sub)能够正常运行。反过来的则不成立,如method2(sub),那么一般而言不可以有method2(base),除非是重载方法

里氏代换原则是实现开闭原则的重要方式之一,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型.

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:
1子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2子类中可以增加自己特有的方法。
3 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
4当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格.


依赖倒置原则:

传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。
在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。(就是Spring之类的注入方式)

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

interface IDriver{
  void drive(ICar car);
}

interface ICar{
  void run();
}

开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。


合成复用原则又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle, CARP),

其定义如下:
合成复用原则:尽量使用对象组合,而不是继承来达到复用的目的.
合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能
的目的。


简言之:复用时要尽量使用组合/聚合关系(关联关系),少用继承。


在面向对象设计中,可以通过两种方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承,但首先应该考虑使用组合/聚合,组合/聚合可以使系统更加灵活,降低
类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂
度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。
通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变(比如换了个数据库),那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;



扩展:
对于继承的深入理解,大家可以参考《软件架构设计》一书作者温昱先生的文章——《见山只是山见水只是水——提升对继承的认识》。由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为“黑箱”复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作;合成复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。

一般而言,如果两个类之间是“Has-A”的关系应使用组合或聚合,如果是“Is-A”关系可使用继承。
"Is-A"是严格的分类学意义上的定义,意思是一个类是另一个类的"一种";而"Has-A"则不同,它表示某一个角色具有某一项责任。

你可能感兴趣的:(六大设计原则)