2014-09-19 21 views
-2
java version 1.7.0_65 

我有一個單例設計模式類。這將始終返回最初創建的同一個實例。當包含的類是單例模式時使用組合創建多個對象

但是,我的問題是這個類需要從另一個類創建許多其他對象。我已經爲此使用了作文(POI課程的實例ArlabFacade)。從這個單例實例中,客戶端應該能夠創建許多POI對象。我不想公開POI類的內部工作,所有事情都必須通過單例實例。

private static ArlabFacade mArlabFacade = null; 
private POI mPoi; /* Should be able to create many object of this class */ 

private ArlabFacade(Context context) {  
     /* initialize properties */ 
     mContext = context; 

     mPoi = null; 
    } 

public static ArlabFacade getInstance(Context context) { 

     /* Create a synchronised singleton instance */ 
     ReentrantLock lock = new ReentrantLock(); 
     lock.lock(); 
     if(mArlabFacade == null) { 
      mArlabFacade = new ArlabFacade(context); 
     } 
     lock.unlock(); 

     return mArlabFacade; 
    } 

我試過做這樣的事情,但它有2個問題。

1) I don't want to return the class instance of POI 
2) because I only have a single instance the mPoi will be overwritten by the next client call to this function. 

此功能將只覆蓋:

public POI createNewPOI() { 
     return mPoi = new POI(); 
    } 

是否有任何的設計模式,解決這個問題呢?

+0

不知道如果我理解正確,但不是有一個實例的方式與POI工作變量'POI'類型的'mPoi',你可以有一個'ArrayList ',每當'createNewPoi()'被調用時,你可以創建一個新的實例,將它添加到'ArrayList'中,然後返回新的實例。這將保留舊的對象,但正如我所說我可能誤解了。 – karakusc 2014-09-19 05:21:18

+0

@karakusc,基本上。 arlabFacade是一個具有應用程序生命週期的單一實例。並且可以根據需要創建儘可能多的POI對象。但我真的不想公開POI課程。我希望我明白這一點。 – ant2009 2014-09-21 07:48:37

+0

每個POI對象有什麼區別?爲什麼你不能爲每個POI使用獨特的ArlabFacade? – dieend 2014-09-22 03:11:01

回答

3

很簡單,但首先,請注意,該鎖必須在靜態的環境中創建,使每個線程都使用相同的鎖定實例(也就沒有同步在所有如果使用每個線程的不同實例)現在

這裏是代碼

public class ArlabFacade { 

    private static ArlabFacade mArlabFacade = null; 

    /* Create a synchronised singleton instance */ 
    private static final ReentrantLock lock = new ReentrantLock(); 

    private ArlabFacade(Context context) {  
     /* initialize properties */ 
     mContext = context; 
    } 

    public static ArlabFacade getInstance(Context context) { 
     lock.lock(); 
     if(mArlabFacade == null) { 
      mArlabFacade = new ArlabFacade(context); 
     } 
     lock.unlock(); 
     return mArlabFacade; 
    } 

    public NotAPOI createNewPOI() { 
     return new NotAPOIImpl(new POI()); 
    } 

    public void doSomething(NotAPOI val) { 
     if(!(val instanceof NotAPOIImpl)) { 
      throw new IllegalArgumentException("Illegal implementation of NotAPOI"); 
     } 
     NotAPOIImpl impl = (NotAPOIImpl) val; 
     POI poi = val.poi; 
     // do something with poi here 
    } 

    private static class NotAPOIImpl implements NotAPOI { 
     private POI poi; 
     private NotAPOIImpl(POI poi) { 
      this.poi = poi; 
     } 
    } 
} 

// As you don't like to expose the POI just hide it behind the interface 
public interface NotAPOI { 

} 

另一種可能的解決方案是允許通過抽象的一個多水平,這是更優雅

public class ArlabFacade { 

    private static ArlabFacade mArlabFacade = null; 

    /* Create a synchronised singleton instance */ 
    private static final ReentrantLock lock = new ReentrantLock(); 

    private ArlabFacade(Context context) {  
     /* initialize properties */ 
     mContext = context; 
    } 

    public static ArlabFacade getInstance(Context context) { 
     lock.lock(); 
     if(mArlabFacade == null) { 
      mArlabFacade = new ArlabFacade(context); 
     } 
     lock.unlock(); 
     return mArlabFacade; 
    } 

    public NotAPOI createNewPOI() { 
     return new NotAPOIImpl(new POI()); 
    } 

    private static class NotAPOIImpl implements NotAPOI { 
     private POI poi; 
     private NotAPOIImpl(POI poi) { 
      this.poi = poi; 
     } 
     public void doSomething() { 
      poi.doSomething(); 
     } 
    } 
} 

// As you don't like to expose the POI just hide it behind the interface 
public interface NotAPOI { 
    void doSomething(); 
} 
6

我有點困惑你到底想要達到什麼目的。它看起來像就像你想要一個工廠,即隱藏如何創建某個類的對象的類。在這種情況下,不需要單例,除非你有其他原因。

至於你的問題:

  1. 您是也不返回class實例,但該類的對象。我認爲這是一個重點:創建POI對象並返回它。我猜這個命名法有些混亂,所以請解釋一下你的類實例是什麼意思,以及爲什麼你不想返回它。

  2. 在工廠方法createNewPOI()中,您只需覆蓋對最後創建的POI對象的引用,而不是對象本身。除非你的工廠類(對應你的Singleton)正在對POI對象本身做一些事情,否則不需要保留一個引用。您將對象返回給方法的調用者。之後,你可以忘記它:

public POI createNewPOI() { 
    return new POI(); 
} 

。在你的代碼多了一個問題:您在getInstance()方法不起作用鎖定。對於ReentrantLock來說,它必須在多個線程之間共享。在你的情況下,每個線程創建它自己的鎖副本,而不知道其他的副本。

最簡單的方法是隻讓方法同步:

public static synchronized ArlabFacade getInstance(Context context) { 

    if(mArlabFacade == null) { 
     mArlabFacade = new ArlabFacade(context); 
    } 

    return mArlabFacade; 
} 
+1

此外,在上下文中傳遞並不是一個好主意。如果我用contextA調用方法,那麼contextB。不同的上下文,但返回的對象將是相同的。 – nablex 2014-09-19 06:07:44

+0

@nablex真的,我完全錯過了。 – 2014-09-19 06:21:23

+0

@nablex,上下文將永遠是一樣的。感謝您通知我關於線程問題。通常,我不喜歡使用同步方法,因爲自旋鎖不如睡眠鎖有效。 – ant2009 2014-09-22 02:47:27

2

如果我理解正確的話,你希望所有來電者得到相同的單例類,但每個來電者對自己的POI對象進行操作。但是這個POI對象應該隱藏在Singleton-Class中。

你可以這樣說:

每個調用點/客戶端將首先必須調用ArlabFacade.getInstance().generateToken()所以他得到一個獨特的令牌,所以他得到一個POI保留使用。當他完成他應該叫releaseToken()

private static ArlabFacade mArlabFacade = null; 

private HashMap<String, POI> poiMap = new ConcurrentHashMap<>(); 

private ArlabFacade(Context context) {  
     /* initialize properties */ 
     mContext = context; 
    } 

public static synchronized ArlabFacade getInstance(Context context) { 
    /* Create a synchronised singleton instance */ 
    if(mArlabFacade == null) { 
     mArlabFacade = new ArlabFacade(context); 
    } 

    return mArlabFacade; 
} 

private AtomicInteger uniqueStringCounter = new AtomicInteger(0); 

public String createUniqueString() { 
    return "TOKEN"+uniqueStringCounter.getAndIncrement(); 
} 

public String generateToken() { 
    String token = createUniqueString(); 
    poiMap.add(token, new POI()); 
} 

public void releaseToken(String token) { 
    poiMap.remove(token); 
} 

public void doStuffOnPOI(String token, int someParameter) { 
    POI mPoi = poiMap.get(token); 

    mPoi.doStuff(someParam); 
} 
+1

對於您的「每個客戶端的POI」示例非常好。但請記住,Double-check模式已被棄用爲不安全:http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html – 2014-09-23 15:19:53

+0

@przemekhertel謝謝爲了您的洞察力,我不知道 - 我通常在任何地方都使用靜態單例初始化,但是在這裏需要一個上下文,所以如果上下文實際上是需要的話,這將不起作用......最好是在業務應用程序中擁有單身作爲一個工廠和外部注入... – Falco 2014-09-23 15:30:42

+0

@przemekhertel @Falco雙重鎖定成語[實際工程](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)時使用'volatile'關鍵字。 UPD:當然,我的意思是「在現代JVM中工作」。 – Aivean 2014-09-23 16:19:25

1

當使用Java語言編寫懶加載(點播)的獨生子,你必須知道的一些問題:

雙重檢查鎖定模式在多線程環境中被認爲是不安全

/* 
* unsafe and broken Double-Checked Locking pattern 
*/ 
public class ArlabFacade { 
    private ArlabFacade mArlabFacade; 

    public ArlabFacade getInstance() { 
     if (mArlabFacade == null) { 
      synchronized(this) { 
       if (mArlabFacade == null) { 
        mArlabFacade = new ArlabFacade(...); 
       } 
      } 
     } 
     return mArlabFacade; 
    } 
} 

雙重檢查鎖定模式是不好的,由於Java內存模型在語言規範描述。 見下面的鏈接:

http://en.wikipedia.org/wiki/Double-checked_locking

http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html

從這個角度來看安全和有效的模式被稱爲初始化點播持有人,並顯示如下:從維基

片段:「 [...]在所有版本的Java中,該慣用語能夠實現安全,高度併發的惰性初始化,並具有良好的性能[...]

參見:

http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom

/* 
* fully safe and performant holder pattern 
*/ 
public class ArlabFacade { 

    private static class Holder { 

     private ArlabFacade instance; 
     private List<POI> mPOIs; 

     static { 
      instance = new ArlabFacade(); 

      mPOIs = new ArrayList(); 
      mPOIs.add(new POI(...));    
      mPOIs.add(new POI(...)); 
      .... 
     } 
    } 


    public static ArlabFacade getInstance() { 
     return Holder.instance; 
    } 

} 

上述保持架模式保證是安全的,高性能 becase的靜態類持有人只加載一次(JVM規範)和懶洋洋地 - 只有當的getInstance()被調用首次。

+0

當Singleton需要上下文進行初始化時,我不認爲這個更好和更好的模式可以工作。 – Falco 2014-09-23 15:46:52

6

看看:What is so bad about singletons?

,如果你有一個原因,你應該只使用代碼模式。例如:opular模式和理由使用它們分別是:

創建模式

  • 抽象工廠創建的類
  • 生成器中隔離對象的構造幾個家庭的實例,其表示
  • 工廠方法創建多個派生類的實例
  • 原型甲完全初始化實例被複制或克隆
  • 的Singleton其中只有一個單個實例可以存在

結構模式

  • 適配器的匹配接口的類不同的分類
  • 分隔的對象的接口從它的實現
  • 複合的簡單和複合樹結構對象
  • 裝飾添加責任對象動態
  • 門面一個表示整個子系統的單個類
  • Flyweight用於高效共享的細粒度實例
  • 代理表示另一個目的

行爲模式

  • RESP的鏈的對象。傳遞一個請求對象
  • 鏈命令之間
  • 封裝的命令請求作爲對象
  • 解釋一種方法,包括在一個程序
  • 迭代順次語言元素的方式訪問的元素一個集合的
  • 中保定義類之間簡化的通信
  • 紀念品捕捉和還原對象的內部狀態
  • 觀察通知改變了許多類
  • 國家阿爾特其狀態改變時
  • 戰略封裝內部的算法對象的行爲的一種方式a類
  • 模板方法將算法的具體步驟推遲到子類
  • 遊客定義一個新的操作的一類,而不改變

來源:dofactory

相關問題