1. 程式人生 > >動態代理相對於靜態代理的優勢

動態代理相對於靜態代理的優勢

oid 無需 int class ash 總結 proxy etc ride

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

靜態代理與動態代理

代理模式是一種設計模式,典型的分為靜態代理和動態代理。 先講靜態代理 首先給個場景,現有個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\n",name); return null; } } public class FontProviderFromNet implements FontProvider { @Override public Font getFont(String name) { System.out.printf("get %s from net\n",name); return null
; } } public class FontProviderFromSystem implements FontProvider { @Override public Font getFont(String name) { System.out.printf("get %s from system\n",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,豈不美哉?

總結

至此,我們知道動態代理相對於靜態代理的優勢:就靜態代理而言,在委托類特別多的應用場景,就要相應的添加許多的代理類,這顯然增加了應用程序的復雜度,而使用動態代理就可以減少代理類的數量,相對降低了應用程序的復雜度

引用

1.https://www.zhihu.com/question/20794107

動態代理相對於靜態代理的優勢