栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

设计模式(一)之七大设计原则

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计模式(一)之七大设计原则

导言:为什么要学设计模式?

让程序具有:

1)代码重用性(相同代码,可以复用,不用多次编写)

2)可读性(代码编程规范,便于其他人阅读理解)

3)可扩展性(在原有基础上增加新功能时,比较方便)

4)可靠性(在原有基础上增加新功能后,对原来的功能没有影响)

5)高内聚,低耦合(模块内部紧密,模块与模块之间依赖性低)

七大设计原则 单一职责原则 定义

对于类来说,一个类应该只负责一项职责,如果类A负责职责a,职责b,当职责a需求变更的时候改变a时,可能造成职责b执行错误,所以需要将类A再次细分,分为Aa和Ab两个类。

注意事项

1)降低类的复杂度,一个类只负责一项职责

2)提高类的可读性,可维护性

3)降低类变更引起的风险

4)一般情况下,尽量遵守单一原则,除非逻辑足够简单,类中方法数量足够少,这样可以只在方法级别遵守单一职责原则。

接口隔离原则 定义

客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

举例解释

①若类A通过一个接口i1依赖类B,类C通过接口i1依赖类D。

②接口i1有三个方法op1、op2、op3,类B和类D都实现了接口i1。

③若类A只使用了op1和op2方法,类C只使用op2和op3方法。

这样的话,也就是说对于类B和类D都会有一个实现的方法用不到,按常理来说,就不应该实现,也就是说接口i1不是实现类B和D的最小接口,这时候将i1拆成三个接口i1、i2、i3,分别有op1、op2、op3这些方法,这样的话,类B就改为实现接口i1与i2,op3用不到就不实现i3了,类C改为实现接口i2和i3,同理。

依赖倒转原则 定义

1)高层模块不应该依赖底层模块,二者都应该依赖其抽象(接口或者抽象类)。

2)抽象不应该依赖细节,细节应该依赖抽象。

3)中心思想:面向接口编程。

补充

1)在java中,抽象一般指接口或者抽象类,细节是具体的实现类。

2)依赖关系传递的三种方式:

①接口传递

 

②构造方法传递

③setter方式传递

 

注意事项

1)底层模块尽量都要有抽象类和接口,或者两者都有,程序稳定性更好。

2)变量的声明类型尽量是抽象类或接口,(比如A是抽象类,B继承A,那么定义A a = new B()或者AImpl实现接口A,那么定义A a = new AImpl())这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。

3)继承时遵循里氏替换原则。(后面会讲)

里式替换原则 定义

1)子类对象可以在程式中代替其父类对象,也就是说引用基类(超类)的地方必须能透明地使用其子类的对象。

2)在使用继承是,遵循里氏替换原则,在子类中尽量不要重写父类中的方法。

3)里氏替换原则告诉我们,继承实际上让两个类耦合性增强。

注意事项

1)在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样起来简单,但整个体系的复用性会降低。特别是多态使用频繁的时候。

2)通用的解决方法:原来的父类和子类的原有继承关系去掉,都继承一个更通俗的基类,使用依赖、聚合、组合等关系替换之。

开闭原则 定义

1)编程中最基础、最重要的设计原则。

2)类、模块、函数对扩展开放(对于提供方),对修改关闭(对于使用方);用抽象构建框架,用实现扩展细节。

3)当软件需要变化时,尽量通过扩展而不是修改来实现变化。

例子

之前自己做的一个项目说吧,其实一般配合多态和抽象类一起使用的。

使用netty搭建pop服务器、smtp服务器,参照标准的smtp、pop协议指令来实现,客户端向服务器端发送指令,指令的前几位代表指令类型cmd,后面部分是指令参数,比如用户名、密码、邮件索引等等。我们在实现的时候使用了面向对象的多态这一特性。首先定义一个接口Command,包含两个方法,获取指令名称以及执行指令,然后定义抽象类baseCommand并声明抽象方法excute,然后我们需要定义一个枚举类,将定义协商好的指令注册进去,并用一个map集合以指令名称为key,指令对应的对象为value存起来。每当我写一个指令类时,都会继承抽象类baseCommand,并重写其中的抽象方法excute,然后编写指令对应的逻辑。这样的话,每次客户端发送消息过来,服务端解析得到指令名称,并从map集合中得到对象,然后父类引用统一指向该对象,得到的对象实例执行excute方法,就会自己去调用相应子类对象的同名方法,执行对应指令业务逻辑,这样的话,不需要对传过来的消息进行复杂的判断,充分使用多态,可以使程序有良好的扩展,并可以对所有类的对象即指令进行通用处理。

这样的话,如果以后要扩展指令,只需要先在枚举类中注册进去,然后新建一个指令类,继承抽象类baseCommand,重写其中的excute方法,编写新增的业务逻辑即可。不需要修改抽象类,不需要修改服务端代码,不需要添加繁杂的if判断。

迪米特法则 定义

1)一个对象应该对其他对象保持最少的了解。

2)类与类关系越密切,耦合度越大。

3)又称最少知道原则,即一个类对自己依赖的类知道得越少越好;换言之,对于被依赖的类,不管多么复杂,都尽量将逻辑封装在类的内部,对外除了提供的public方法,不对外泄露任何信息。

4)简单来说,只与直接的朋友通信。

ps:只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,例如依赖、关联、组合、聚合等,其中出现成员变量,方法参数,方法返回值中的类为直接的朋友。(而出现在局部变量中的类不是)

例子
package com.hnu.wy.demeter;

import java.util.ArrayList;
import java.util.List;

public class Demeter1 {

	public static void main(String[] args) {
		SchoolManager schoolManager = new SchoolManager();
		schoolManager.printAllEmployee(new CollegeManager());

	}

}


class Employee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}
}


class CollegeEmployee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}
}


class CollegeManager {
	public List getAllEmployee() {
		List list = new ArrayList();
		for (int i = 0; i < 10; i++) {
			CollegeEmployee emp = new CollegeEmployee();
			emp.setId("学院员工id= " + i);
			list.add(emp);
		}
		return list;
	}
}


class SchoolManager {
	public List getAllEmployee() {
		List list = new ArrayList();

		for (int i = 0; i < 5; i++) {
			Employee emp = new Employee();
			emp.setId("学校总部员工id= " + i);
			list.add(emp);
		}
		return list;
	}

	void printAllEmployee(CollegeManager sub) {

		List list1 = sub.getAllEmployee();
		System.out.println("------------学院总部员工------------");
		for (CollegeEmployee e : list1) {
			System.out.println(e.getId());
		}
		List list2 = this.getAllEmployee();
		System.out.println("------------学校总部员工------------");
		for (Employee e : list2) {
			System.out.println(e.getId());
		}
	}
}

我们分析类SchoolManager,由于有List作为其成员函数方法返回值,所以和Employee是直接朋友关系,又有CollogeManager作为其成员函数方法参数,所以和CollogeManager是直接朋友关系,但是关于List是函数里的局部变量,所以和CollegeEmployee不是直接朋友关系,违反了迪米特法则。所以修改之后如下:

package com.hnu.wy.demeter.improve;

import java.util.ArrayList;
import java.util.List;

public class Demeter1 {

	public static void main(String[] args) {
		SchoolManager schoolManager = new SchoolManager();
		schoolManager.printAllEmployee(new CollegeManager());

	}

}


class Employee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}
}


class CollegeEmployee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}
}


class CollegeManager {
	public List getAllEmployee() {
		List list = new ArrayList();
		for (int i = 0; i < 10; i++) {
			CollegeEmployee emp = new CollegeEmployee();
			emp.setId("学院员工id= " + i);
			list.add(emp);
		}
		return list;
	}

	
	public void printEmployee() {
		List list1 = getAllEmployee();
		System.out.println("------------学院总部员工------------");
		for (CollegeEmployee e : list1) {
			System.out.println(e.getId());
		}
	}
}


class SchoolManager {
	public List getAllEmployee() {
		List list = new ArrayList();

		for (int i = 0; i < 5; i++) {
			Employee emp = new Employee();
			emp.setId("学校总部员工id= " + i);
			list.add(emp);
		}
		return list;
	}

	void printAllEmployee(CollegeManager sub) {

		//这里直接调用sub的方法即可
		sub.printEmployee();
		List list2 = this.getAllEmployee();
		System.out.println("------------学校总部员工------------");
		for (Employee e : list2) {
			System.out.println(e.getId());
		}
	}
}
合成复用原则 定义

尽量使用依赖、组合、聚合的方式,而不是使用继承。

例子

比如类B需要使用类A中的两个方法,但是类A共有十个方法,那么这时候使用继承就很没有必要,因为其他八个方法根本用不到。

可以有三种常见解决方式:

①使用依赖,比如类B中有个函数op1(A a),这个函数的方法参数类A。

②使用组合,当类B的一个实例创建好时,顺便通过A a = new A()创建好了类A的一个实例。也就是说A类是B类的一个属性,且通过new实例化了。

③使用聚合,成员变量包含类A,即private A a,然后配合一个set(A a)方法进行传递。

设计原则的核心思想

1)找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

2)针对接口编程,而不是针对实现编程。

3)为了交互对象之间的松耦合去设计。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/298739.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号