结构型设计模式
六大原则
设计模式遵循六大原则:单一职责、里氏替换、依赖倒置、接口隔离、迪米特原则、开闭原则、会在具体的设计模式章节中进行体现
结构型设计模式
这类模式关注如何将类或对象组合成更大的结构,以更好地满足系统的需求。结构型模式包裹适配器模式、桥接模式、装饰器模式等。这些模式帮助在不同类之间建立更强大的关联,同时保持系统的灵活性和可维护性。
适配器设计模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
适配器模式还有个别名叫:Wrapper(包装器),顾名思义就是将目标类用一个新类包装一下,相当于在客户端与目标类直接加了一层。IT世界有句俗语:没有什么问题是加一层不能解决的。
适配器设计模式案例
部分重要代码:
import adapter.HDMI.HDMI;
import adapter.HDMI.HDMIImpl;
import adapter.VGA.VGA;
//适配器继承VGA接口
public class ObjectAdapter implements VGA {
//HDMI接口实例
HDMI h = new HDMIImpl();
@Override
public void projection() {
System.out.print("对象适配器:");
h.show();
}
}
import adapter.VGA.VGA;
import adapter.VGA.VGA2;
public class Test {
//show方法参数需要VGA类型
public static void show(VGA vga){
vga.projection();
}
public static void show(VGA2 vga2){
vga2.projection();
}
public static void main(String[] args) {
show(new ClassAdapter());
show(new ObjectAdapter());
show(new AbstractAdapterImpl());
}
}
装饰器设计模式
装饰器的核心就是在不改变原有类的基础上给类新增功能。
继承、AOP切面等都可以实现,但装饰器模式是另外一种思路更为灵活,可以避免继承导致的子类过多,也可以避免AOP带来的复杂性。
在对象功能拓展方面,他比继承更有弹性
装饰器设计模式案例
import decorator.before.HandlerInterceptor;
public abstract class SsoDecorator implements HandlerInterceptor {
private HandlerInterceptor handlerInterceptor;
private SsoDecorator(){}
public SsoDecorator(HandlerInterceptor handlerInterceptor) {
this.handlerInterceptor = handlerInterceptor;
}
public boolean preHandle(String request, String response, Object handler) {
return handlerInterceptor.preHandle(request, response, handler);
}
}
import decorator.before.HandlerInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class LoginSsoDecorator extends SsoDecorator {
private Logger logger = LoggerFactory.getLogger(LoginSsoDecorator.class);
private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();
static {
authMap.put("huahua", "queryUserInfo");
authMap.put("doudou", "queryUserInfo");
}
public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {
super(handlerInterceptor);
}
@Override
public boolean preHandle(String request, String response, Object handler) {
boolean success = super.preHandle(request, response, handler);
if (!success) return false;
String userId = request.substring(8);
String method = authMap.get(userId);
logger.info("模拟单点登录方法访问拦截校验:{} {}", userId, method);
// 模拟方法校验
return "queryUserInfo".equals(method);
}
}
代理设计模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
动态代理实现:
Java.lang.reflect.Proxy类可以直接生成一个代理对象
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数1:ClassLoader loader 代理对象的类加载器 一般使用被代理对象的类加载器
参数2:Class<?>[] interfaces 代理对象的要实现的接口 一般使用的被代理对象实现的接口
参数3:InvocationHandler h (接口)执行处理类
InvocationHandler中的invoke(Object proxy, Method method, Object[] args)
方法:调用代理类的任何方法,此方法都会执行
参数3.1:代理对象(慎用)
参数3.2:当前执行的方法
参数3.3:当前执行的方法运行时传递过来的参数
CGLIB 原理
动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。
在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
由于今天内容重点是设计模式,为了避免喧宾夺主不对jdk代理与cglib代理的区别以及应用做更深入讲解
代理设计模式案例
动态代理:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
cglib:
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(final Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("买房前准备");
Object result = methodProxy.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
外观设计模式
外观模式也叫门面模式,主要解决的是降低调用方的使用接⼝的复杂逻辑组合。这样调用方与实际的接口提供⽅方提供方提供了了一个中间层,用于包装逻辑提供API接口。有些时候外观模式也被用在中间件层,对服务中的通⽤用性复杂逻辑进行中间件层包装,让使用方可以只关⼼心业务开发。
外观设计模式案例
部分重要代码
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer() {
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void start() {
System.out.println("Computer start begin");
cpu.start();
disk.start();
memory.start();
System.out.println("Computer start end");
}
public void shutDown() {
System.out.println("Computer shutDown begin");
cpu.shutDown();
disk.shutDown();
memory.shutDown();
System.out.println("Computer shutDown end...");
}
}
桥接设计模式
桥接模式的主要作⽤用就是通过将抽象部分与实现部分分离,把多种可匹配的使⽤用进⾏行行组合。说⽩白了了核⼼心实现也就是在A类中含有B类接⼝口,通过构造函数传递B类的实现,这个B类就是设计的桥。
桥接设计模式案例
部分重要代码
import brige.after.mode.IPayMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
public abstract class Pay {
protected Logger logger = LoggerFactory.getLogger(Pay.class);
protected IPayMode payMode;
public Pay(IPayMode payMode) {
this.payMode = payMode;
}
public abstract String transfer(String uId, String tradeId, BigDecimal amount);
}
使用支付宝支付、微信支付分别实现Pay接口,并且选择对应的PayMode就可以完成两种支付方式的桥接。
组合设计模式
有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
组合设计模式案例
部分重要代码
由于组合设计模式需要构建决策树,代码篇幅较大,有需要的朋友可以在压缩包内找到对应代码自行学习。
包路径 | 类 | 介绍 |
---|---|---|
model.aggregates | TreeRich | 聚合对象,包含组织树信息 |
model.vo | EngineResult | 决策返回对象信息 |
model.vo | TreeNode | 树节点;子叶节点、果实节点 |
model.vo | TreeNodeLink | 树节点链接链路 |
model.vo | TreeRoot | 树根信息 |
最后构建出来的决策树长这样:
所以当我们输入man 29时,会获得果实B
享元设计模式
享元模式,主要在于共享通⽤用对象,减少内存的使⽤用,提升系统的访问效率。⽽而这部分共享对象通常⽐比较耗费内存或者需要查询⼤大量量接⼝口或者使⽤用数据库资源,因此统⼀一抽离作为共享对象使⽤用。
简单来说,我们抽取出一个对象的外部状态(不能共享)和内部状态(可以共享)。然后根据外部状态的决定是否创建内部状态对象。内部状态对象是通过哈希表保存的,当外部状态相同的时候,不再重复的创建内部状态对象,从而减少要创建对象的数量。
总结
这是本周讲课内容,花了大概一周时间准备,上csdn、github、z-library上找了不少材料,花了不少心血。最后因为时间原因这周讲课大概率也讲不了了,索性先写成博客发出来,供以后复习设计模式时回头看。