2014-09-19 52 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

相关问题