使用Singleton模式是否适合Java中的大型通用类?

时间:2017-12-28 20:22:40

标签: java generics singleton

我是Generics的新手,也是Java中的Singleton模式。我有以下问题:

  • Generic类是否与Singleton Pattern兼容,即具有静态实例?

  • 如果是这样,我有以下要转换为Singleton的Generic类。什么是最佳做法?

  • (初学者问题)有没有一种很好的方法可以在运行时取消生成类的实例?将类作为参数传递给getInstance()?

class GenFooSingleton <T>
{
    private T t;

    // Prevent direct instantiation
    private GenFooSingleton(T o){
        this.t = o;
    }

    private static GenFooSingleton INSTANCE = null;

    // Returns the single instance of this class, creating it if necessary
    static synchronized <T> GenFooSingleton<T> getInstance(T tClass)
    {
        if (INSTANCE == null)
        {
            INSTANCE = new GenFooSingleton<>(tClass);
        }
        return INSTANCE;
    }

}

修改

我使用Generic with Singleton的用例:

1。为何选择Generic? 首先,让我说我有一个以下类型的数据开头的以下Singleton存储库,以下示例来自我在googlesamples/android-architecture

中学到的内容
class FooRepository implements FooDatasource
{

    private final FooDatasource local;
    private final FooDatasource remote;
    Map<String, Foo> mCahcedItems;

    // Prevent direct instantiation
    private FooRepository(FooDatasource remote, FooDatasource local){
        this.remote = remote;
        this.local = local;
    }

    private static FooRepository INSTANCE = null;

    // Returns the single instance of this class, creating it if necessary
    public static synchronized FooRepository getInstance(FooDatasource remote, FooDatasource local)
    {
        if (INSTANCE == null)
        {
            new FooRepository(remote,local);
        }
        return INSTANCE;
    }

    // implement CRUD methods
    @Override
    public Flowable<List<Foo>> getFoos(){
        // Update the mCahcedItems with the list of Foos
        // return a list of Foos and syncing between the local and remote datasources...For brevity the bunch of Rxjava implementation is omitted.  
    }

    @Override
    public Flowable<Optional<Foo>> getFoo(){
        // Update the mCahcedItems with Foo
        //...
    }
}

但我可以看到我必须为每种数据类型创建存储库。 (Foo,Baa,Daa等),其中CURD逻辑基本上与每个实例相同。所以我很自然地想要将存储库变成通用存储库。

2。为什么选择Singleton? 在不使用Singleton模式的情况下,每个新实例都会启动一个全新的重新加载内存缓存,这意味着新的本地数据库查询。在开发移动和内存受限设备(Android应用程序)时,每次设备更改配置/轮换时,这都会产生不必要的和I / O调用。仅仅想到这只是标志着我将不得不处理的巨大性能问题。因此,我认为只是懒惰实例化的全局可访问单个实例是一个加号。

我的尝试 因此,我开始创建Repository和Datasource接口的通用版本,并在每个数据类型实现Datasource接口时提供具体实现,如下所示:

class FooDatasource implements GenericDatasource<Foo>
{
    //...
}

class BarDatasource implements GenericDatasource<Bar>
{
    //...and so on and so forth
}

更新

我目前的方法是单例模式,对于Java和特别是Android开发人员,使用Dagger 2的依赖注入可以更好地管理Generic实例。

2 个答案:

答案 0 :(得分:2)

  

Generic类是否与Singleton Pattern兼容,即具有静态实例?

不,它不会那样工作,静态字段只存在一次,静态继承在Java中不存在。但是有很多不同的方法来实现单例。

  

如果是这样,我有以下要转换为Singleton的Generic类。什么是最佳做法?

不要这样做。 Singleton是一个反模式,主要是因为它对于测试目的而言非常糟糕。相反,使用容器(Spring,Guice,EJB等)为您管理单例,确保只存在一个实例。首先阅读Dependency Inversion PrincipleInversion of Control

  

(初学者问题)有没有一种很好的方法可以在运行时取消生成类的实例?将类作为参数传递给getInstance()?

是的,将类传递给getInstance实际上会使它更好一点,特别是如果你在内部使用类到实例映射(Guava has such a type

答案 1 :(得分:1)

  1. 由于单例模式在任何时候都试图只保证给定类的一个生存实例,因此根据其当前的泛型类型,它可以接受或产生不同结果的通用类的概念不太兼容。对于泛型是有用的,你需要能够创建相同类型的不同风格(通常的例子:一个字符串列表和一个整数列表)
  2. N / A
  3. 如果要将参数传递给单例的getInstance,那么您实际上并不想要单例而是工厂。单例只能是非参数化的,否则第一个调用会冻结上下文。
  4. 不要滥用单身人士。它们是您可能尝试实施的第一种模式,因为它是每本书中的第一种模式,但它几乎总是至少无用,至多是性能瓶颈和糟糕的设计决策(不是非常OO)

    修改

    你假设每个新实例无法共享同一个缓存基本上是错误的,原因有两个:

    1. 不使用Singleton不会强制您使用相同类型的多个实例。它只是允许你这样做,以及启用继承(单身人士只能这样做)。如果您使用Spring和单例范围的bean(默认值),那么您的存储库只在内存中存在一次 - 即使它没有实现书籍中描述的单例模式 - 并且在所有消费者之间共享(因此只有一个缓存) )。这可以在没有弹簧的情况下完成,只需使用某种工厂或注册表。
    2. 在你的课堂中使用hashmap进行缓存也有点可疑。缓存是一个实现细节,你不应该尝试以这种方式实现它(你最终会很容易地吃掉整个内存,至少使用WeakHashMap - 或使用CacheBuilder的Guava版本)。您也可以将缓存声明为静态,因此它只在内存中存在一次。现代应用程序将缓存视为方面,例如事务。它不应泄露给您的代码。例如,查看ehcache,redis,terracotta等,它们都实现了JSR-107,并直接在您的方法原型上配置,带有一些注释(@Cacheable等)。 Ho和缓存通常进入服务层 - 您不缓存数据库的状态,缓存在处理业务逻辑后发送给用户的响应(即使此规则不是绝对严格)
    3. 单例也有一个很大的问题:它直接负责实例化对象,即直接使用new关键字。这很痛苦,因为您无法在运行时更改实现的concret类型(用于测试目的或任何其他用途)。查看工厂/工厂方法模式,以查看在运行时更改类型的更好方法。

      你可以做的是拥有一个抽象的基类,通用,你的混凝土dao将扩展(但那些不是通用的)。像这样:

      abstract class AbstractDao<ID, T> {
        private Class type;
        protected AbstractDao(Class type) {
            this.type = type;
        }
        void save(T entity) {
          // save an entity
        }
      
        T get(ID pkey) { /* get an entity */}
        ...
      }
      
      public class DaoX extends AbstractDao<Long, X> {
        DaoX() {
          super(X.class)
        }
        /* Empty! or only methods applicable for X */
      }
      public class DaoY extends AbstractDao<Integer, Y> {
        DaoY() {
          super(Y.class)
        }
        /* Empty! or only methods applicable for Y */
      }
      

      在这种情况下,您不会复制任何代码。