// Notice the private FooFactory constructor
private FooFactory() {
}
public static FooFactory create() {
return new FooFactory();
}
public FooFactory withColor( final Color col ) {
this.color = color;
return this;
}
public Foo build() {
return new FooImpl( color, and, all, the, other, parameters, go, here );
}
public interface Foo {
X getX();
Y getY();
}
public interface MutableFoo extends Foo {
void setX(X x);
void setY(Y y);
}
public class FooImpl implements MutableFoo {...}
public SomeClassThatUsesFoo {
public Foo makeFoo(...) {
MutableFoo ret = new MutableFoo...
ret.setX(...);
ret.setY(...);
return ret; // As Foo, not MutableFoo
}
}
class Foo
{
// private fields only to be written inside a constructor
private readonly int i;
private readonly string s;
private readonly Bar b;
// public getter properties
public int I { get { return i; } }
// etc.
}
**选项1.**带可选参数的构造函数
public Foo(int i = 0, string s = "bla", Bar b = null)
{
this.i = i;
this.s = s;
this.b = b;
}
例如new Foo(5, b: new Bar(whatever))。不适用于4.0之前的Java或C#版本。但仍然值得展示,因为它是一个例子,说明并非所有解决方案都是语言不可知的。
**选项2.**接受单参数对象的构造函数
public Foo(FooParameters parameters)
{
this.i = parameters.I;
// etc.
}
class FooParameters
{
// public properties with automatically generated private backing fields
public int I { get; set; }
public string S { get; set; }
public Bar B { get; set; }
// All properties are public, so we don't need a full constructor.
// For convenience, you could include some commonly used initialization
// patterns as additional constructors.
public FooParameters() { }
}
用法示例:
FooParameters fp = new FooParameters();
fp.I = 5;
fp.S = "bla";
fp.B = new Bar();
Foo f = new Foo(fp);`
C#从3.0开始使用对象初始化器语法(语义上等同于前面的例子)使这一点变得更加优雅:
FooParameters fp = new FooParameters { I = 5, S = "bla", B = new Bar() };
Foo f = new Foo(fp);
9条答案
按热度按时间b09cbbtk1#
好吧,你想要一个更容易阅读和不可变的对象一旦创建?
我认为流畅的界面正确完成会对您有所帮助。
它看起来像这样(纯粹是编造的例子):
我用粗体写 “CORRECTLY DONE” 是因为大多数Java程序员错误地使用了流畅的接口,并用构建对象所必需的方法污染了他们的对象,这当然是完全错误的。
诀窍在于 * 只有build()方法真正创建了一个Foo*(因此您的Foo可以是不可变的)。
创建(),其中XXX(..) 和 * with XXX(..)* 都创建“其他东西”。
其他东西可能是一个食品工厂,这里有一个方法来做到这一点...
你的FooFactory看起来像这样:
gfttwv5a2#
在Scala 2.8中,可以在case类中使用命名参数、默认参数以及
copy
方法,下面是一些示例代码:k4ymrczo3#
在Scala 2.8上考虑一下这个问题:
当然,这也有它的问题。例如,尝试生成
espouse
和Option[Person]
,然后让两个人结婚。我想不出一种方法来解决这个问题,除非诉诸private var
和/或private
构造函数加上工厂。cxfofazt4#
下面是几个选项:
选项1
使实现本身成为可变的,但是将它公开的接口分离为可变的和不可变的。这取自Swing库设计。
选项2
如果您的应用程序包含大量预定义的不可变对象(例如,配置对象),则可以考虑使用Spring框架。
ecr0jaav5#
记住有different kinds of immutability是很有帮助的。对于您的情况,我认为“冰棒”不变性会非常好:
冰棒不变性:是我异想天开地称之为一次写入不变性的轻微削弱。可以想象一个对象或字段在初始化期间保持了一段时间的可变性,然后被永远“冻结”。这种不变性对于循环引用彼此的不可变对象特别有用,或者已串行化到磁盘且在反串行化时的不可变对象需要是“流动的”,直到完成整个反串行化过程为止,此时所有对象可被冻结。
所以你初始化你的对象,然后设置一个“冻结”标志,表示它不再是可写的。最好是,你把变异隐藏在一个函数后面,这样这个函数对于使用你的API的客户端来说仍然是纯粹的。
mklgxw1f6#
你也可以让不可变对象公开看起来像mutator的方法(比如addSibling),但是让它们返回一个新的示例,这就是不可变Scala集合的作用。
缺点是你可能会创建过多的示例,它也只适用于存在中间有效配置的情况(比如一些节点没有兄弟节点,这在大多数情况下是可以的),除非你不想处理部分构建的对象。
例如,还没有目标的图边不是有效的图边。
9o685dep7#
考虑四种可能性:
对我来说,2、3和4中的每一个都适应了不同的情况。由于OP引用的原因,第一个很难让人喜欢,通常是设计遭受了一些蠕变并需要一些重构的症状。
我列出的是(2)当“工厂”背后没有国家时是好的,而(3)是有状态时选择的设计。我发现自己使用(2)而非(3)当我不想担心线程和同步,并且我不需要担心在许多对象的生产过程中分摊一些昂贵的设置时。另一方面,当真实的工作进入工厂的构建(从SPI设置、阅读配置文件等)时,调用(3)。
最后,其他人的回答提到了选项(4),其中您有许多小的不可变对象,并且更可取的模式是从旧对象中获取新对象。
请注意,我不是“模式粉丝俱乐部”的成员--当然,有些东西值得效仿,但在我看来,一旦人们给他们起了名字,戴了滑稽的帽子,他们就开始了自己无益的生活。
nfzehxib8#
另一个可能的选择是重构以拥有更少的可配置字段。如果一组字段(大部分)只能相互作用,则将它们聚集到自己的小的不可变对象中。这个“小”对象的构造器/构建器应该更易于管理,这个“大”对象的构造器/构建器也是如此。
sirbozc59#
我使用C#,这些是我的方法。
**选项1.**带可选参数的构造函数
例如
new Foo(5, b: new Bar(whatever))
。不适用于4.0之前的Java或C#版本。但仍然值得展示,因为它是一个例子,说明并非所有解决方案都是语言不可知的。**选项2.**接受单参数对象的构造函数
用法示例:
C#从3.0开始使用对象初始化器语法(语义上等同于前面的例子)使这一点变得更加优雅:
备选案文3:
重新设计类,使其不需要如此大量的参数。你可以将类分解为多个类。或者不将参数传递给构造函数,而是根据需要只传递给特定的方法。虽然不总是可行的,但如果可行,就值得这样做。