Java设计模式之代理模式原理及实现代码分享

简介

Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生活中的这么一个概念:中介

代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

代理模式包含如下角色:

ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

RealSubject:真实主题角色,是实现抽象主题接口的类。

Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

实现动态代理的关键技术是反射。

静态代理

代理模式有几种,虚拟代理,计数代理,远程代理,动态代理。主要分为两类,静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。

考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现:

/**方式一:聚合式静态代理
 * @author Goser  (mailto:goskalrie@163.com)
 * @Since 2016年9月7日
 */
//1.抽象主题接口
public interface Manager {
  void doSomething();
}
//2.真实主题类
public class Admin implements Manager {
  public void doSomething() {
    System.out.println("Admin do something.");
  }
}
//3.以聚合方式实现的代理主题
public class AdminPoly implements Manager{
  private Admin admin;

  public AdminPoly(Admin admin) {
    super();
    this.admin = admin;
  }

  public void doSomething() {
    System.out.println("Log:admin操作开始");
    admin.doSomething();
    System.out.println("Log:admin操作结束");
  }
}
//4.测试代码
    Admin admin = new Admin();
    Manager m = new AdminPoly(admin);
    m.doSomething();
//方式二:继承式静态代理
//与上面的方式仅代理类和测试代码不同
//1.代理类
public class AdminProxy extends Admin {
  @Override
  public void doSomething() {
    System.out.println("Log:admin操作开始");
    super.doSomething();
    System.out.println("Log:admin操作开始");
  }
}
//2.测试代码
    AdminProxy proxy = new AdminProxy();
    proxy.doSomething();

聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。

比如,在管理员操作的同时需要进行权限的处理,操作内容的日志记录,操作后数据的变化三个功能。三个功能的排列组合有6种,也就是说使用继承要编写6个继承了Admin的代理类,而使用聚合,仅需要针对权限的处理、日志记录和数据变化三个功能编写代理类,在业务逻辑中根据具体需求改变代码顺序即可。

动态代理

一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。

但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。

JDK动态代理

实现

//1. 抽象主题
public interface Moveable {
  void move() throws Exception;
}
//2. 真实主题
public class Car implements Moveable {
  public void move() throws Exception {
    Thread.sleep(new Random().nextInt(1000));
    System.out.println("汽车行驶中…");
  }
}
//3.事务处理器
public class TimeHandler implements InvocationHandler {
  private Object target;
  public TimeHandler(Object target) {
    super();
    this.target = target;
  }
  /**
   * 参数:
   *proxy 被代理的对象
   *method 被代理对象的方法
   *args 方法的参数
   * 返回:
   *Object 方法返回值
   */
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    long startTime = System.currentTimeMillis();
    System.out.println("汽车开始行驶…");
    method.invoke(target, args);
    long stopTime = System.currentTimeMillis();
    System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
    return null;
  }
}
//测试类
public class Test {
  public static void main(String[] args) throws Exception{
    Car car = new Car();
    InvocationHandler h = new TimeHandler(car);
    Class<!--?--> cls = car.getClass();
    /**
     *loader 类加载器
     *interfaces 实现接口
     *h InvocationHandler
     */
    Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
    m.move();
  }
}

代码讲解:

在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。

JDK动态代理步骤

1.创建一个实现InvocationHandler接口的类,它必须实现invoke()方法

2.创建被代理的类及接口

3.调用Proxy的静态方法,创建一个代理类

4.通过代理调用方法

而为什么要进行如此操作,可以从Proxy和InvocationHandler的源码中找打答案。对源码不感兴趣的可以将下面的源码部分小节略过。

JDK动态代理原理与源码

newProxyInstance()方法的源码:

public static Object newProxyInstance(ClassLoader loader,
                     Class<!--?-->[] interfaces,
                     InvocationHandler h)
    throws IllegalArgumentException{
    if (h == null) {
      throw new NullPointerException();
    }
    final Class<!--?-->[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    /*查找或生成指定的代理类*/
    Class<!--?--> cl = getProxyClass0(loader, intfs);
    /*用指定的调用处理程序调用它的构造函数.*/
    try {
    //获得类的构造函数
      final Constructor<!--?--> cons =cl.getConstructor(constructorParams);
      final InvocationHandler ih = h;
      if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
       //当需要代理的类实现了一个非public的接口时,因为这样的接口需要特殊的权限,因此调用doPrivilege(native 修饰的方法)创建代理实例。
        return AccessController.doPrivileged(newPrivilegedAction<object>() {
          public Object run() {
            return newInstance(cons,ih);
          }
        });
      } else {
        return newInstance(cons,ih);
      }
    } catch (NoSuchMethodException e) {
      throw new InternalError(e.toString());
    }
}

可以看到,获得代理类的代码是

Classcl = getProxyClass0(loader,intfs);

并由此获得代理类的构造函数,生成代理类的实例返回给该方法的调用者。

继续跟进getProxyClass0()方法:

/** 生成代理类。调用该方法前必须使用checkproxyaccess方法执行权限检查。*/
  private static Class<!--?--> getProxyClass0(ClassLoader loader,
                     Class<!--?-->... interfaces) {
  //检查实现的接口数,65535这个数字好特殊,端口数好像也是这个,这个数字是由虚拟机所决定的,2^16-1个
  if (interfaces.length > 65535) {
      throw new IllegalArgumentException("interface limit exceeded");
    }
    // 如果代理类已经通过实现给定接口的类加载器创建了,则返回缓存中的该类的副本;否则将通过ProxyClassFactory创建代理类
    return proxyClassCache.get(loader, interfaces);
}

还是没有看到代理类是怎么生成的,只知道代理类是从proxyClassCache中取得的,这个变量是与缓存相关的一个对象,查看该变量的声明与初始化:

private static final WeakCache<classloader,>[], Class<!--?-->>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());</classloader,>

可以发现proxyClassCache是个用来缓存代理类的类变量,大家知道类变量的特点是与类一一对应,在一个虚拟机中类只有一个,对应着在一个虚拟机中类变量也只有一个,且在此处,在Proxy类被加载的时候就赋值了。在赋值操作的参数中有ProxyClassFactory()这么一个构造函数,这个是动态代理中的关键:生成代理类的类文件字节码。继续跟进去,找到代理类的生成之处了:

  /** 根据给定的类加载器和接口数组生成代理类的工厂类*/
  private static final class ProxyClassFactory
    implements BiFunction<classloader,class<?>[], Class<!--?-->>
  {
    // 所有代理类名称的前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    //用于生成唯一代理类名称的下一个序号
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<!--?--> apply(ClassLoader loader,Class<!--?-->[] interfaces) {

      Map<class<?>, Boolean>interfaceSet = new IdentityHashMap<>(interfaces.length);
      for (Class<!--?--> intf : interfaces) {
        /* 验证类加载器将此接口的名称解析为实际对象的名称。*/
        Class<!--?--> interfaceClass =null;
        try {
          interfaceClass = Class.forName(intf.getName(),false, loader);
        } catch (ClassNotFoundException e) {
        }
        if (interfaceClass != intf) {
          throw new IllegalArgumentException(
            intf + " is not visible from classloader");
        }
        /* 验证类对象确实是一个接口。*/
        if (!interfaceClass.isInterface()) {
          throw new IllegalArgumentException(
            interfaceClass.getName() + " is not an interface");
        }
        /*确保接口唯一*/
        if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
          throw new IllegalArgumentException(
            "repeated interface: " + interfaceClass.getName());
        }
      }

      String proxyPkg = null;   // 代理类的包名

      /*记录非公开代理接口的包,以便将代理类定义在同一个包中。确认所有非公共代理接口都在同一个包中。*/
      for (Class<!--?--> intf : interfaces) {
        int flags = intf.getModifiers();
        if (!Modifier.isPublic(flags)) {
          String name =intf.getName();
          int n = name.lastIndexOf('.');
          String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
          if (proxyPkg == null) {
            proxyPkg = pkg;
          } else if (!pkg.equals(proxyPkg)) {
            throw new IllegalArgumentException(
              "non-public interfaces fromdifferent packages");
          }
        }
      }

      if (proxyPkg == null) {
        // 如果没有非公开的代理接口,使用com.sun.proxy作为包名
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
      }

      /* 生成代理类名的序号*/
      long num = nextUniqueNumber.getAndIncrement();
      //生成全类名
      String proxyName = proxyPkg + proxyClassNamePrefix + num;

      /*生成代理类字节码 */
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);
      try {
        return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
      } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
      }
    }
}

在ProxyClassFactory中,可以看到产生代理类的具体逻辑,大致上是,根据传递的被代理类及其实现的接口生成代理类的字节码加载到缓存中,但是加载到缓存中只是一个.java文件也不能用,所以底层还有编译等操作。到这里,可以大致的看清JDK中动态代理的面孔了,实现的步骤为:

1.创建代理类的源码;

2.对源码进行编译成字节码;

3.将字节码加载到内存;

4.实例化代理类对象并返回给调用者;

底层的代码我们看不到,但是我们可以查看其生成的字节码:

//获得字节码的测试方法
  byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", Car.class.getInterfaces());
    FileOutputStream out = null;
    try {
      out = new FileOutputStream(System.getProperty("user.dir") + "\\$Proxy1.class");
      out.write(classFile);
      out.flush();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      try {
        out.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

//生成的字节码:
importcn.com.goser.proxy.imooc.staticproxy.Moveable;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy1 extends Proxy
 implements Moveable
{
 private static Method m1;
 private static Method m3;
 private static Method m0;
 private static Method m2;

 public $Proxy1(InvocationHandler paramInvocationHandler)
  throws
 {
  super(paramInvocationHandler);
 }

 public final boolean equals(Object paramObject)
  throws
 {
  try
  {
   return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
  }
  throw new UndeclaredThrowableException(localThrowable);
 }

 public final void move()
  throws Exception
 {
  try
  {
   this.h.invoke(this, m3, null);
   return;
  }
  catch (Exception localException)
  {
   throw localException;
  }
  catch (Throwable localThrowable)
  {
  }
  throw new UndeclaredThrowableException(localThrowable);
 }

 public final int hashCode()
  throws
 {
  try
  {
   return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
  }
  throw new UndeclaredThrowableException(localThrowable);
 }

 public final String toString()
  throws
 {
  try
  {
   return (String)this.h.invoke(this, m2, null);
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
  }
  throw new UndeclaredThrowableException(localThrowable);
 }

 static
 {
  try
  {
   m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
   m3 = Class.forName("cn.com.goser.proxy.imooc.staticproxy.Moveable").getMethod("move", new Class[0]);
   m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
   m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
   return;
  }
  catch (NoSuchMethodExceptionlocalNoSuchMethodException)
  {
   throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundExceptionlocalClassNotFoundException)
  {
  }
  throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
 }
}

生成的字节码比较长,但是在字节码中最关键的信息是代理类的声明:

public final class $Proxy1 extends Proxy

可以看到生成的代理类是继承了Proxy类的,这就是说明了为什么使用JDK动态代理不能实现继承式动态代理,原因是Java不允许多继承,而生成的代理类本身就已经继承了Proxy类。

至此,JDK的动态代理的使用及底层原理分析完毕,揭下动态代理的神秘面纱,果然是枚美女。

至于最底层的native方法是怎么动态生成代理类的字节码我们也可以简单的模拟一下,先分析下模拟的步骤:首先要生成一段代理类的源码,然后将源码编译后生成代理类的实例返回给调用者。依据此步骤开始编写我们的模拟代码:

/**
 * JDK java.lang.reflect.Proxy的模拟
 * @author Goser  (mailto:goskalrie@163.com)
 * @Since 2016年9月7日
 */
public class Proxy {
  private static final String RT = "\r\n";
  public static Object newProxyInstance() throws Exception{
    //声明一段源码
    String sourceCode =
    "packagecn.com.goser.proxy.jdk.simulate;"+ RT +
    "importcn.com.goser.proxy.imooc.staticproxy.Admin;" + RT +
    "importcn.com.goser.proxy.imooc.staticproxy.Manager;" + RT +
    "//以聚合方式实现的代理主题" + RT +
    "public class $Proxy0 implementsManager{" + RT +
    "  privateAdmin admin;" + RT +
    "  public$Proxy0(Admin admin) {" + RT +
    "    super();" + RT +
    "    this.admin= admin;" + RT +
    "  }" + RT +
    "  publicvoid doSomething() {" + RT +
    "    System.out.println(\"Log:admin操作开始\");" + RT +
    "    admin.doSomething();" + RT +
    "    System.out.println(\"Log:admin操作结束\");" + RT +
    "  }" + RT +
    "}";
    String filename = System.getProperty("user.dir") + "/src/main/java/cn/com/goser/proxy/jdk/simulate/$Proxy0.java";
    File file = new File(filename);
    //使用org.apache.commons.io.FileUtils.writeStringToFile()将源码写入磁盘
    //编写到处,可以运行一下程序,可以在当前目录中看到生成的.java文件
    FileUtils.writeStringToFile(file,sourceCode);
    //获得当前系统中的编译器
    JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
    //获得文件管理者
    StandardJavaFileManager fileMgr =complier.getStandardFileManager(null, null, null);
    Iterable its =fileMgr.getJavaFileObjects(filename);
    //编译任务
    CompilationTask task = complier.getTask(null, fileMgr, null, null, null, its);
    //开始编译,执行完可在当前目录下看到.class文件
    task.call();
    fileMgr.close();
    //load到内存
    ClassLoader loader = ClassLoader.getSystemClassLoader();
    Class cls = loader.loadClass("cn.com.goser.proxy.jdk.simulate.$Proxy0");
    //生成代理类对象
    Constructor ct = cls.getConstructor(Admin.class);
    return ct.newInstance(new Admin());
  }
}
class test{
  public static void main(String[] args) throws Exception {
    Manager m = (Manager)Proxy.newProxyInstance();
    m.doSomething();
  }
}

运行测试代码,结果和手工编写的结果一致,完成了JDK中动态代理的实现模拟。

cglib动态代理

前面分析到,因为Java只允许单继承,而JDK生成的代理类本身就继承了Proxy类,因此,使用JDK实现的动态代理不能完成继承式的动态代理,但是我们可以使用cglib来实现继承式的动态代理。

大名鼎鼎的Spring中就含有cglib动态代理,在此也以Spring中自带的cglib完成动态代理的实现:

//1.具体主题
public class Train{
  public void move(){
    System.out.println("火车行驶中…");
  }
}
//2.生成代理
public class CGLibProxy implements MethodInterceptor {
  private Enhancer enhancer = new Enhancer();
  public Object getProxy(Class<!--?--> clazz){
    enhancer.setSuperclass(clazz);
    enhancer.setCallback(this);
    return enhancer.create();
  }
  /**
   * 拦截所有目标类方法的调用
   * 参数:
   * obj目标实例对象
   *method 目标方法的反射对象
   * args方法的参数
   * proxy代理类的实例
   */
  public Object intercept(Object obj, Method method, Object[] args,
      MethodProxy proxy) throws Throwable {
    //代理类调用父类的方法
    System.out.println("日志开始");
    proxy.invokeSuper(obj, args);
    System.out.println("日志结束");
    return null;
  }
}
//3.测试
public class Test {
  public static void main(String[] args) {
    CGLibProxy proxy = new CGLibProxy();
    Train t = (Train) proxy.getProxy(Train.class);
    t.move();
  }
}

小结

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。class与interface的区别本来就模糊,在java8中更是增加了一些新特性,使得interface越来越接近class,当有一日,java突破了单继承的限制,动态代理将会更加强大。

以上就是本文关于Java设计模式之代理模式原理及实现代码分享的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:快速理解Java设计模式中的组合模式、Java设计者模式简单工厂模式解析、Java设计模式之访问者模式使用场景及代码示例等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!

时间: 2017-11-12

Java设计模式笔记之抽象工厂代码示例

上一篇说到了工厂模式,那么学习了工厂模式,抽象工厂也得学习一下.实际上,抽象工厂模式实际上就是在工厂模式的基础上再嵌套一层工厂模式而已,通过父工厂制造子工厂.只是,也并不完全是嵌套一层,各个工厂会被抽象成一个集多个工厂共同点的抽象类.通过工厂制造器,创建出该抽象工厂的子类. 好比如说,一个博客页面有个换肤系统.那么假如有两套风格,黑和白.那么,我选择了黑色风格的,实际这步就相当通过换肤系统这个工厂制造器,创建出一个黑色主题的工厂,该黑色主题的工厂内可以生产各种黑色风格的产品,比如黑色头像挂饰,黑

快速理解Java设计模式中的组合模式

组合模式是一种常见的设计模式(但我感觉有点复杂)也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系. 个人理解:组合模式就是将部分组装成整体. 定义如下: 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 通用类图如下: 组合模式的包含角色: ● Component 抽象构件角色 定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性. ● Leaf 叶子构件 叶子对象,其下再也没有其他的分支,也就是遍历的最

java 设计模式(DAO)的实例详解

java 设计模式(DAO)的实例详解 应用场景:在Java程序中,经常需要把数据持久化,也需要获取持久化的数据,但是在进行数据持久化的过程中面临诸多问题(如:数据源不同.存储类型不同.供应商不同.访问方式不同等等),请问如何能以统一的接口进行数据持久化的操作? 其实这个我没学号(≧ ﹏ ≦).我的理解就是一个产品面向的用户不是单一的,所以我们要兼容许多情况如前面提到的数据源不同.存储类型不同.供应商不同.访问方式不同等等. ★ 解决方案 DAO的理解: 1.DAO其实是利用组合工厂模式来解决问

Java设计模式之代理模式与装饰模式实例详解

本文实例讲述了Java设计模式之代理模式与装饰模式.分享给大家供大家参考,具体如下: 之所以把这两种模式放在一起说,是因为我发现这了两种模式几乎一模一样! 从网上也搜了一些资料,发现两者还是有一些区别的.我们在学习的同时也把这种困惑搞清楚. 定义: 代理模式,为其他对象提供一种代理以控制对这个对象的访问. 装饰模式,动态地给一个对象添加一些额外的职责. 代理模式,很好理解,就是把一个对象再次封装,以后就对封装的对象访问就可以了. 因为代理对象已经取代了被代理对象. 装饰模式,给一个对象增加功能,

Java设计模式之静态工厂模式详解

本文实例讲述了Java设计模式之静态工厂模式.分享给大家供大家参考,具体如下: 静态工厂模式(static factory)也叫简单工厂模式. 涉及到3个角色:工厂类角色,抽象产品类角色和具体产品类角色. 抽象产品类可以使用接口或者父类来描述产品对象的行为特征. 具体产品类就是某一具体的对象. 静态工厂类有一个静态的方法,含有判断逻辑,决定要创建哪一种具体的产品对象. 其设计模式如下: 抽象产品类  IProduct package org.test.design.sf; public inte

Java设计模式之访问者模式使用场景及代码示例

Java设计模式访问者模式 模式概念 访问者模式表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作.访问者模式适用于数据结构相对稳定算法又易变化的系统,若系统数据结构对象易于变化,则不适合使用访问者模式.访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者. Visitor应用场景 一定会有的疑问:visitor和iterator的区别: visitor可以访问不同的对象(只需要在Element定义对应的accept),但是Ite

JAVA设计模式之访问者模式原理与用法详解

本文实例讲述了JAVA设计模式之访问者模式.分享给大家供大家参考,具体如下: 访问者模式: 一个作用于某对象结构中各元素的操作,使你可以在不改变各元素类数据结构的前提下增加作用于这些元素的新操作. 结构对象是访问者模式必备条件,且这个结构对象必须存在遍历自身各个对象的方法. 适用于:数据结构相对稳定,把数据结构和作用与其上的其它操作解耦,使得操作相对自由. 优点: 1.符合单一职责原则 2.扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展. 缺点: 1.如果要增加新元素,则会让操

JAVA设计模式之访问者模式详解

在阎宏博士的<JAVA与模式>一书中开头是这样描述访问者(Visitor)模式的: 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变. 分派的概念 变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type):而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type).比如: 复制代码 代码如下: List lis

Java设计模式之备忘录模式_动力节点Java学院

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

Java设计模式之迭代器模式_动力节点Java学院整理

定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 类图: 如果要问Java中使用最多的一种模式,答案不是单例模式,也不是工厂模式,更不是策略模式,而是迭代器模式,先来看一段代码吧: public static void print(Collection coll){ Iterator it = coll.iterator(); while(it.hasNext()){ String str = (String)it.next(); System.out

Java设计模式之策略模式_动力节点Java学院整理

定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 类图: 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换.在前面说过的行为类模式中,有一种模式也是关注对算法的封装--模版方法模式,对照类图可以看到,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中

Java设计模式之解释器模式_动力节点Java学院整理

定义:给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子. 类型:行为类模式 类图: 解释器模式是一个比较少用的模式,本人之前也没有用过这个模式.下面我们就来一起看一下解释器模式. 解释器模式的结构 抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作.具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器Nonterm

Android编程设计模式之访问者模式详解

本文实例讲述了Android编程设计模式之访问者模式.分享给大家供大家参考,具体如下: 一.介绍 访问者模式是一种将数据操作与数据结构分离的设计模式,它是<设计模式>中23种设计模式中最复杂的一个,但它的使用频率并不高,正如<设计模式>的作者GOF对访问者模式的描述:大多数情况下,你不需要使用访问者模式,但是当你一旦需要使用它时,那你就是真的需要它了. 访问者模式的基本想法是,软件系统中拥有一个由许多对象构成的.比较稳定的对象结构,这些对象的类都拥有一个accept方法用来接受访问

JS设计模式之访问者模式定义与用法分析

本文实例讲述了JS设计模式之访问者模式定义与用法.分享给大家供大家参考,具体如下: 在访问者模式中,主要包括下面几个角色 1.抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的. 2.访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情. 3.抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的.抽象元素一般有两类方法,一部分是本身的业务逻辑,另外