繁体   English   中英

使用单例模式对 Java 中的大型泛型类有好处吗?

[英]Is using a Singleton pattern good for large Generic classes in Java?

我是 Java 中泛型和单例模式的新手。 我有以下问题:

  • 泛型类是否与单例模式兼容,即具有静态实例?

  • 如果是这样,我有以下通用类要转换为单例。 最佳做法是什么?

  • (初学者问题)有没有一种在运行时对类的实例进行去泛化的好方法? 在 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;
    }

}

编辑

我的单例泛型用例:

1. 为什么是泛型? 首先,假设我有以下单例存储库用于一种类型的数据,以下示例来自我在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. 为什么是单例? 如果不使用单例模式,每个新实例都会启动一个全新的内存缓存重新加载,这意味着新的本地数据库查询。 在为移动设备和内存受限设备(Android 应用程序)进行开发时,每次设备更改配置/旋转时都会产生不必要的 I/O 调用。 仅仅想到这一点就表明我必须处理一个巨大的性能问题。 因此,我认为仅延迟实例化的全局可访问单个实例是一个加分项。

我的尝试所以我着手创建 Repository 和 Datasource 接口的通用版本,并让每种数据类型在实现 Datasource 接口时提供具体的实现,如下所示:

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

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

更新

我目前的方法是使用 Dagger 2 通过依赖注入更好地管理通用实例的单例模式,适用于 Java 和特别是 Android 开发。

泛型类是否与单例模式兼容,即具有静态实例?

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

如果是这样,我有以下通用类要转换为单例。 最佳做法是什么?

不要这样做。 Singleton 是一种反模式,主要是因为它对于测试目的来说很糟糕。 相反,使用容器(Spring、Guice、EJB 等)为您管理单例,确保只有一个实例存在。 首先阅读依赖倒置原则控制倒置

(初学者问题)有没有一种在运行时对类的实例进行去泛化的好方法? 在 getInstance() 中将类作为参数传递?

是的,将类传递给 getInstance 实际上会使这更好一点,特别是如果您在内部使用类到实例映射( 番石榴有这样的类型

  1. 由于单例模式在任何时候都试图保证给定类的一个活实例,因此它与通用类的想法不太兼容,后者可以根据当前的泛型类型接受或产生不同的结果。 为了使泛型有用,您需要能够创建相同类型的不同风格(通常的例子:字符串列表和整数列表)
  2. 不适用
  3. 如果您将参数传递给单例的 getInstance,那么您并不是真正想要单例而是工厂。 单例只能是非参数化的,否则第一次调用会冻结上下文。

不要滥用单身人士。 它们是您可能尝试实现的第一个模式,因为它是每本书中的第一个,但它几乎总是至少没用,最多是性能瓶颈和糟糕的设计决策(不是很面向对象)

编辑:

您假设每个新实例不能共享相同的缓存基本上是错误的,原因有两个:

  1. 不使用 Singleton 不会强制您使用多个相同类型的实例。 它只是允许您这样做,以及启用继承(单例根本不能)。 如果你使用 Spring 和一个单例范围的 bean(默认),那么你的存储库在内存中只存在一次——即使它没有实现书中描述的单例模式——并且在所有消费者之间共享(因此只有一个缓存)。 这也可以在没有 spring 的情况下完成,只需使用某种工厂或注册表。
  2. 在你的班级中使用 hashmap 缓存也有点可疑。 缓存是一个实现细节,你不应该尝试以这种方式实现它(你最终会很容易吃掉整个内存,至少,使用 Wea​​kHashMap 代替 - 或者使用 CacheBuilder 的 Guava 版本)。 您还可以将缓存声明为静态缓存,这样它在内存中只会存在一次。 现代应用程序将缓存视为一个方面,例如事务。 它不应该泄漏到您的代码中。 比如看ehcache、redis、terracotta等,它们都实现了JSR-107,直接在你的方法原型上配置,加上一些注解(@Cacheable等)。 Ho 和缓存通常进入服务层——你不缓存数据库的状态,你缓存在处理业务逻辑后发送给用户的响应(即使这个规则不是绝对严格的)

单例还有一个非常大的问题:它直接负责实例化对象,也就是直接使用new关键字。 这很痛苦,因为您无法在运行时更改实现的具体类型(出于测试目的或任何其他用途)。 查看工厂/工厂方法模式以了解在运行时更改类型的更好方法。

你可以做的是有一个抽象基类,泛型,你的具体 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 */
}

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

暂无
暂无

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

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