本篇介绍结构型模式中的后3种: 门面、代理、桥梁
Facade
门面模式:
1 门面(外观)模式属于对象型结构模式,使外部系统与内部子系统的交互通过统一的门面对象进行,这就是门面模式
2 门面模式提供一个高层次的接口,使得子系统对外部更容易被使用
考虑一个现实点的例子--保安系统。保安的日常工作就是监视大楼及周边环境、维护大楼治安等,如有异常情况发生还会触警报器(火灾烟雾报警器、强烈震动响铃)
一个保安可能要负责很多灯管的维护开关,很多的监控录像机的开关,很多警报器的开关等繁杂的事情。这时如果引入门面模式,将所有的这些设备提供统一的操作、
管理接口,则会大大减轻保安员的工作负担,安保员甚至可以坐在保安室看着电脑大屏就能够对整个大楼的情况了如指掌。 示例类代码如下:
警报器
package com.jelly.mypattern.facade;
/**
* 警报器
* @author jelly
*
*/
public class Alarm {
public void activate(){
System.out.println("启动警报器");
}
public void deactivate(){
System.out.println("关闭警报器");
}
public void ring(){
System.out.println("拉响警报器,发出警报");
}
public void stopRing(){
System.out.println("停止警报");
}
}
录像机
package com.jelly.mypattern.facade;电灯泡
/**
* 录像机
* @author jelly
*
*/
public class Camera {
public void trunON(){
System.out.println("打开录像机...");
}
public void trunOFF(){
System.out.println("关闭录像机...");
}
public void rotate(int degress){
System.out.println("录像机 旋转:"+degress+" 度");
}
}
package com.jelly.mypattern.facade;/** * 灯泡 * @author jelly * */public class Light { public void trunON(){ System.out.println("打开电灯..."); } public void trunOFF(){ System.out.println("关闭电灯 ..."); } public void changeBulb(){ System.out.println("换灯泡..."); }}感应器
package com.jelly.mypattern.facade;门面类
/**
* 感应器
* @author jelly
*
*/
public class Sensor {
public void activate(){
System.out.println("启动感应器...");
}
public void deactivate(){
System.out.println("关闭感应器...");
}
public void trigger(){
System.out.println("触发了感应器");
}
}
package com.jelly.mypattern.facade;
/**
* 安保系统 门面类
* 假设一个保安需要管理以下设备:
* 2个录像机
* 3个灯泡
* 1个感应器
* 1个警报器
* @author jelly
*
*/
public class SecurityFacade {
private Camera camera1 ,camera2;
private Light light1,light2,light3;
private Sensor sensor;
private Alarm alarm;
public SecurityFacade() {
camera1=new Camera();
camera2=new Camera();
light1=new Light();
light2=new Light();
light3=new Light();
sensor=new Sensor();
alarm=new Alarm();
}
/**
* 保安员开始工作 监视周边环境
*/
public void activate(){
camera1.trunON();
camera2.trunON();
light1.trunON();
light2.trunON();
light3.trunON();
sensor.activate();
alarm.activate();
}
/**
* 保安员结束工作 关闭所有设备
*/
public void deactivate(){
camera1.trunOFF();
camera2.trunOFF();
light1.trunOFF();
light2.trunOFF();
light3.trunOFF();
sensor.deactivate();
alarm.deactivate();
}
}
客户端测试代码
package com.jelly.mypattern.facade;
public class FacadeTest {
/**
* Facade
* 门面模式:
* 1 门面(外观)模式属于对象型结构模式,使外部系统与内部子系统的交互通过统一的门面对象进行,这就是门面模式
* 2 门面模式提供一个高层次的接口,使得子系统对外部更容易被使用
* 3 例子 安保系统
* @param args
*/
public static void main(String[] args) {
SecurityFacade securityFacade=new SecurityFacade();
System.out.println("保安员上班---");
securityFacade.activate();
System.out.println("保安员下班---");
securityFacade.deactivate();
}
}
Proxy
代理模式:
1 代理主题并不改变主题接口,因为代理的意图是不让客户端感觉到代理的存在。
2 代理主题使用对象委派,将客户端的请求委派到真实的主题对象。
3 代理主题在传递请求的之前、之后都可以执行一些特定的操作,而不是单纯地传递请求。
示例类代码如下:
抽象主题角色
package com.jelly.mypattern.proxy;真实主题角色
/**
* 抽象主题角色
* 声明了代理主题和真实主题的共同接口
* @author jelly
*
*/
public abstract class Subject {
/**
* 发出请求方法
*/
public abstract void request();
}
package com.jelly.mypattern.proxy;/** * 真实主题角色: * 继承抽象主题 * @author jelly * */public class RealSubject extends Subject { public RealSubject() { super(); } @Override public void request() { System.out.println("发送请求 request"); }}现在想在发出请求之前、之后做一些操作,可以引入代理类。
代理主题角色
package com.jelly.mypattern.proxy;客户端测试代码
/**
* 代理主题角色:
* 含有真实主题角色的引用,从而可以在任何时候操作真实主题,
* 代理主题角色提供与真实主题相同的接口,以便在任何时候都可以代替真实主题,
* 控制对真实主题的调用
* @author jelly
*
*/
public class ProxySubject extends Subject{
private RealSubject realSubject ;
@Override
public void request() {
if(realSubject==null){
realSubject=new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
/**
* 执行request之前的一些操作
*/
public void preRequest(){
System.out.println("do preRequest...");
}
/**
* 执行request之后的一些操作
*/
public void postRequest(){
System.out.println("do postRequest...");
}
}
package com.jelly.mypattern.proxy;
/**
* 客户端测试类
* @author jelly
*
*/
public class ProxyTest {
/**
* Proxy
* 代理模式:
* 1 代理主题并不改变主题接口,因为代理的意图是不让客户端感觉到代理的存在。
* 2 代理主题使用对象委派,将客户端的请求委派到真实的主题对象。
* 3 代理主题在传递请求的之前、之后都可以执行一些特定的操作,而不是单纯地传递请求
* @param args
*/
public static void main(String[] args) {
Subject subject=new ProxySubject();
subject.request();
}
}
扩展:
代理模式是如此之重要,以至于java 在jdk1.3中就内置了支持动态代理功能支持,它提供了关键的3个类Proxy , InvocationHandler , Method实现了基于反射的动态代理。
java JDK中动态的代理
1 涉及三个类:Proxy , InvocationHandler , Method(都是java.lang.reflect包中)
2 生成的代理类继承自继承自java.lang.reflect.Proxy 并实现了程序员指定的接口(数组)中所有方法
3 生成的代理类重写了toString equals hashCode 方法
4 目标类(被代理类)必须实现接口,即JDK的动态代理只能代理实现了接口的类。
实例代码如下:
学生业务接口
package com.jelly.mypattern.proxy;学生业务类
public interface IStudentService {
public void save(Student student);
public Student findById(int id);
public void update(Student student);
}
package com.jelly.mypattern.proxy;
public class StudentService implements IStudentService{
@Override
public void save(Student student) {
System.out.println("保存 Student 对象成功");
}
@Override
public Student findById(int id) {
Student student=new Student();
student.setId(id);
student.setName("张晓华");
student.setGrade("小学3年级");
student.setChinese(80);
student.setMath(80);
student.setEnglish(80);
return student;
}
@Override
public void update(Student student) {
System.out.println("更新 Student 对象成功");
}
}
现在想在业务类方法执行时记录日志:统计业务方法的执行耗时。这时可以引入统计执行时间的代理处理器类。
package com.jelly.mypattern.proxy;客户端测试代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 统计业务方法执行耗时的 handler
* @author jelly
*
*/
public class LogBusinessTimeHandler implements InvocationHandler {
private Object target ;
LogBusinessTimeHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("=====开始执行目标业务方法:"+method.getName()+"======");
long startTime=System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime=System.currentTimeMillis();
System.out.println("=====执行业务方法完成,耗时: "+(endTime-startTime)+" 毫秒======");
return result;
}
}
package com.jelly.mypattern.proxy;控制台打印结果
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
public class ProxyTest2 {
public static void main(String[] args) {
IStudentService studentService=new StudentService();
LogBusinessTimeHandler handler=new
LogBusinessTimeHandler(studentService);
IStudentService studentServiceProxy= (IStudentService) Proxy.newProxyInstance(studentService.getClass().getClassLoader(),
studentService.getClass().getInterfaces(), handler);
Student stu= studentServiceProxy.findById(11);
System.out.println(stu);
stu.setName("张晓明");
studentServiceProxy.update(stu);
stu.setName("张小可");
studentServiceProxy.save(stu);
System.out.println(studentServiceProxy.getClass().getName());//类名 com.sun.proxy.$Proxy0
System.out.println(studentServiceProxy.getClass().getSimpleName());//类的简单名称 $Proxy0
printClassDefinition(studentServiceProxy.getClass());
}
@SuppressWarnings("rawtypes")
public static void printClassDefinition(Class clz){
String clzModifier = getModifier(clz.getModifiers());
if(clzModifier!=null && !clzModifier.equals("")){
clzModifier = clzModifier + " ";
}
String superClz = clz.getSuperclass().getName();
if(superClz!=null && !superClz.equals("")){
superClz = "extends " + superClz;
}
Class[] interfaces = clz.getInterfaces();
String inters = "";
for(int i=0; i<interfaces.length; i++){
if(i==0){
inters += "implements ";
}
inters += interfaces[i].getName();
}
System.out.println(clzModifier +clz.getName()+" " + superClz +" " + inters );
System.out.println("{");
Field[] fields = clz.getDeclaredFields();
for(int i=0; i<fields.length; i++){
String modifier = getModifier(fields[i].getModifiers());
if(modifier!=null && !modifier.equals("")){
modifier = modifier + " ";
}
String fieldName = fields[i].getName();
String fieldType = fields[i].getType().getName();
System.out.println(" "+modifier + fieldType + " "+ fieldName + ";");
}
System.out.println();
Method[] methods = clz.getDeclaredMethods();
for(int i=0; i<methods.length; i++){
Method method = methods[i];
String modifier = getModifier(method.getModifiers());
if(modifier!=null && !modifier.equals("")){
modifier = modifier + " ";
}
String methodName = method.getName();
Class returnClz = method.getReturnType();
String retrunType = returnClz.getName();
Class[] clzs = method.getParameterTypes();
String paraList = "(";
for(int j=0; j<clzs.length; j++){
paraList += clzs[j].getName();
if(j != clzs.length -1 ){
paraList += ", ";
}
}
paraList += ")";
clzs = method.getExceptionTypes();
String exceptions = "";
for(int j=0; j<clzs.length; j++){
if(j==0){
exceptions += "throws ";
}
exceptions += clzs[j].getName();
if(j != clzs.length -1 ){
exceptions += ", ";
}
}
exceptions += ";";
String methodPrototype = modifier +retrunType+" "+methodName+paraList+exceptions;
System.out.println(" "+methodPrototype );
}
System.out.println("}");
}
public static String getModifier(int modifier){
String result = "";
switch(modifier){
case Modifier.PRIVATE:
result = "private";
case Modifier.PUBLIC:
result = "public";
case Modifier.PROTECTED:
result = "protected";
case Modifier.ABSTRACT :
result = "abstract";
case Modifier.FINAL :
result = "final";
case Modifier.NATIVE :
result = "native";
case Modifier.STATIC :
result = "static";
case Modifier.SYNCHRONIZED :
result = "synchronized";
case Modifier.STRICT :
result = "strict";
case Modifier.TRANSIENT :
result = "transient";
case Modifier.VOLATILE :
result = "volatile";
case Modifier.INTERFACE :
result = "interface";
}
return result;
}
}
=====开始执行目标业务方法:findById======
=====执行业务方法完成,耗时: 0 毫秒======
Student [id=11, name=张晓华, grade=小学3年级, chinese=80.0, math=80.0, english=80.0]
=====开始执行目标业务方法:update======
更新 Student 对象成功
=====执行业务方法完成,耗时: 0 毫秒======
=====开始执行目标业务方法:save======
保存 Student 对象成功
=====执行业务方法完成,耗时: 0 毫秒======
com.sun.proxy.$Proxy0
$Proxy0
com.sun.proxy.$Proxy0 extends java.lang.reflect.Proxy implements com.jelly.mypattern.proxy.IStudentService
{
java.lang.reflect.Method m3;
java.lang.reflect.Method m1;
java.lang.reflect.Method m5;
java.lang.reflect.Method m0;
java.lang.reflect.Method m4;
java.lang.reflect.Method m2;
boolean equals(java.lang.Object);
java.lang.String toString();
int hashCode();
void save(com.jelly.mypattern.proxy.Student);
com.jelly.mypattern.proxy.Student findById(int);
void update(com.jelly.mypattern.proxy.Student);
}
说明程序在运行期动态地生成了一个代理类com.sun.proxy.$Proxy0 ,它继承自Proxy,并实现我们指定的com.jelly.mypattern.proxy.IStudentService接口。
代理类中执行的是统计方法执行耗时的代理处理器中invoke方法,而不是直接执行学生业务类(目标类)中的方法。
CGLIB 动态代理
1 CGlib是一个强大的,高性能,高质量的Code生成类库,比JDK动态代理(基于反射)更高效。
2 CGLIB是基于ASM底层框架通过转换字节码得到新的类。
3 CGLIB 代理不要求目标类实现某个接口(JDK动态代理要求目标类必须实现接口,在这点上cglib显然要做的更好),目标类非final
引入第三方jar包 cglib-nodep-3.1.jar,还是对学生业务类进行动态代理,新加入一个CGLIB的动态代理类。
package com.jelly.mypattern.proxy;CGLIB代理客户端测试代码
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyCGLIBProxy implements MethodInterceptor{
private Object target; //目标对象
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
// 回调方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("before...");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after...");
return result ;
}
}
package com.jelly.mypattern.proxy;
public class ProxyTest3 {
public static void main(String[] args) {
MyCGLIBProxy cglibProxy=new MyCGLIBProxy();
StudentService studentService =
(StudentService)cglibProxy.getInstance(new StudentService());
Student stu=new Student();
studentService.save(stu);
}
}
Bridge
桥梁模式:
1 将抽象化与实现化解耦,使得二者都可以独立地变化,这就是桥梁模式。
2 抽象化角色: 给出抽象化的定义,并持有一个实现化对象的引用。
3 修正抽象化角色:抽象化角色的自然扩展,修正抽象化角色的接口功能。
4 实现化角色:给出实现化接口,但不给出具体的实现。这个接口不一定与抽象化角色的接口定义相同,
实际上,二者可以非常不一样。实现化角色应当给出底层的操作,而抽象化给出的是基于底层更高一层的操作 。
5 具体实现化角色:实现实现化接口的具体角色,是实现化的自然扩展。
桥梁模式的一个典型的应用例子就是JDBC的驱动器。JDBC为所有的关系型数据库提供统一的界面。一个应用程序动态的选择一个驱动器,然后通过驱动器向数据库发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。抽象角色可以针对数据库引擎发出查询指令,因为抽象角色并不直接与数据库引擎打交道,JDBC驱动负责这个底层工作。由于JDBC驱动器的存在,应用系统可以不依赖于数据库引擎的细节而独立地演化;同时数据库引擎也不依赖于应用系统的细节而独立地演化。
抽象化角色
package com.jelly.mypattern.bridge;
/**
* 抽象化角色
* @author jelly
*
*/
public abstract class Abstracation {
protected Implementor imp;
public void setImp(Implementor imp){
this.imp=imp;
}
public void operation(){
imp.operationImp();
}
}
修正抽象化角色
package com.jelly.mypattern.bridge;实现化角色
/**
* 修正 抽象化角色
* @author jelly
*
*/
public class RefindAbstracation extends Abstracation {
@Override
public void operation() {
this.imp.operationImp();
System.out.println("扩展、修正抽象化功能。。。");
}
}
package com.jelly.mypattern.bridge;具体实现化类A
/**
* 实现化角色
* @author jelly
*
*/
public abstract class Implementor {
public abstract void operationImp() ;
}
package com.jelly.mypattern.bridge;/** * 具体实现化类A * @author jelly * */public class ConcreteImplementorA extends Implementor{ @Override public void operationImp() { System.out.println("ConcreteImplementorA 的一些操作 "); }}具体实现化类B
package com.jelly.mypattern.bridge;
/**
* 具体实现化类 B
* @author jelly
*
*/
public class ConcreteImplementorB extends Implementor{
@Override
public void operationImp() {
System.out.println("ConcreteImplementorB 的一些操作 ");
}
}
客户端测试代码
package com.jelly.mypattern.bridge;
/**
* 桥梁模式 测试代码
* @author jelly
*
*/
public class BridgeTest {
public static void main(String[] args) {
Abstracation abstration=new RefindAbstracation();
Implementor imp=new ConcreteImplementorA();
abstration.setImp(imp);
// Implementor imp2=new ConcreteImplementorB();
// abstration.setImp(imp2);
abstration.operation();
}
}
控制台输出
ConcreteImplementorA 的一些操作
扩展、修正抽象化功能。。。
。。。。。。