我应该将 Singleton 作为函数参数传递吗
Should i pass a Singleton as a function parameter
我最近在对同事进行的代码审查中遇到了一种我以前从未见过的奇特模式。他将单例作为函数的参数发送,并将其作为成员保存在调用 class 中。
假设我有一个简单的单身人士 class Foo
public class Foo {
private static Foo instance;
private int counter;
private Foo(){}
public static Foo getInstance(){
if (instance == null){
instance = new Foo();
}
return instance;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}
现在 Foo
有一个客户叫 Bar
:
public class Bar {
private Foo foo;
public Bar(Foo foo){
this.foo = foo;
this.foo.setCounter(10);
this.foo.setCounter(20);
}
public int getCounter(){
return foo.getCounter();
}
}
所以 Bar
做了 2 件不寻常的事情 - 1. 在构造函数中调用 Foo
,而不是仅仅使用 Foo.getInstance()
和 2. 将 Foo
保存为成员'save' 调用 Foo.getInstance()
的样板使用它。当然,我演示的用法是微不足道的。
这对我来说看起来很奇怪和尴尬,更糟糕的是,我根本无法在 Bar
范围内将 Foo
识别为单例。如果我改变一些关键的状态怎么办?但除了代码可读性的原因之外,我不能说这样做不是可选的,而且这种用法似乎最终有效。我是对还是错?为什么?传这个单例保存为成员对不对?
这看起来真的很主观。如果您认为代码有可能在某个时候不再是单例,或者如果您认为您可能会转向像 Spring 这样具有依赖注入的框架,那么保留对单例的引用会很方便。所以你可以说这是对未来灵活性的期待。通常,单身人士不赞成反对真正面向对象的代码。
但与此同时,如果您的应用程序严重依赖单例并且这是一种非常典型的模式......并且如果您担心以某种方式最终可能会出现重复的单例实例 class 因为这种用法,所以可以说这段代码不符合您应用程序的通用模式和使用期望,它应该遵循预期的准则。
在不了解您的应用程序、团队等的情况下,很难给出比这更具体的答案。
不是常规的,但我认为根据用法:
1) 将单例作为参数传递给另一个 class 的构造函数可能是有效的。
2) 存储来自客户端的单例实例也可能是有效的。
例如,国际奥委会可以这样做。
现在,如果您想向 getInstance()
添加一些逻辑,这可能是不可取的:缓存实例、根据时间线返回不同的实例、客户端等等...但这确实是一个很好的方法实现具有如此多逻辑的单例?不确定...
This looked odd and awkward to me, and worse, i can't identify Foo as
a singleton at all in Bar scope.
但是单例模式也很别扭。如果单例模式将单例实例的引用公开给客户,那么这些客户应该可以自由使用它。
为什么您认为在查看条形码时应该知道 Foo 是单例?
Bar 依赖于 Foo,因为它需要它。实现(单例或非单例)在其级别上无关紧要。
一般来说,应该避免 "hardcoded" 单例模式:一种全局变量,更难切换到其他实现,更难模拟...
如果您可以使用实际代码不难实现的 Java 5 枚举,那就更好了。
实际单例的补充说明:
它不是线程安全的。使用 Bill Pugh 惯用语可以轻松做到这一点。
public class Foo {
private static class Holder{
private static Foo instance = new Foo();
}
private Foo(){}
public static Foo getInstance(){
return Holder.instance();
}
}
这是在您的原始代码中惰性实例化的。
但一般来说,保留一个不昂贵且必然需要的实例化是非常小心的。
急切的方式往往是首选:
public class Foo {
private static Foo instance = new Foo();
private Foo(){}
public static Foo getInstance(){
return instance;
}
}
我最近在对同事进行的代码审查中遇到了一种我以前从未见过的奇特模式。他将单例作为函数的参数发送,并将其作为成员保存在调用 class 中。
假设我有一个简单的单身人士 class Foo
public class Foo {
private static Foo instance;
private int counter;
private Foo(){}
public static Foo getInstance(){
if (instance == null){
instance = new Foo();
}
return instance;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}
现在 Foo
有一个客户叫 Bar
:
public class Bar {
private Foo foo;
public Bar(Foo foo){
this.foo = foo;
this.foo.setCounter(10);
this.foo.setCounter(20);
}
public int getCounter(){
return foo.getCounter();
}
}
所以 Bar
做了 2 件不寻常的事情 - 1. 在构造函数中调用 Foo
,而不是仅仅使用 Foo.getInstance()
和 2. 将 Foo
保存为成员'save' 调用 Foo.getInstance()
的样板使用它。当然,我演示的用法是微不足道的。
这对我来说看起来很奇怪和尴尬,更糟糕的是,我根本无法在 Bar
范围内将 Foo
识别为单例。如果我改变一些关键的状态怎么办?但除了代码可读性的原因之外,我不能说这样做不是可选的,而且这种用法似乎最终有效。我是对还是错?为什么?传这个单例保存为成员对不对?
这看起来真的很主观。如果您认为代码有可能在某个时候不再是单例,或者如果您认为您可能会转向像 Spring 这样具有依赖注入的框架,那么保留对单例的引用会很方便。所以你可以说这是对未来灵活性的期待。通常,单身人士不赞成反对真正面向对象的代码。
但与此同时,如果您的应用程序严重依赖单例并且这是一种非常典型的模式......并且如果您担心以某种方式最终可能会出现重复的单例实例 class 因为这种用法,所以可以说这段代码不符合您应用程序的通用模式和使用期望,它应该遵循预期的准则。
在不了解您的应用程序、团队等的情况下,很难给出比这更具体的答案。
不是常规的,但我认为根据用法:
1) 将单例作为参数传递给另一个 class 的构造函数可能是有效的。
2) 存储来自客户端的单例实例也可能是有效的。
例如,国际奥委会可以这样做。
现在,如果您想向 getInstance()
添加一些逻辑,这可能是不可取的:缓存实例、根据时间线返回不同的实例、客户端等等...但这确实是一个很好的方法实现具有如此多逻辑的单例?不确定...
This looked odd and awkward to me, and worse, i can't identify Foo as a singleton at all in Bar scope.
但是单例模式也很别扭。如果单例模式将单例实例的引用公开给客户,那么这些客户应该可以自由使用它。
为什么您认为在查看条形码时应该知道 Foo 是单例?
Bar 依赖于 Foo,因为它需要它。实现(单例或非单例)在其级别上无关紧要。
一般来说,应该避免 "hardcoded" 单例模式:一种全局变量,更难切换到其他实现,更难模拟...
如果您可以使用实际代码不难实现的 Java 5 枚举,那就更好了。
实际单例的补充说明:
它不是线程安全的。使用 Bill Pugh 惯用语可以轻松做到这一点。
public class Foo {
private static class Holder{
private static Foo instance = new Foo();
}
private Foo(){}
public static Foo getInstance(){
return Holder.instance();
}
}
这是在您的原始代码中惰性实例化的。
但一般来说,保留一个不昂贵且必然需要的实例化是非常小心的。
急切的方式往往是首选:
public class Foo {
private static Foo instance = new Foo();
private Foo(){}
public static Foo getInstance(){
return instance;
}
}