-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;
    }

我试过做这样的事情,但它有两个问题。

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();
    }

有没有解决这个问题的设计模式?

4

5 回答 5

6

我有点困惑你到底想要达到什么目的。看起来你想要一个工厂,即隐藏如何创建某个类的对象的类。在这种情况下不需要 Singleton,除非您有其他原因。

至于你的问题:

  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;
}
于 2014-09-19T05:32:43.450 回答
6

看看:单身人士有什么不好的?

如果你有理由,你应该只使用代码模式。例如:流行的模式和使用它们的原因是:

创作模式

  • 抽象工厂 创建几个类族的实例
  • Builder 将对象构造与其表示分离
  • 工厂方法创建几个派生类的实例
  • 原型 要复制或克隆的完全初始化的实例
  • 例 只能存在一个实例的类

结构模式

  • 不同类的适配器匹配接口
  • Bridge将对象的接口与其实现分开
  • 复合 简单和复合对象的树结构
  • 装饰器 动态地为对象添加职责
  • Facade代表整个子系统的单个类
  • Flyweight 用于高效共享的细粒度实例
  • 代理 代表另一个对象的对象

行为模式

  • 响应链。一种在对象链之间传递请求的方法
  • Command 将命令请求封装为对象
  • 解释器 一种在程序中包含语言元素的方法
  • 迭代器 顺序访问集合的元素
  • 中介者 定义类之间的简化通信
  • Memento 捕获并恢复对象的内部状态
  • 观察者 一种通知多个类变化的方法
  • 状态 当对象的状态改变时改变其行为
  • 策略 将算法封装在一个类中
  • 模板方法 将算法的确切步骤推迟到子类
  • 访问者 为类定义一个新的操作而不做任何改变

来源:工厂

于 2014-09-24T12:34:56.373 回答
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 {

}

另一种可能的解决方案是允许通过更高级别的抽象来使用 POI,这更优雅

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();
}
于 2014-09-25T04:25:26.570 回答
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);
}
于 2014-09-23T12:49:43.873 回答
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

从这个角度来看,安全有效的模式称为按需初始化,如下所示:

来自 wiki 的片段:“ [...] 在所有 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;
    }

}

上面的 holder 模式保证了安全和高性能,因为静态类 Holder 只加载一次(JVM 规范)并且延迟加载 - 仅在第一次调用 getInstance() 时。

于 2014-09-23T15:16:10.937 回答