zoukankan      html  css  js  c++  java
  • 动态代理相对于静态代理的优势

    整理自知乎;整理自知乎;整理自知乎

    静态代理与动态代理

    代理模式是一种设计模式,典型的分为静态代理和动态代理。
    先讲静态代理
    首先给个场景,现有个FontProvider接口,用于获取字体
    public interface FontProvider {
    
        Font getFont(String name);
    }

    具体的实现类有三种,分别是FontProviderFromDisk,FontProviderFromNet,FontProviderFromSystem

    public class FontProviderFromDisk implements FontProvider {
    
        @Override
        public Font getFont(String name) {
            System.out.printf("get %s from disk
    ",name);
            return null;
        }
    }
    
    public class FontProviderFromNet implements FontProvider {
    
        @Override
        public Font getFont(String name) {
            System.out.printf("get %s from net
    ",name);
            return null;
        }
    }
    
    
    public class FontProviderFromSystem implements FontProvider {
    
        @Override
        public Font getFont(String name) {
            System.out.printf("get %s from system
    ",name);
            return null;
        }
    }

    然后我们使用上面FontProvider的实现类,获取字体,但是我突然想增加个缓存的功能以提高效率!怎么办呢?第一种就是在各个FontProvider实现类中添加缓存的代码,这样显然不理想,万一我这有一百个FontProvider实现类呢,那岂不是得修改一百多个FontProvider的实现类?第二种就是利用静态代理控制方法的调用(从缓存中获取成功,则不调用委托类的getFont()方法,获取失败则老老实实调用委托类的getFont()方法),这样即使有一百个FontProvider实现类也可以保证就仅有一份实现字体缓存的代码。

    public class CachedFontProvider implements FontProvider {
    
        private FontProvider fontProvider; //委托类
        private Map<String, Font> cached;
        public CachedFontProvider(FontProvider fontProvider) {
            this.fontProvider = fontProvider;
            cached = new HashMap<>();
        }
    
        /**
         * 绑定委托类,依靠这个方法可以运行时更换委托类
         * @param fontProvider
         */
        public void bindTo(FontProvider fontProvider) {
            this.fontProvider = fontProvider;
            cached.clear(); //委托类更换,缓存清除
        }
    
        @Override
        public Font getFont(String name) {
    
            Font font = cached.get(name);
            if(font == null) { //从缓存中获取失败,调用委托类的getFont()方法
                font = fontProvider.getFont(name);
                cached.put(name, font);
                return font;
            } else { //从缓存中获取成功,无需调用委托类的getFont()方法
                return font;
            }
        }
    }
    自此,静态代理的作用就体现出来了,它可以控制方法的调用,处理方法调用的结果。
     
    现在,我又添加了ColorProvider,ShapeProvider两个结果,各自也拥有若干实现类,现在我也想为各个XXXProvider添加缓存功能,咋办?
    这还不简单,静态代理啊,好不好?!
    public class CachedColorProvider implements ColorProvider {
    
        private ColorProvider colorProvider; //委托类
        private Map<String, Color> cached;
        public CachedColorProvider(ColorProvider colorProvider) {
            this.colorProvider = colorProvider;
            cached = new HashMap<>();
        }
    
        /**
         * 绑定委托类,依靠这个方法可以运行时更换委托类
         * @param ColorProvider
         */
        public void bindTo(ColorProvider colorProvider) {
            this.colorProvider = colorProvider;
            cached.clear(); //委托类更换,缓存清除
        }
    
        @Override
        public Color getColor(String name) {
    
            Color color = cached.get(name);
            if(Color == null) { //从缓存中获取失败,调用委托类的getColor()方法
                Color = ColorProvider.getColor(name);
                cached.put(name, color);
                return color;
            } else { //从缓存中获取成功,无需调用委托类的getColor()方法
                return color;
            }
        }
    }
    
    public class CachedShapeProvider implements ShapeProvider {
    
        private ShapeProvider shapeProvider; //委托类
        private Map<String, Shape> cached;
        public CachedShapeProvider(ShapeProvider shapeProvider) {
            this.shapeProvider = shapeProvider;
            cached = new HashMap<>();
        }
    
        /**
         * 绑定委托类,依靠这个方法可以运行时更换委托类
         * @param shapeProvider
         */
        public void bindTo(ShapeProvider shapeProvider) {
            this.shapeProvider = shapeProvider;
            cached.clear(); //委托类更换,缓存清除
        }
    
        @Override
        public Shape getShape(String name) {
    
            Shape shape = cached.get(name);
            if(shape == null) { //从缓存中获取失败,调用委托类的getShape()方法
                shape = shapeProvider.getShape(name);
                cached.put(name, shape);
                return shape;
            } else { //从缓存中获取成功,无需调用委托类的getShape()方法
                return shape;
            }
        }
    }
    上面的代码乍看实现了功能,但是仍不理想,代码冗余太高,还是那个“万一”,万一我这还有ImageProvider、MusicProvider等等数百个XXXProvider呢?为每个XXXPrivder提供一个CachedXXXProvider自然不现实。
    这就是静态代理的不足之处,它的实现方式以接口实现的形式与委托类绑定在了一起,所以决定了一个委托类对应一个代理类的模式(FontProvider对应CachedFontProvider,ShapeProvider对应CachedShapeProvider,ColorProvider对应CachedColorProvider),如果在委托类很多的应用场景,静态代理显然力不从心。这个时候,使用动态代理就可以打破这种限制。
     
    public class CachedProviderHandler implements InvocationHandler {
        private Map<String, Object> cached = new HashMap<>();
        private Object target;
    
        public CachedProviderHandler(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
            Type[] types = method.getParameterTypes();
            if (method.getName().matches("get.+") && (types.length == 1) &&
                    (types[0] == String.class)) { //控制getFont(),getColor(),getShape()等方法的访问
                String key = (String) args[0];
                Object value = cached.get(key);
                if (value == null) {
                    value = method.invoke(target, args);
                    cached.put(key, value);
                }
                return value;
            }
            return method.invoke(target, args);
        }
    }
    
    //生成FontProvider的代理对象
    Proxy.newProxyInstance(FontProvider.class.getClassLoader(),new Class[]{FontProvider.class},new CachedProviderHandler(new FontProviderFromDisk()));
    
    //生成ShapeProvider的代理对象
    Proxy.newProxyInstance(ShapeProvider.class.getClassLoader(),new Class[]{ShapeProvider.class},new CachedProviderHandler(new ShapeProviderFromDisk()));
    
    ...
    一个 CachedProviderHandler就可以代理 FontProvider,ShapeProvider,ColorProvider多个XXXProvider,岂不美哉?

    总结

    至此,我们知道动态代理相对于静态代理的优势:就静态代理而言,在委托类特别多的应用场景,就要相应的添加许多的代理类,这显然增加了应用程序的复杂度,而使用动态代理就可以减少代理类的数量,相对降低了应用程序的复杂度
  • 相关阅读:
    Oracle学习笔记<5>
    Oracle学习笔记<4>
    fabric动态获取远程目录列表
    fabric查看本地与远程主机信息
    fabric安装使用
    pexpect实现远程操作
    pexpect的pxssh类实现远程操作
    Most Distant Point from the Sea
    Art Gallery
    Rotating Scoreboard(半平面交模板题)
  • 原文地址:https://www.cnblogs.com/fudashi/p/7117365.html
Copyright © 2011-2022 走看看