简体   繁体   English

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

[英]Generic factory pattern - how to handle return type

I am trying to implement a generic cache factory with a method that return a cache of the nested type. 我正在尝试使用一种返回嵌套类型缓存的方法来实现通用缓存工厂。 I am facing problem about configuring cache in the factory depending on generic type parameter used to get the cache instance. 我面临有关在工厂中根据用于获取缓存实例的通用类型参数配置缓存的问题。

I think my model is not good to handle specific configuration in the factory but I would like to manage my different cache in a single place. 我认为我的模型不适用于工厂中的特定配置,但我想在一个地方管理我的不同缓存。 What do you advise to do this ? 您对此有何建议?

My implementation : 我的实现:

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
}

How I need to use it : 我需要如何使用它:

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", ...);

I know what type I want to use depending on name. 我知道我要根据姓名使用哪种类型。 There is no better solution to resolve my problem ? 有没有更好的解决方案来解决我的问题?

You can replace the string constant that identifies your cache type by the actual type of the expected cache. 您可以将标识缓存类型的字符串常量替换为预期缓存的实际类型。 This works fine for untyped classes like String . 对于像String这样的非类型化类,这很好用。 For the service list, you would have to define your own class to make it work: 对于服务列表,您必须定义自己的类以使其起作用:

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) {
            // ...
        }
}

Then use it like this: 然后像这样使用它:

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", ...);

Mind the racy code: 注意安全代码:

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

The volatile does not help here, it only affects modifications to the field cacheMap, not the map itself. volatile在这里无济于事,它只影响对字段cacheMap的修改,而不影响映射本身。 Take a look at Collections.synchronizedMap(...) or make the getCache method sychronized. 看一下Collections.synchronizedMap(...)或使getCache方法同步。

centralized configuration or god object anti-pattern : How many different caches will your application need finally? 集中配置或上帝对象反模式 :您的应用程序最终将需要多少个不同的缓存? You have probably bidirectional dependencies: Cache users depend on the factory to construct the cache, the cache factory depends on the users to define needed value types, maybe additional needed listeners, etc. At the end everything depends on each other, that is not a layered architecture. 您可能具有双向依赖性:缓存用户依赖于工厂来构造缓存,缓存工厂取决于用户来定义所需的值类型,也许还需要其他所需的侦听器,等等。最后,所有内容都相互依赖,而不是相互依赖。分层体系结构。

exposal of the mighty cache interface : Who is using the cache factory? 强大的缓存接口的公开内容 :谁在使用缓存工厂? Is everybody allowed to request the service cache and delete a service? 是否允许所有人请求服务缓存并删除服务? Maybe the service cache is only needed by the service factory. 也许服务工厂只需要服务缓存。 Why make the service cache a public and general offering then? 那么为什么要使该服务缓存为公开和通用产品呢?

error prone reconfiguration : Probably you like to have the configuration in a central place. 容易出错的重新配置 :可能您希望将配置放在一个中央位置。 However, some configuration options inside your factory are not meant to be changed, or, the cache users need to change also. 但是,工厂内部的某些配置选项并不能更改,或者缓存用户也需要更改。 Here is cohesion, but the code is separated. 这是内聚力,但是代码是分开的。

So the better solution is to instantiate the caches where you need them. 因此,更好的解决方案是在需要的地方实例化缓存。

You are also coding something what is there already in more advanced caches. 您还正在编写更高级的缓存中已有的内容。 Take a look at EHCache or cache2k which have a CacheManager or at any JCache compliant cache implementation. 看一下具有CacheManager的EHCachecache2k或任何符合JCache的缓存实现。

Sorry for the slightly off-topic answer. 抱歉,您的回答有点偏离主题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM