天天看点

设计模式六大原则1.开放封闭原则2.单职责原则3.里氏代换原则4.依赖倒置原则5.迪米特法则6.接口隔离原则7.参考文章

1.开放封闭原则

一个软件实体应当对扩展开放,则修改关闭。因此在设计一个模块时,应当使得这个模块可以在不被修改的前提下被扩展。一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。

设计的目的便在于:面对需求的改变而保持系统的相对稳定,从而使得系统可以很容易的从一个版本升级到另一个版本。

开放封闭原则的实现

开闭原则其实在大话设计模式中说得非常好,让人通俗易懂。它举了一个例子,我觉得说得非常好。是加减乘除法的例子。开始需求是做一个加法的操作。后来继续加入减法、乘法、除法。

有了加法以后可能会做一个需求变更:加入其它的算法法则。所以我们要有一个预判性,这个预判性会导致我们项目以后的扩展性,也会导致如果需求发生变更,程序修改的难易程度。所以,我们要做一个算法法则的操作类,加减乘除法都继承此操作接口。再加一个算法法则的客户端类类操作此算法。

设计模式六大原则1.开放封闭原则2.单职责原则3.里氏代换原则4.依赖倒置原则5.迪米特法则6.接口隔离原则7.参考文章

开放封闭原则的特点

  • 1.通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,是变化中的软件有一定的适应性和灵活性。
  • 2.已有的软件模块,特别是最重要的抽象模块不能再修改,这就使变化中的软件系统有一定的稳定性和延续性。

2.单职责原则

单一职责原则是实现高内聚、低耦合的指导方针。就一个类而言,应该仅有一个引起它变化的原因。

每一个职责都是一个变化的轴线,当需求变化时会反映为类的职责的变化。如果一个类承担的职责多于一个,那么引起它变化的原因就有多个。一个职责的变化甚至可能会削弱或者抑制类完成其他职责的能力,从而导致脆弱的设计。

示例:假如有类Class1完成职责T1,T2,当职责T1或T2有变更需要修改时,有可能影响到该类的另外一个职责正常工作。

好处:类的复杂度降低、可读性提高、可维护性提高、扩展性提高、降低了变更引起的风险。

3.里氏代换原则

所有引用基类(父类)的地方必须能透明地使用其子类的对象。而且它察觉不出父类和子类对象的区别。也就是说,在软件里面,把父类替换成它的子类,程序的行为没有变化。

里氏替换原则告诉我们不要破坏继承体系

继承优点

代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;提高代码的重用性;子类可以形似父类,但又异于父类;提高代码的可扩展性,实现父类的方法就可以“为所欲为”了;提高产品或项目的开放性。

继承缺点

继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;降低代码的灵活性。子类必须拥有父类的属性和方法;增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果;大片的代码需要重构。

示例:克服继承的缺点——里氏替换原则

这里参考大佬的文章https://blog.csdn.net/zhengzhb/article/details/7281833

我们需要完成一个两数相减的功能,由类A来负责。

class A{
	public int func1(int a, int b){
		return a-b;
	}
}
 
public class Client{
	public static void main(String[] args){
		A a = new A();
		System.out.println("100-50="+a.func1(100, 50));
		System.out.println("100-80="+a.func1(100, 80));
	}
}

运行结果:

100-50=50
100-80=20
           

后来,我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。即类B需要完成两个功能:

  • 两数相减。
  • 两数相加,然后再加100。

由于类A已经实现了第一个功能,所以类B继承类A后,只需要再完成第二个功能就可以了,代码如下:

class B extends A{
	public int func1(int a, int b){
		return a+b;
	}
	
	public int func2(int a, int b){
		return func1(a,b)+100;
	}
}
 
public class Client{
	public static void main(String[] args){
		B b = new B();
		System.out.println("100-50="+b.func1(100, 50));
		System.out.println("100-80="+b.func1(100, 80));
		System.out.println("100+20+100="+b.func2(100, 20));
	}
}

类B完成后,运行结果:

100-50=150
100-80=180
100+20+100=220
           

我们发现原本运行正常的相减功能发生了错误。原因就是类B在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了类B重写后的方法,造成原本运行正常的功能出现了错误。 在本例中,引用基类A完成的功能,换成子类B之后,发生了异常。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

在使用里氏代换原则时需要注意如下几个问题:

  • (1)子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。 根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
  • (2) 我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏代换原则是开闭原则的具体实现手段之一。
  • (3) Java语言中,在编译阶段,Java编译器会检查一个程序是否符合里氏代换原则,这是一个与实现无关的、纯语法意义上的检查,但Java编译器的检查是有局限的

4.依赖倒置原则

依赖倒置原则告诉我们要面向接口编程;

核心思想:高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;

说明:高层模块就是调用端,低层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。

好处:依赖倒置的好处在小型项目中很难体现出来。但在大中型项目中可以减少需求变化引起的工作量。使并行开发更友好。

怎样做到依赖倒转

1.工厂方法模式

2.模板方法模式

3.迭代器模式

设计模式详解传送门https://mp.weixin.qq.com/s/AMJ0h4s8kOOSBBDlwN0nlw

5.迪米特法则

迪米特法则告诉我们要降低耦合。迪米特法则(Law of Demeter )又叫做最少知识原则,也就是说,一个对象应当对其他对象尽可能少的了解。

迪米特法则最初是用来作为面向对象的系统设计风格的一种法则,于1987年秋天由lan holland在美国东北大学为一个叫做迪米特的项目设计提出的。

迪米特法则实现方式:

1.外观模式

2.中介者模式

核心思想:类间解耦。

通俗来讲: 一个类对自己依赖的类知道的越少越好。自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。

6.接口隔离原则

开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。接口隔离原则告诉我们在设计接口的时候要精简单一;

  • 1.不应该强行要求客户端依赖于它们不用的接口;
  • 2.类之间的依赖应该建立在最小的接口上面。简单点说,客户端需要什么功能,就提供什么接口,对于客户端不需要的接口不应该强行要求其依赖;
  • 3.类之间的依赖应该建立在最小的接口上面,这里最小的粒度取决于单一职责原则的划分。

7.参考文章

https://blog.csdn.net/yucaixiang/article/details/90239817

https://www.jianshu.com/p/807bc228dbc2

继续阅读