结构型设计模式
适配器模式
定义
将一个类的接口转化成用户期望的另一个接口,使原本接口不兼容的类可以一起工作
UML类图
适用场景
- 已经存在的类,他的方法和需求不匹配时
- 不是软件设计阶段考虑的,是随着维护,由于不同产品不同厂家造成功能类似,接口不相同情况下的解决方案
优缺点
优点:提高类的透明性和复用性,现有的类复用但不需要改变,目标类和适配器类解耦,提高程序扩展性
缺点:适配器编写过程中需要全面考虑,可能会增加系统复杂性,增加系统代码可读难度
装饰器模式
定义
在不改变原有对象的基础上,将功能附加到对象上,是比继承更有弹性的替代方案,也是扩展原有对象功能
UML类图
适用场景
- 扩展一个类的功能,或添加附加职责时
- 动态的给一个对象添加功能,这些功能也可以动态的撤销
- 被装饰者和装饰品有不同的排列组合
优缺点
优点:是继承的补充,比继承灵活,未改变原有对象,而且使用不同装饰类以及这些装饰类的排列组合可以实现不同效果,扩展增加装饰类或装饰品也比较方便
缺点:会出现更多的代码和类,增加程序的复杂性,动态装饰和多层装饰会更加复杂
代理模式
定义
为其他对象提供一种代理, 以控制对这个对象的访问,代理对象在客户端和目标对象之间起中介作用
UML类图
静态代理
动态代理
动态代理的动态是指动态生成代理类对象,通过JDK自带的API来实现
要求:被代理对象类要实现接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
static interface Inter {
void hello();
}
static class Src implements Inter {
@Override
public void hello() {
System.out.println("hello world");
}
}
public static void main(String[] args) {
Src src = new Src(); //要被代理的源对象
/**
* 动态代理类的创建接收三个参数
* ClassLoader loader :类加载器,一般与被代理对象使用同一个加载器
* Class<?>[] interfaces :被代理类实现的接口,可以单独指定,也可以使用反射获取代理类实现所有接口
* InvocationHandler h :调用处理器对象,其中要实现invoke方法,拦截对源对象方法的调用
* invoke方法也接收三个参数
* Object proxy :指向生成的代理对象
* Method method :运行时所调用的方法,method.getName()可获得具体方法名
* Object[] args :运行时所调用方法的参数
*/
Inter proxy = (Inter) Proxy.newProxyInstance(src.getClass().getClassLoader(),
src.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// before code
Object res = method.invoke(src, args); //反射调用被代理对象方法
//after code
return res;
}
}); //代理对象,必须要强转成要被代理对象的接口,而并非代理对象类
proxy.hello(); //代理调用方法
/**
* 若调用处理器对象中逻辑足够简单可以使用lambda表达式,要注意lambda中的参数不要和外界冲突
* (proxy1, method, args1) -> {
* // before code
* Object res = method.invoke(src, args1);
* //after code
* return res;
* }
*/
}
}
CGlib代理
也属于动态代理的一种,通过继承要被代理的类来实现的代理,所以并不需要被代理对象一定实现接口,被代理类也不能是final的,要代理的方法不能是final和static,因为这些都是无法继承的或复写的
需要引入相关jar包,从maven官网中查找CGlib并下载,以及下载相关依赖(ams、ant和junit)包导入项目即可
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Main {
static class Src {
public void hello() {
System.out.println("hello world");
}
}
public static void main(String[] args) {
Src src = new Src(); //要被代理的源对象
Enhancer enhancer = new Enhancer(); //创建增强器,用来生成代理类对象
enhancer.setSuperclass(src.getClass()); //设置要代理的类,即代理对象要继承的父类
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// before code
Object res = method.invoke(src, args);
//after code
return res;
}
});
//设置方法拦截器对象,即与jdk中调用处理器对象一举同工
Src proxy = (Src) enhancer.create(); //创建代理对象,强转成要代理对象,即父类对象
proxy.hello(); //代理调用方法
}
}
适用场景
- 保护目标对象
- 增强目标对象
优缺点
优点:代理模式能将代理对象与真实被调用的目标对象分离,并且保护了目标对象,一定程度上降低系统耦合度,扩展性好
缺点:会造成系统设计中类的数目增加,增加了系统复杂度,客户与目标对象中间增加一个代理对象会造成请求处理速度变慢
外观模式
定义
提供一个统一的高层接口,用来访问子系统中的一群接口,让子系统更容易适用
UML类图
适用场景
- 子系统复杂,可增加外观模式提供简单调用接口
- 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
优缺点
优点:简化了调用过程,无需了解深入子系统,防止带来风险,减少系统依赖、松散耦合,更好的划分访问层次
缺点:增加子系统、扩展子系统行为容易引入风险
桥接模式
定义
将抽象部分和与具体行为实现部分分离了,使之可以独立化变化,通过组合方式建立类之间关系而不是继承
UML类图
适用场景
- 抽象和具体实现之间需要增加更多灵活性
- 一个类存在两个或多个变化维度,并且这些维度需要独立变化,也就是说抽象部分需要独立扩展,实现部分也需要独立扩展
- 不希望使用继承,或因为多层继承导致系统类个数爆炸
优缺点
优点:分离抽象部分以及具体实现部分,提高了系统的可扩展性
缺点:增加了系统的理解和设计难度,需要能正确的识别出系统中两个独立变化的维度
组合模式
定义
将对象组成树形结构表示部分到整体的层次结构(包含关系),客户对单个对象和组合对象保持一致处理方式
UML类图
适用场景
- 希望客户可以忽略组合对象与单个对象的差异
- 处理树形结构时
优缺点
优点:清楚定义分层次的复杂对象,表示对象全部或部分层次,使得客户忽略层次差异,从而方便对整个层次结构进行控制,同时还简化了客户的代码
缺点:限制类型时较为复杂,并且设计会更加抽象
享元模式
定义
提供减少对象数量从而改善应用所需的对象结构方式,使用共享技术有效的支持大量细粒度的对象
UML类图
适用场景
- 用于系统底层开发,以便解决系统性能问题
- 系统中有大量相似对象,需要缓冲池
优缺点
优点:减少对象创建,降低内存中对象的数量,降低系统的内存以及其他资源占用,提高效率
缺点:关注内外部状态和线程安全问题,使得系统程序逻辑复杂化
Comments NOTHING