设计模式之结构型模式
结构型模式
主要以 组合关系 或 聚合关系 组成
结构型模式分为以下 7 种:
代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
除了适配器模式分为类结构型模式和对象结构型模式两种,其他的全部属于对象结构型模式
代理模式
定义:给某对象提供一个代理以控制对该对象的访问
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
缺点:
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
静态代理
一个明星, 一般都有经纪人, 处理 公司, 活动, 粉丝 一些任务, 所以 经纪人 就是 这个明星的代理人
代码实例:
//接口
public interface SuperStarService {
void company();
}
//实现
public class SuperStarServiceImple implements SuperStarService{
@Override
public void company() {
System.out.println("与公司洽谈");
}
}
//代理
public class SuperStarProxyForCompany {
private SuperStarService superStarService;
public SuperStarProxyForCompany (SuperStarService superStarService) {
this.superStarService = superStarService;
}
public void company() {
System.out.println("经纪人代理去公司开始");
superStarService.company();
System.out.println("经纪人代理去公司结束");
}
}
//测试
public class StaticProxyMain {
public static void main(String[] args) {
SuperStarProxyForCompany proxy = new SuperStarProxyForCompany(new SuperStarServiceImple());
proxy.company();
}
}
假设我们又另外一个类也实现了业务接口,即明星B : class SuperStarBImple implements SuperStarDao,此时如果要加多一种代理, 那么就是要创建多一个代理类, 需要代理的方法越多, 代理类越多, 动态代理就可以解决这类问题.
动态代理
JDK动态代理 (基于接口实现)
动态代理采用反射的机制,在运行时创建一个接口类的实例。在JDK的实现中,我们需要借助Proxy类和InvocationHandler接口类
实现步骤如下:
1.定义一个类去实现InvocationHandler接口,这个接口下有一个invoke(Object proxy, Method method, Object[] args) 方法,它负责调用对应接口的接口方法;
- proxy: 生成的代理对象
- method: 原对象的方法
- args: 原对象的方法参数, 参考反射调用方法 method.invoke(object, args)
2.调用代理类的方法时,处理程序会利用反射,将代理类、代理类的方法、要调用代理类的参数传入这个函数,并运行这个函数,这个函数是实际运行的,我们在这里编写代理的核心代码。
通过Proxy.newProxyInstance()创建某个interface实例,它需要3个参数:
- ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader
类a , a.getCalss().getClassLoader(), 目标对象的类加载器 - Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。
- InvocationHandler h : 我们自己写的,代理类要完成的功能。
代码实现:
//动态代理的主要方法
public class SuperStartDynamicProxy implements InvocationHandler {
//获取需要被代理的接口对象, 只有实现接口,才能被代理
//private SuperStarService superStarService;
private Object service;
public SuperStartDynamicProxy(Object service) {
this.service = service;
}
//用来获取代理对象 $Proxy
public Object getProxyObject() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), service.getClass().getInterfaces(), this);
}
//代理对象需要执行的代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理开始");
//这里是原对象的方法
Object invoke = method.invoke(service, args);
System.out.println("动态代理结束");
return invoke; //原来调用方法返回值, 如果是void返回 null
}
}
//测试方法
public class DynamicProxyMain {
public static void main(String[] args) {
SuperStarService superStarService1 = new SuperStarServiceImpl();
SuperStarService superStarService = (SuperStarService) new SuperStartDynamicProxy(superStarService1).getProxyObject();
superStarService.company();
}
}
cglib动态代理 (基于继承实现)
CGLIB(Code Generation Library)是一个开源、高性能、高质量的Code生成类库(代码生成包)。
它可以在运行期扩展Java类与实现Java接口。Hibernate用它实现PO(Persistent Object 持久化对象)字节码的动态生成,Spring AOP用它提供方法的interception(拦截)。
CGLIB的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。但不鼓励大家直接使用ASM框架,因为对底层技术要求比较高。
代码示例:
首先引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
创建类, 代理方法, 测试类
public class SuperStart {
public void singing() {
System.out.println("唱歌");
}
public String say(String word) {
System.out.println(word);
return "hello world";
}
}
public class SuperStartCglibProxy implements MethodInterceptor {
/**
*
* @param obj 表示要进行增强的对象
* @param method 表示拦截的方法
* @param args 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
* @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
* @return 执行结果
* @throws Throwable 异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before(method.getName());
Object result = methodProxy.invokeSuper(obj, args);
after(method.getName());
return result;
}
/**
* 调用invoke方法之前执行
*/
private void before(String methodName) {
System.out.println("调用方法" + methodName +"之【前】的日志处理");
}
/**
* 调用invoke方法之后执行
*/
private void after(String methodName) {
System.out.println("调用方法" + methodName +"之【后】的日志处理");
}
}
//测试方法
public class CglibProxyMain {
public static void main(String[] args) {
// 通过CGLIB动态代理获取代理对象的过程
// 创建Enhancer对象,类似于JDK动态代理的Proxy类
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(SuperStart.class);
// 设置回调函数
enhancer.setCallback(new SuperStartCglibProxy());
// create方法正式创建代理类
SuperStart superStart = (SuperStart