设计模式六大原则
开闭原则
总结:对拓展开放,对修改关闭
一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
单一原则
总结:一个类负责一项职责
一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因
依赖倒置原则
总结:面像接口编程
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
接口隔离原则
总结:设计接口时,最小功能单元
两个定义:
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口
类间的依赖关系应该建立在最小的接口上
里氏替换原则
总结:子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
Barbara Liskov提出:
标准定义:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。
迪米特法则
总结:低耦合,高内聚。陌生的类要出现在成员变量、方法参数、方法返回值中,不要以局部变量的形式出现。
定义:一个对象应该对其他对象有最少的了解。
设计模式六大原则.xmind
设计模式种类
创建型模式——单例模式、静态工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
结构性模式——桥接模式、适配器模式、装饰器模式、代理模式、组合模式、门面模式、享元模式
行为型模式——解释器模式、模板方法模式、迭代器模式、调节者模式、备忘录模式、状态模式、策略模式、观察者模式、责任链模式、命令模式、访问者模式
工厂模式
简单工厂模式
意思是写一个工厂类,由工厂创建具体实例。
![image.png](https://cdn.nlark.com/yuque/0/2019/png/607561/1575105041114-57d8aa9d-8173-402e-924f-d325bcc7b42d.png#align=left&display=inline&height=264&name=image.png&originHeight=450&originWidth=627&size=20358&status=done&style=none&width=368)
缺陷:
不符合单一原则:一个类负责创建对应实例的创建
不符合开闭原则:拓展不同实例的时候,需要修改已有的代码
工厂方法模式
解决简单工厂模式的缺陷,拓展新种类代码的时候,不需要修改已有的代码
意思是用户需要不同的种类的产品的创建,则从对应产品实例化的工厂创建。
举例:
编写一个工厂接口类,然后根据需求不同种类的产品编写不同的具体工厂类(实现该抽象类)。
在引用的时候,则可以使用多态方式获取对应工厂,然后使用该工厂创建对应的产品实体。
![image.png](https://cdn.nlark.com/yuque/0/2019/png/607561/1575105381998-b0f4e5ee-1708-4ba6-b4bf-7e7287892933.png#align=left&display=inline&height=285&name=image.png&originHeight=416&originWidth=665&size=16377&status=done&style=none&width=456)
抽象工厂模式
为了应对产品方面的考虑,增加了抽象工厂概念。
比如:奔驰贴上了宝马的商标这种事情是不对的,所以需要用抽象进行约束
在工厂方法上优化,在抽象类上添加对应的抽象方法或抽象成员对象,这是为了应对防止产品之间具备一定的联系,用抽象的方式让这些产品联系起来。
工厂之间的区别
简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)
注意事项
(1)工厂类常常采用单例模式(Singleton)。
(2)工厂类拥有基类(定义共同接口),基类可以为纯虚类,也可以定义缺省方法。
(3)对于工厂方法和抽象工厂,基类中的生产产品的函数常常为虚函数,以实现动态绑定。
(4)调用工厂方法的函数通常采用工厂实现的指针和引用作为形参,以便根据不同的工厂实参调用不同的工厂方法。
建造者模式
定义:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。
听听上面官方给的定义,这是人讲的话吗??
通俗理解:用户想要一辆车,车在工厂中创建过程是标准并且复杂的,而将从获取零件组装制造成一辆车最后交付给用户这一过程,就叫做建造者模式。
建造者模式中包含指挥者/建造者/抽象建造者/产品实体类,指挥者指挥建造者建造这个产品的过程顺序,建造者负责创建。(符合单一值则原则),但为了保证整个工艺标准,需要做一个约束,这时候需要给建造者做一个抽象来保证这个工艺的完整标准
//产品类
public class Bike {
private IFrame frame;
private ISeat seat;
private ITire tire;
public IFrame getFrame() {
return frame;
}
public void setFrame(IFrame frame) {
this.frame = frame;
}
public ISeat getSeat() {
return seat;
}
public void setSeat(ISeat seat) {
this.seat = seat;
}
public ITire getTire() {
return tire;
}
public void setTire(ITire tire) {
this.tire = tire;
}
}
// 抽象 builder 类
public abstract class Builder {
abstract void buildFrame();
abstract void buildSeat();
abstract void buildTire();
abstract Bike createBike();
}
// 具体 builder 类
public class MobikeBuilder extends Builder{
private Bike mBike = new Bike();
@Override
void buildFrame() {
mBike.setFrame(new AlloyFrame());
}
@Override
void buildSeat() {
mBike.setSeat(new DermisSeat());
}
@Override
void buildTire() {
mBike.setTire(new SolidTire());
}
@Override
Bike createBike() {
return mBike;
}
}
public class OfoBuilder extends Builder{
private Bike mBike = new Bike();
@Override
void buildFrame() {
mBike.setFrame(new CarbonFrame());
}
@Override
void buildSeat() {
mBike.setSeat(new RubberSeat());
}
@Override
void buildTire() {
mBike.setTire(new InflateTire());
}
@Override
Bike createBike() {
return mBike;
}
}
//指挥者类
public class Director {
private Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
public Bike construct() {
mBuilder.buildFrame();
mBuilder.buildSeat();
mBuilder.buildTire();
return mBuilder.createBike();
}
}
//用户获取产品的过程
public class Click {
public static void main(String[] args) {
showBike(new OfoBuilder());
showBike(new MobikeBuilder());
}
private void showBike(Builder builder) {
//用户只需要告诉指挥者想要哪种型号的产品
Director director = new Director(builder);
//用户在这里就能得到产品,并且产品的质量标准有保证
Bike bike = director.construct();
bike.getFrame().frame();
bike.getSeat().seat();
bike.getTire().tire();
}
}
代理模式
代理模式的由来
需求
v0.0实现增删查功能
v0.1在0.0的功能上增加日志打印
v0.2在0.1的日志打印上,修改成英文日志打印
v0.3增加修改功能
v0.4凌晨0-6点,不打印日志
问题——从上面的版本变更会产生一些问题
1、0.1开始,代码在重复
2、0.2版本,需要修改很多地方
3、代码急剧膨胀,核心代码与非核心代码
对于这些问题,问题不能完全解决,或者具有很多不太合适的解决办法,这时就有了动态代理模式
理解代理模式
假设有这么个场景,你们是一个软件公司,客户需要对程序员开发一个软件,显然不会带着需求直接找开发谈,而是会去找商务去谈,这个时候客户就会认为商务就代表了这个公司。
从上图可以看出代理对象和真实对象存在代理和被代理的关系,那代理模式有什么作用呢?
代理模式的作用就是在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制调用者访问真实对象。
所以得出代理必须分为两个步骤:
- 代理对象和真实对象建立代理关系
- 实现代理对象的逻辑方法(本文使用的是JDK中的动态代理技术)
使用java完成动态代理模式
先看一个图理解JVM加载类
从上图可以得出,字节码有两种途径获得,一种是硬盘中的.java文件由javac编译出来,另一种是程序运行期时由流向内存写入字节码,而动态代理则是由运行期创建字节码文件的方式创建代理对象,那如何获取代理对象?
jdk内部提供Proxy.newProxyInstance方法获取代理对象,Proxy.newProxyInstance方法需要三个参数(类加载器,接口字节码数组,InvocationHandler接口实现类)
1、获取类加载器
通过类加载器加载字节码文件到内存中,再进一步实例化对象(简单来说就是,只要涉及实例化类的对象,就一定要加载类的字节码,而加载字节码就必须使用类加载器)
2、获取需要代理的接口数组,将所有需要代理的接口装进一个数组传入
生成的代理对象会自动实现这些接口的方法
3、获取InvocationHandler接口的实现类
最后调用代理对象的方法最终都会执行InvocationHandler实现类里的invoke方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface ICalc{
//ICalc接口
int add(int a,int b);
int sub(int a,int b);
int mul(int a,int b);
int div(int a,int b);
}
class ICalImpl implements ICalc{
//ICals实现类
@Override
public int add(int a, int b) {
int result = a + b;
return result;
}
@Override
public int sub(int a, int b) {
int result = a - b;
return result;
}
@Override
public int mul(int a, int b) {
int result = a * b;
return result;
}
@Override
public int div(int a, int b) {
int result = a / b;
return result;
}
}
class ProxyExample implements InvocationHandler{
//代理类
private Object target;
public ProxyExample() {}
public Object bind(Object target){
this.target = target;
//Proxy.newProxyInstance三个参数
//1、类加载器
//2、接口字节码数组
//3、InvocationHandler接口实现类
//对代理对象的调用不会进入真正的实现方法中,而是进入第三个参数的Invoke方法中
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理对象执行的所有方法最终都会在这里执行
//Object proxy这个参数代表代理对象本身
//Method method这个参数代表调用的方法
//Object[] args这个数组代表传入的参数
//利用反射的方式调用对象
Object result = method.invoke(target,args);
return result;
}
}
public class proxyTest {
public static void main(String[] args) {
//真实对象
ICalc trueObject = new ICalImpl();
System.out.println(trueObject.add(1,2));
ProxyExample proxy = new ProxyExample();
//获取代理对象(将真实对象和代理对象进行绑定)
ICalc proxyObject = (ICalc) proxy.bind(trueObject);
//proxyObject对象是一个代理对象,它会进入代理的逻辑方法invoke里
System.out.println(proxyObject.add(1,1));
System.out.println(proxyObject.div(1,1));
System.out.println(proxyObject.mul(1,1));
System.out.println(proxyObject.sub(1,1));
}
}
类比前面举的例子,proxy相当于商务对象,target相当于开发人员,bind方法就是建立商务和开发人员代理关系的方法,而invoke就是商务逻辑,它将控制对开发人员的访问。
适配器模式
装饰器模式
单例模式