通用工厂模式 - 如何处理返回类型

时间:2015-07-24 08:45:43

标签: java generics caching

我正在尝试使用返回嵌套类型缓存的方法实现通用缓存工厂。 我面临着在工厂中配置缓存的问题,具体取决于用于获取缓存实例的泛型类型参数。

我认为我的模型不适合处理工厂中的特定配置,但我想在一个地方管理我的不同缓存。你有什么建议这样做的?

我的实施:

public class GuavaCacheFactory {

    public static final String STRING_CACHE = "STRING_CACHE";
    public static final String SERVICES_LIST_CACHE = "SERVICES_LIST_CACHE";

    private volatile Map<String, Cache<String, ? extends Object>> cacheMap = Maps.newHashMap();

    public final <I extends Object> Cache<String, I> getCache(String name) {

        Cache<String, I> cache = (Cache<String, I>) cacheMap.get(name); // Unchecked cast :(

        if (cache == null) {
            if(STRING_CACHE.equals(name)) {
                cache = CacheBuilder.newBuilder()
                        .maximumSize(300)
                        .expireAfterWrite(12, TimeUnit.HOURS)                       
                        .removalListener(new RemovalListener<String, I>() {
                            @Override
                            public void onRemoval(RemovalNotification<String, I> notification) { // I -> String here
                                System.out.println("Remove parameter '" + notification + "' from cache with cause " + notification.getCause()));
                            }
                        })
                        .build();
            } else if(SERVICES_LIST_CACHE.equals(name)) {
                // I need to do an unchecked cast if I want to manipulate "List<Service>" instead of I
                cache = (Cache<String, I>) CacheBuilder.newBuilder()
                        .maximumWeight(1000L)
                        .weigher(new Weigher<String, List<Service>>() {
                            @Override
                            public int weigh(String key, List<Service> services) { // I -> List<Service>
                                return services != null ? services.size() : 0;
                            }
                        })
                        .expireAfterWrite(1, TimeUnit.HOURS)
                        .build();
                /*cache = CacheBuilder.newBuilder()
                        .maximumWeight(1000L)
                        .weigher(new Weigher<String, I>() {
                            @Override
                            public int weigh(String key, I services) { // I -> List<Service> here
                                return services != null ? services.size() : 0; // Error -> Here I want manipulate "List"
                            }
                        })
                        .expireAfterWrite(1, TimeUnit.HOURS)
                        .build();*/
            } else {
                throw new IllegalStateException("Cache with name '"+ name + "' cannot be created by GuavaCacheFactory");
            }
            cacheMap.put(name, cache);
        }

        return cache;
    }   
    // singleton
}

我需要如何使用它:

Cache<String, String> stringCache = GuavaCacheFactory.getInstance().getCache(GuavaCacheFactory.STRING_CACHE);
String value = stringCache.get("str1", ...);
// or
Cache<String, List<Service>> listCache = GuavaCacheFactory.getInstance().getCache(GuavaCacheFactory.SERVICES_LIST_CACHE);
List<Service> services = listCache.get("serv1", ...);

根据名称,我知道我想要使用什么类型。没有更好的解决方案来解决我的问题?

2 个答案:

答案 0 :(得分:1)

您可以使用预期缓存的实际类型替换标识缓存类型的字符串常量。这适用于像String这样的无类型类。对于服务列表,您必须定义自己的类才能使其工作:

public class ServiceList extends List<Service> { }

public class GuavaCacheFactory {

    public final <I> Cache<String, I> getCache(Class<I> cacheType) {
        if cacheType.equals(String.class) {
            // ...
        } else if cacheType.equals(ServiceList.class) {
            // ...
        }
}

然后像这样使用它:

GuavaCacheFactory f = GuavaCacheFactory.getInstance()

// String-Cache
Cache<String, String> stringCache = f.getCache(String.class);
String value = stringCache.get("str1", ...);

// ServiceList-Cache
Cache<String, ServiceList> listCache = f.getCache(ServiceList.class);
ServiceList services = listCache.get("serv1", ...);

答案 1 :(得分:1)

注意有趣的代码:

private volatile Map<String, Cache<String, ? extends Object>> cacheMap = Maps.newHashMap();

volatile在这里没有帮助,它只影响对字段cacheMap的修改,而不影响地图本身。查看Collections.synchronizedMap(...)或使getCache方法同步。

集中配置或上帝对象反模式:您的应用程序最终需要多少个不同的缓存?您可能有双向依赖关系:缓存用户依赖于工厂来构建缓存,缓存工厂依赖于用户来定义所需的值类型,可能需要额外的监听器等等。最后一切都取决于彼此,这不是一个分层架构。

暴露强大的缓存接口:谁在使用缓存工厂?是否允许每个人请求服务缓存并删除服务?也许服务工厂只需要服务缓存。为什么要使服务缓存成为公共和一般产品呢?

容易出错的重新配置:您可能希望将配置置于中心位置。但是,工厂内部的某些配置选项无意更改,或者缓存用户也需要更改。这是凝聚力,但代码是分开的。

因此,更好的解决方案是实例化您需要的缓存。

您还在编写更高级缓存中已有的内容。查看具有CacheManager的EHCachecache2k或任何符合JCache的缓存实现。

对于稍微偏离主题的回答感到抱歉。