最近跟着B站上 尚硅谷Java设计模式(图解+框架源码剖析) 视频学习了设计模式,现将其中的七大设计原则整理如下。
七大设计原则:
1)单一职责原则
2)接口隔离原则
3)依赖倒转原则
4)里氏替换原则
5)开闭原则(ocp原则)
6)迪米特法则
7)合成复用原则
1.单一职责原则
概念:对一个类来讲,一个类应该只负责一项职责。
拿Dao举例,userDao应该只负责关于User表的增删改查,而不应该负责Order表,那样会使得代码结构混乱。如果一个类负责多项职责,还可能由于不同功能间相互影响导致错误。
总的来说,单一职责原则的目的有以下几点:
1)降低类的复杂度,一个类只负责一项职责;
2)提高类的可读性、可维护性;
3)降低变更功能引起的风险
2.接口隔离原则
理解:一个类对另一个类的依赖应该建立在最小接口上。
简单点的例子:假设A类实现了B接口,那么在设计时,应该把A类用得到的方法放在在B接口中,尽量不要放用不到的方法,否则A在实现B接口时,也要实现那些用不到的方法。
复杂点的例子:假设有4个类:A,B,C,D,以及一个接口:a,a中有5个方法:m1,m2,m3,m4,m5。A通过接口a依赖C(比如A a=new A(new C()),而C实现了a接口),B通过接口a依赖D;A只会用到接口a中的m1,m2,m3方法,B只会用到接口a中的m1,m4,m5方法。也就是说,接口a对于A和B来说都不是最小接口,解决办法:把接口a拆分为最小接口:a1(包含m1方法),a2(包含m2,m3方法),a3(包含m4,m5方法),这时只需让C实现a1接口、a2接口,而D实现a1接口、a3接口,就避免了C、D去实现它们不用实现的方法。
3.依赖倒转原则
理解:1)高层模块不应该依赖底层模块,二者都应该依赖其抽象;
2)抽象不应该依赖细节,细节应该依赖抽象;(在java中,抽象指的是接口或抽象类,细节就是具体的实现类)
3)中心思想是面向接口编程
例子:
public class Demo {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email {
public String getInfo() {
return "电子邮件信息: hello,world";
}
}
//完成Person接收消息的功能
//方式1分析
//1. 简单,比较容易想到
//2. 如果我们获取的对象是 微信,短信等等,则新增类,同时Perons也要增加相应的接收方法
//3. 解决思路:引入一个抽象的接口IReceiver, 表示接收者, 这样Person类与接口IReceiver发生依赖
// 因为Email, WeiXin 等等属于接收的范围,他们各自实现IReceiver 接口就ok, 这样我们就符号依赖倒转原则
class Person {
public void receive(Email email ) {
System.out.println(email.getInfo());
}
}
改进:
public class Demo{
public static void main(String[] args) {
//客户端无需改变
Person person = new Person();
person.receive(new Email());
person.receive(new WeiXin());
}
}
//定义接口
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";
}
}
//方式2
class Person {
//这里我们是对接口的依赖
public void receive(IReceiver receiver ) {
System.out.println(receiver.getInfo());
}
}
总的来说就是,类与类之间如果有依赖关系,尽量利用接口或抽象类作为中间缓冲层。
另:依赖关系传递的三种方式:
1)接口传递
2)构造方法传递
3)set方法传递
public class Demo{
public static void main(String[] args) {
// TODO Auto-generated method stub
ChangHong changHong = new ChangHong();
// OpenAndClose openAndClose = new OpenAndClose();
// openAndClose.open(changHong);
//通过构造器进行依赖传递
// OpenAndClose openAndClose = new OpenAndClose(changHong);
// openAndClose.open();
//通过setter方法进行依赖传递
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.setTv(changHong);
openAndClose.open();
}
}
// 方式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();
// }
// }
// 方式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();
// }
// }
// 方式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("长虹电视机,打开");
}
}
4.里氏替换原则
针对编程时涉及到继承时的一种编码规范。
理解:1)所有引用基类的地方必须能透明地使用其子类的对象;
2)子类中尽量不要重写父类的方法;
3)如果两个类之间的关系不是那么密切,尽量不要使用继承,而通过组合、聚合、依赖来解决问题。
5.开闭原则(ocp原则)
理解:1)编程中最基础、最重要的设计原则
2)一个类中的方法应该对扩展开放(提供方),对修改关闭(使用方)
例子与依赖倒转原则的例子相仿。开闭原则的侧重点在于,设计程序时,尽量利用接口、抽象类,使得在添加新功能时,尽量不修改或少修改代码,并且使用方的代码不用修改,ocp原则较好地可扩展性、可重用性。
下面的例子体现了ocp原则:
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Triangle());
}
}
//这是一个用于绘图的类 [使用方]
class GraphicEditor {
//接收Shape对象,调用draw方法
public void drawShape(Shape s) {
s.draw();
}
}
//Shape类,基类
abstract class Shape {
int m_type;
public abstract void draw();//抽象方法
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println(" 绘制矩形 ");
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println(" 绘制圆形 ");
}
}
//新增画三角形
class Triangle extends Shape {
Triangle() {
super.m_type = 3;
}
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println(" 绘制三角形 ");
}
}
6.迪米特法则(最少知道原则)
理解:1)一个对象应该对其他对象保持最少的了解
2)只与直接的朋友通信;
3)直接的朋友:如果两个对象之间有耦合关系,那么它们就是朋友关系,我们称 A类的对象作为成员变量、方法参数、方法返回值存在于B类中 时,A类就是B类的直接朋友;而如果A类的对象只作为局部变量存在于B类中,A类不是B类直接的朋友。迪米特法则讲的就是编程时不要出现陌生的朋友。
7.合成复用原则
理解:尽量使用合成/聚合的方式,而不是使用继承。
设计原则核心思想:
1)把可能产生变化的代码独立出来;
2)针对接口编程,而不是针对实现;
3)最终目的是松耦合。
继承、依赖、聚合、组合图解:
上面是我结合自己的理解以及视频上的笔记总结出来的,一些容易理解的我就没有粘代码出来。
这位博友写的文章也是按照视频总结出来的,所有代码都有粘上:原创-Java设计模式篇章1。