零. 结构型模式概述
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构模式和对象结构模式,前者采用继承机制来组织接口和类,后者采用组合和聚合来组合对象,由于组合关系或聚合关系比继承关系耦合低,满足“合成复用原则”,故对象结构型模式比类结构型模式具有更多灵活性。
结构型模式分为以下 7种:
1. 代理模式(Proxy Pattern):
为某种对象提供一种代理以控制对该对象的访问,即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
2. 适配器模式(Adapter Pattern)
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容二不能一起工作的那些类能一起工作;
3. 桥接模式(Birdge Pattern)
将抽象与实现分离,使它们可独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
4. 装饰模式(Decorator Pattern)
动态地给对象增加一些职责,即增加其额外的功能;
5. 外观模式(Facade Pattern)
为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问;
6. 享元模式(Flyweight Pattern)
运用共享技术来有效地支持大量细粒度对象的复用;
7. 组合模式(Composite Pattern)
将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性;
以上 7 种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式两种,其他的全部属于对象结构型模式;
一. 代理模式的定义
在有些情况下,一个客户不能或不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。生活中,如通过12306买票,打官司等,在软件设计中,要访问的远程对象比较大(如视频或大图像等)其下载时间长或因安全原因屏蔽客户端就会用到代理模式;
1. 模式概述
由于某些原因不能直接访问目标对象,需要提供一个代理以控制对该目标对象的访问,代理对象作为访问对象和目标对象间的中介。
给对象提供副本,通过这个 副本对象 来控制 真实对象 的访问,那么就算想扩展新的内容或功能,在副本对象上修改即可
-
该模式通过代理对象访问目标对象,目的是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能;
-
被代理的对象可以是远程对象,创建开销大的对象 或 需要安全控制的对象;
-
有三种不同形式: 静态代理、动态代理(接口代理)、Cglib代理(可在内存中动态创建对象,而不需实现接口,属于动态代理)
2. 优点
-
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
-
代理对象可以扩展目标对象的功能
-
代理模式能将客户端与目标对象分离在一定程度上降低了系统的耦合度,增加了程序的可扩展性;
3. 缺点
-
代理模式会造成系统设计中类的数量增加,增加了系统的复杂度;
-
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
-
使用场景
1>当无法直接引用某个对象时,可通过代理对象来间接访问,为保证客户端使用的透明性,代理和真实主题类都要实现抽象主题接口;
2>被访问对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容;
3>根据适用范围,代理模式可以分为以下几种
1)远程代理: 为一个对象在不同的地址空间提供局部代表,这样系统可以将Server部分的事项隐藏;
2)虚拟代理: 如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建;
3)安全代理: 用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限;
4)智能引用: 在引用原始对象的时候附加操作,并对指向原始对象的引用增加引用计数;
5)延迟加载:为了提供系统的性能,延迟对目标的加载。
-
模式的结构
代理模式角色分析:
1. Subject(抽象角色):用于声明真实角色与代理角色的共同方法,一般为一个接口;
2. RealSubject(真实角色):需要实现抽象角色接口来实现具体的业务逻辑,以供代理角色调用,它是处理真正业务逻辑的类;
3. Proxy(代理角色):同样实现抽象角色接口,并在内部扩展并调用真实角色对应的接口方法,将统一流程控制都放到此处处理;
4. Client(客户端类) :使用代理模式访问真实角色的地方;
-
模式的实现--海淘
1.静态代理
1>代理对象与被代理对象(目标对象) 要实现相同的接口方法,然后通过调用相同的方法来调用目标对象的方法;
2>应用实例
1)定义一个接口: ITeacherDao;
2)目标对象TeacherDAO实现ITeacherDao
3)用静态代理方式,就需要在代理对象TeacherDaoProxy中也实现ITeacherDao;
4)调用的时候通过调用代理对象的方法来调用目标对象;
5)代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法(切记);
-
静态代理实例结构图
-
静态代理代码示例
public class ProxyClient {
public static void main(String[] args) {
OverseaProxy proxy = new OverseaProxy();//静态代理
proxy.buy("牛奶");
}
}
/**
* 创建抽象主题类 海购 待实现购买的方法
*/
public interface IShopping {
void buy(String goodsName);//购买
}/**
* 创建真实主题类---国内买主
* 国内的人想购买某些产品,定义具体的购买过程
*/
public class NativePeople implements IShopping{
@Override
public void buy(String goodsName) {
System.out.println("国内要买"+goodsName);
}
}public class OverseaProxy implements IShopping{
private NativePeople user;//持有真实主题类的引用
@Override
public void buy(String goodsName) {
//创建国内购买人(真实角色实例)
if (user == null) {
user = new NativePeople();
}
preBuy();
user.buy(goodsName);//调用了被代理者的buy()方法
postBuy();
}
private void preBuy() {
System.out.println("海淘前的预处理。");
}
private void postBuy() {
System.out.println("海淘后的后续处理。");
}
}
2.动态代理
1>基本介绍:
1)动态对象, 代理对象不需要实现接口, 只需要目标对象实现接口即可;
2)代理对象的生成, 是利用JDK的API,动态的在内存中构建代理对象;
3)动态代理也叫做: JDK代理, 接口代理;
2>JDK 中生成代理对象的API
1) 代理类所在包: java.lang.reflect.Proxy
2) JDK实现代理只需使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
3) 实际上,Proxy.newProxyInstance会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来的,
即没有真正的文件,只是在内存中按照Class格式生成了一个Class;
/**
* @param loader 指定当前目标对象使用的类的加载器
* @param interfaces: 目标对象实现的接口,可以设多个抽象主题接口
* @param h: InvocationHandler接口,执行目标对象方法时,会由这个监听回调处理,再通过反射得到执行的目标对象方法
*/
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
//抽象角色
interface Api {
void test(String a);
}
//真实角色
class ApiImpl{
@Override
public void test(String a) {
System.out.println("真实实现:" + a);
}
}
class Client{
//创建真实角色实例
ApiImpl api = new ApiImpl();
//JDK动态代理:
Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{Api.class}, //JDK实现只能代理接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行真实对象方法
return method.invoke(api, args);
}
});
}
String name = Api.class.getName()+"$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Api.class});
FileOutputStream fos = new FileOutputStream("lib/" + name+".class");
fos.write(bytes);
fos.close()
3>动态代理代码示例
public class DynamicProxy {
public void dynamicProxy(){
//创建国内购买人(真实角色实例)
NativePeople user = new NativePeople();
//被代理的对象(JDK动态代理)
Object obj = Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{IShopping.class},//创建动态代理类
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行真实对象方法
return method.invoke(user, args);
}
});
IShopping shop=(IShopping) obj;
preBuy();
shop.buy("牛奶");
postBuy();
}
private void preBuy() {
System.out.println("海淘前的预处理。");
}
private void postBuy() {
System.out.println("海淘后的后续处理。");
}
}
程序运行结果:
海淘前的预处理。
国内要买牛奶
海淘后的后续处理。
-
使用代理模式架构网络框架