簡體   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