让程序具有:
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
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)为了交互对象之间的松耦合去设计。



