龙空技术网

通俗易懂-举例解释设计模式-原则篇

JH爱coding 40

前言:

目前小伙伴们对“设计模式原则详解”大约比较关怀,大家都想要知道一些“设计模式原则详解”的相关内容。那么小编同时在网络上搜集了一些对于“设计模式原则详解””的相关知识,希望咱们能喜欢,各位老铁们一起来学习一下吧!

设计模式开始之前,你一定需要知道的七大原则,分别是一下七种:

单一职责原则接口隔离原则依赖倒置原则里氏替换原则开闭原则迪米特原则合成复用原则

前面已经介绍了1和2,今天来介绍3和4。

依赖倒置原则

先做个介绍,看不懂没关系,看懂更好。

高层模块不应该依赖低层模块,二者都应该依赖其抽象抽象(接口)不应该依赖细节(实现类),细节应该依赖抽象依赖倒转(倒置)的中心思想是面向接口编程。依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

也许你看的也不是太明白,没关系,用代码来解释一下。

//完成Person接收消息的功能//方式1分析//简单,比较容易想到class Email {	public String getInfo() {		return "电子邮件信息: hello,world";	}}class Person {	public void receive(Email email ) {		System.out.println(email.getInfo());	}}public class DependecyInversion {	public static void main(String[] args) {		Person person = new Person();		person.receive(new Email());	}}# 但是如果我们获取的对象是微信,短信等等,则新增类,同时Perons也要增加相应的接收方法class Email {	public String getInfo() {		return "电子邮件信息: hello,world";	}}# 新增部分class WeChat {	public String getInfo() {		return "WeChat信息: hello,world";	}}class Person {	public void receive(Email email ) {		System.out.println(email.getInfo());	}		# 新增部分  通过这可以发现,如果我们再新增短信,就会不听的在这里添加接收方法	public void receive(Wechat wechat) {		System.out.println(wechat.getInfo());	}}public class DependecyInversion {	public static void main(String[] args) {		Person person = new Person();		person.receive(new Email());		# 新增部分		person.receive(new WeChat());	}}

上面方式的弊端显而易见。接下来做个改进。

解决思路:

引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver 发生依赖,因为Email, WeiXin等等属于接收的范围,他们各自实现IReceiver 接口就ok,这样我们就符号依赖倒转原则。

代码实现:

//定义接口interface IReceiver {	public String getInfo();}class Email implements IReceiver {	public String getInfo() {		return "电子邮件信息: hello,world";	}}//增加微信class WeiXin implements IReceiver {	public String getInfo() {		return "微信信息: hello,ok";	}}//方式2class Person {	//这里我们是对接口的依赖	public void receive(IReceiver receiver ) {		System.out.println(receiver.getInfo());	}}public class DependecyInversion {	public static void main(String[] args) {		//客户端无需改变		Person person = new Person();		person.receive(new Email());				person.receive(new WeiXin());	}}# 通过这种方式可以发现,我们的Person类中,不会再因为新增一个类,就要去增加一个方法

下面介绍依赖关系传递的的三种式和应用案例(了解即可)

接口传递

# 方式1: 通过接口传递实现依赖# 开关的接口interface IOpenAndClose {      public void open(ITV tv); //抽象方法,接收接口}interface ITV { //ITV接口     public void play();}class ChangHong implements ITV {	@Override	public void play() {		// TODO Auto-generated method stub		System.out.println("长虹电视机,打开");	}	 }// 实现接口class OpenAndClose implements IOpenAndClose{    public void open(ITV tv){         tv.play();  }}public class DependencyPass {	public static void main(String[] args) {				ChangHong changHong = new ChangHong();		OpenAndClose openAndClose = new OpenAndClose();		openAndClose.open(changHong); }}	
构造方法传递
# 方式2: 通过构造方法依赖传递interface IOpenAndClose {     public void open(); //抽象方法}interface ITV { //ITV接口     public void play();}class OpenAndClose implements IOpenAndClose{     public ITV tv; //成员     public OpenAndClose(ITV tv){ //构造器          this.tv = tv;    }         public void open(){          this.tv.play();    }    }public class DependencyPass {	public static void main(String[] args) {        //通过构造器进行依赖传递		OpenAndClose openAndClose = new OpenAndClose(changHong);        openAndClose.open();		}}
setter方式传递
// 方式3 , 通过setter方法传递interface IOpenAndClose {	public void open(); // 抽象方法	public void setTv(ITV tv);}interface ITV { // ITV接口	public void play();}class OpenAndClose implements IOpenAndClose {	private ITV tv;	public void setTv(ITV tv) {		this.tv = tv;	}	public void open() {		this.tv.play();	}}class ChangHong implements ITV {	@Override	public void play() {		// TODO Auto-generated method stub		System.out.println("长虹电视机,打开");	}	 }public class DependencyPass {	public static void main(String[] args) {	   //通过setter方法进行依赖传递		OpenAndClose openAndClose = new OpenAndClose();		openAndClose.setTv(changHong);		openAndClose.open();	}}

依赖倒置原则注意事项:

低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化继承时遵循里氏替换原则

里氏替换原则

为什么会有里氏(人名)原则?

继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。问题提出:在编程中,如何正确的使用继承? 由此提出里氏替换原则

基本介绍

里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的以为姓里的女士提出的。如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序Р在所有的对象o1都代换成o2时,程序Р的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。

关于聚合,组合,依赖也是很重要的一部分,具体请参考我上一篇文章。

下面通过代码来让你对里氏原则有一个更好的理解。

// A类class A {	// 返回两个数的差	public int func1(int num1, int num2) {		return num1 - num2;	}}// B类继承了A// 增加了一个新功能:完成两个数相加,然后和9求和class B extends A {	//这里,重写了A类的方法, 可能是无意识	public int func1(int a, int b) {		return a + b;	}	public int func2(int a, int b) {		return func1(a, b) + 9;	}}public class Liskov {	public static void main(String[] args) {		// TODO Auto-generated method stub		A a = new A();		System.out.println("11-3=" + a.func1(11, 3));		System.out.println("1-8=" + a.func1(1, 8));		System.out.println("-----------------------");		B b = new B();		System.out.println("11-3=" + b.func1(11, 3));//这里本意是求出11-3		System.out.println("1-8=" + b.func1(1, 8));// 1-8		System.out.println("11+3+9=" + b.func2(11, 3));				}}# 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有# 功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简 单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。

通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替。

改进代码实现

//创建一个更加基础的基类class Base {	//把更加基础的方法和成员写到Base类}// A类class A extends Base {	// 返回两个数的差	public int func1(int num1, int num2) {		return num1 - num2;	}}// 增加了一个新功能:完成两个数相加,然后和9求和class B extends Base {	//如果B需要使用A类的方法,使用组合关系	private A a = new A();		public int func1(int a, int b) {		return a + b;	}	public int func2(int a, int b) {		return func1(a, b) + 9;	}		//我们仍然想使用A的方法	public int func3(int a, int b) {		return this.a.func1(a, b);	}}public class Liskov {	public static void main(String[] args) {		// TODO Auto-generated method stub		A a = new A();		System.out.println("11-3=" + a.func1(11, 3));		System.out.println("1-8=" + a.func1(1, 8));		System.out.println("-----------------------");		B b = new B();		//因为B类不再继承A类,因此调用者,不会再func1是求减法		//调用完成的功能就会很明确		System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出11+3		System.out.println("1+8=" + b.func1(1, 8));// 1+8		System.out.println("11+3+9=" + b.func2(11, 3));				//使用组合仍然可以使用到A类相关方法		System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3		}}

这里解释一下,为什么这么做,首先继承一个公共的类Base,因为毕竟是B类要在A类的基础上扩展功能,所以A类和B类性质是一样的,通过继承一个类,保证是同一性质的类,A和B继承一个公共的类,同时也保证方便进行多态调用,使得A和B类扩展性变的很好。减少继承的使用,防止开发者重写父类方法,破坏父类原有的功能,也防止如果对父类方法进行修改还要兼顾不同的子类。

标签: #设计模式原则详解