interface S {
// Method definitions up-to and including the S3 class
}
class S0 implements S {
// Code that counts samples
}
class S1 extends S0 {
// Code that calls the superclass methods and also computes the mean
}
class S2 extends S1 {
// Code that calls the superclass methods and also computes the variance
}
class S3 extends S2 {
// Code that calls the superclass methods and also computes the skewness
}
假设现在我们想要扩展这些类,例如检查度量的收敛性。对于我的目的,我不需要在运行时进行此扩展。我可以考虑以下替代方案:
创建子类
S0C
,S1C
,S2C
和S3C
,分别从S0
,S1
,S2
和S3
继承,并拥有检查收敛性的代码副本:- 优点:
- 概念上直观简单
- 生成的对象仍然属于超类
- 子类源代码仅包含额外的收敛检查代码
- 缺点:
- 大量的代码重复 - 未来会导致同步开销的增加
- 主要缺点:
- 如果我想要另一组例如预处理样本的类怎么办?我们在谈论指数级别的相同代码复制!
- 优点:
使用装饰器模式:
- 优点:
- 没有代码重复!
- 缺点:
- 对象不再属于原始类(可以很容易地解决这个问题)
- 在Java中,由于使用虚方法调用而非特殊方法调用,会有一个非常小的(存在!我测量过!)性能损失。这并不是非常重要,但仍然可以注意到。
- 主要缺点:
- 必须保持与包装对象接口同步的数量庞大的委托方法。使用接口确保没有遗漏的方法,但即使使用自动化生成委托方法的IDE也很难维护。
- 为了拥有适当实现的装饰器模式,所有装饰器和被包装的类都需要实现完全相同的接口。这实际上意味着我必须将例如收敛检查方法添加到
S
接口中,这完全破坏了任何模块化的意义。 唯一取消此要求的方法是禁止代码中的嵌套装饰器。
- 优点:
在Java中有更好的处理此问题的方法吗?也许是不同的设计模式?更技术性的解决方案?某种特殊的仪式舞蹈?
PS:如果我误解了什么,请随时(温柔地)指出...
编辑:
看来我需要稍微澄清一下我的目标:
- 我不需要运行时对象组合。我想要的是使用新方法扩展S*类的能力。如果我可以根据需要创建子类而不重复代码,我可能会这样做。如果我可以在使用位置进行操作(不太可能),那就更好了。 - 我不想一遍又一遍地编写相同的代码。注意:委托方法和构造函数是可以的,我想,实现算法的方法则不行。 - 我希望保持我的接口模块化。这是我对装饰器模式的主要问题 - 除非放置非常特定的嵌套约束,否则您最终将得到所有接口的超级接口...
回应几条评论:
The
S*
classes are structured using template methods:class S0 { int addSample(double x) { ...; } double getMean() { return Double.NaN; } } class S1 extends S0 { int addSample(double x) { super.addSample(x); ...; } double getMean() { return ...; } }
My
S*C
extended classes from the first solution would be like this:interface S { int addSample(double x); double getMean(); } class S0C extends S0 implements S { int addSample(double x) { super.addSample(x); ...; } boolean hasConverged() { return ...; } } class S1C extends S1 { int addSample(double x) { super.addSample(x); ...; } boolean hasConverged() { return ...; } }
Note the duplication of the
hasConverged()
method.A convergence checking decorator would be like this:
class CC<T extends S> implements S { T o = ...; int addSample(double x) { o.addSample(x); ...; } double getMean() { return o.getMean(); } boolean hasConverged() { return ...; } }
The problem: If I want to combine another separator behavior besides convergence checking, I need a separate decorator e.g.
NB
- and in order to have access to e.g. thehasConverged()
method, the new decorator needs to:- Implement the same interface as
CC
- Use the same interface as
CC
for its wrapped object type... - ...which forces me to use that interface for the
S*
methods if I want to be able to useNB
withS*
objects without usingCC
- Implement the same interface as
My selection of the Decorator patter was only for lack of a better alternative. It's just the cleanest solution I have found thus far.
When extending the
S*
classes, I still need the originals intact. Putting e.g. the convergence functionality in a common super-class would mean that the associated behavior (and its performance impact) would now exist in all subclasses, which is definitely not what I want.
interface Decorator extends S
并在其中声明必要的收敛函数。 - VoohasConverged()
而不仅仅是在某个S接口函数之前/之后吗?如果是这样,那么装饰器就不是正确的解决方案,因为你基本上创建了一个完全不同的数据类型。不过这并不是什么大问题,因为“Decorator extends S”解决方案仍然可以工作——它只是不再是装饰器了。 - Voo