此问题与Android有关,但在其他情况下也可以询问。我需要创建一个暴露单身人士的图书馆;或者我想确保只存在我的类的一个实例,并且可以在代码中的任何位置抓取而不传递引用。
但是单身人士需要一些参数。例如,在Android中,经常需要Context
个对象。我还必须准确一点,因为我提供了一个库,我希望用户能够轻松完成任务并且我无法控制Android中的Application
类(有时可以使用此类管理整个应用程序的对象实例。
一种已知的解决方案是执行以下操作:
static MySingleton sInstance;
MySingleton.getInstance(Context c) {
if (sInstance == null) {
sInstance = new MySingleton(c.getApplicationContext());
}
return sInstance;
}
但很奇怪,因为getInstance
的参数实际上只在第一次创建单身时使用。
我可以为单例提供一个setter并要求开发人员正确设置所需的参数,但可能会出现一些奇怪的情况:
// Good usage
MySingleton.getInstance().setContext(context.getApplicationContext());
MySingleton.getInstance().doSomethingThatRequiresContext(); // OK
// Bad usage
MySingleton.getInstance().doSomethingThatRequiresContext(); // Error!
MySingleton.getInstance().setContext(context.getApplicationContext());
我可以检查每个方法的开头是否正确配置了单例,并在状态不好的情况下启动了一些异常,但API使用起来不太直接:
MySingleton.getInstance().setContext(context.getApplicationContext());
try {
MySingleton.getInstance().doSomethingThatRequiresContext();
}
catch(BadSingletonConfiguration e) { }
即使我使用运行时异常,使用它也会很危险。
除非我恳请用户手动创建实例并确保自己只存在一个实例,否则我看不到一个好的解决方案。
答案 0 :(得分:2)
你可以有一个createInstance方法,它接受一个Context和一个getInstance,如果它们在create instance之前调用getInstance,它将返回null或抛出一些有意义的异常。也许抛出一个RuntimeException,声明必须先调用createInstance。
此外,如果已经调用了createInstance,它将只返回已创建的实例。这是我正在思考的代码示例:
public class MySingleton
{
private static MySingleton INSTANCE;
private final Context context;
public static MySingleton createInstance(Context context)
{
if(INSTANCE == null)
{
INSTANCE = new MySingleton(context);
}
return INSTANCE;
}
public static MySingleton getInstance()
{
if(INSTANCE == null)
{
throw new RuntimeException("You must call createInstance first");
}
return INSTANCE;
}
public void doSomethingThatRequiresContext()
{
context.doSomething();
}
private MySingleton(Context context)
{
this.context = context;
}
}
答案 1 :(得分:1)
如何做到这一点:
MySingleton.getInstance().doSomethingThatRequiresContext(context);
但通常最好使用依赖注入方法而不是使用手动创建的单例。您可能需要查看Dagger。
答案 2 :(得分:1)
另一种方法是从静态init
方法返回一个实例,并在返回的实例上包含所有方法。
MyInstance instance = MyLibrary.init(context, and, whatever, else);
instance.doSomethingThatRequiresContext();
现在无法撤消的调用顺序。
然后你可能需要防范的是两次调用init
。您可以使用运行时异常执行哪个操作,或者返回上一个实例,如果init
被调用两次,我会个人运行。
我想确保只存在一个类的实例,并且可以在代码中的任何位置抓取而不传递引用。
提供仅在致电getInstance
init
//MyLibrary.getInstance() would throw before init
MyInstance instance1 = MyLibrary.init(context, and, whatever, else);
MyInstance instance2 = MyLibrary.getInstance();
assertSame(instance1, instance2);
请注意,虽然只是略微不同于原作,但将分配和单身人士管理的职责分为MyLibrary
,至少仅 init
和{{1方法需要检查是否已调用init。 getInstance
上的所有方法都不用担心。
即使我使用运行时异常,使用它也会很危险。
如果没有它们,我认为你无法解决这个问题。如果用户没有初始化,那么在出现严重错误时不抛出会更危险。只需添加一条好的错误消息即可备份文档。
完整列表:
MyInstance