Java中装饰器模式的替代方案?
假设您有以下与统计信息相关的类的层次结构,其结构类似于: 假设现在我们想要扩展这些类中的每一类,例如检查度量的收敛性。出于我的目的,我不需要在运行时进行此扩展。我可以想到以下几种选择:Java中装饰器模式的替代方案?,java,oop,Java,Oop,假设您有以下与统计信息相关的类的层次结构,其结构类似于: 假设现在我们想要扩展这些类中的每一类,例如检查度量的收敛性。出于我的目的,我不需要在运行时进行此扩展。我可以想到以下几种选择: 分别从S0、S1C、S2C和S3C创建子类S0C、S1、S2和S3,每个子类都带有检查收敛性的代码副本: 优点: 概念上直截了当 结果对象仍然属于超类 子类源代码仅包含附加的收敛检查代码 缺点: 大量的代码重复-在将来会导致更改同步开销 主要缺点: 如果我需要另一组类,例如预处理样本,该怎么办?我们
- 分别从
、S0
、S1C
和S2C
创建子类S3C
、S0C
、S1
和S2
,每个子类都带有检查收敛性的代码副本:S3
- 优点:
- 概念上直截了当
- 结果对象仍然属于超类
- 子类源代码仅包含附加的收敛检查代码
- 缺点:
- 大量的代码重复-在将来会导致更改同步开销
- 主要缺点:
- 如果我需要另一组类,例如预处理样本,该怎么办?我们谈论的是同一代码的指数复制李>
- 优点:
- 使用:
- 优点:
- 没有代码重复李>
- 缺点:
- 对象不再属于原始类(易于处理)
- 由于使用了虚拟方法调用,而不是特殊的方法调用,Java中的性能受到了非常轻微的影响(它是存在的!我测量过了!)。这不是很重要,但仍然很明显
- 主要缺点:
- 关于大量必须与包装对象接口保持同步的委托方法。使用接口确实可以确保不会遗漏任何方法,但即使使用自动生成委托方法的IDE,也很难维护
- 为了有一个正确实现的decorator模式,所有decorator和包装类都需要实现完全相同的接口。这本质上意味着我必须在
接口中添加收敛性检查方法,这完全破坏了模块化的感觉。 解除此要求的唯一方法是禁止代码中的嵌套装饰符S
- 优点:
- 我不需要运行时对象组合。我想要的是用新方法扩展
类的功能。如果我可以根据需要创建子类而不需要代码复制,我可能会这样做。如果我能在使用的地方做(不太可能),那就更好了S*
- 我不想一遍又一遍地写同样的代码。注意:委托方法和构造函数是好的,我想实现算法的方法不是
- 我想保持我的接口模块化。这是我对Decorator模式的主要问题-除非放置了非常具体的嵌套约束,否则最终会得到一个包含所有接口的超级接口
类是使用模板方法构造的:S*
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 ...; } }
- 我的
第一个解决方案中的扩展类如下所示:S*C
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 ...; } }
请注意class CC<T extends S> implements S { T o = ...; int addSample(double x) { o.addSample(x); ...; } double getMean() { return o.getMean(); } boolean hasConverged() { return ...; } }
方法的重复hasConvergend()
- 收敛检查装饰器如下所示:
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 ...; } }
类CC实现了{ To=。。。; int addSample(双x){ o、 添加样品(x); ...; } 双getMean(){ 返回o.getMean(); } 布尔收敛(){ 返回。。。; } } 问题:如果我想结合收敛检查之外的另一个分隔符行为,我需要一个单独的装饰器,例如class CC<T extends S> implements S { T o = ...; int addSample(double x) { o.addSample(x); ...; } double getMean() { return o.getMean(); } boolean hasConverged() { return ...; } }
——为了访问例如NB
方法,新的装饰器需要:hasConvergend()
- 实现与
CC
- 对其包装对象类型使用与
相同的接口CC
- …这迫使我将该接口用于
方法,如果我希望能够在不使用S*
CC的情况下将
与NB
对象一起使用S*
- 实现与
- 我选择的装饰风格只是因为缺少更好的选择。这是迄今为止我找到的最干净的解决方案
- 在扩展
类时,我仍然需要原样。例如,将聚合功能放在一个通用的超类中意味着相关的行为(及其性能影响)现在将存在于所有子类中,这绝对不是我想要的S*
- 我很困惑。不清楚为什么需要第一个继承树。类似下面的代码可以完成这项工作:
public class Statistics
{
void add(final double x)
{
sum += x;
sum2 += x * x;
sum3 += x * x * x;
n++;
}
double mean()
{
return n != 0 ? sum / n : 0;
}
double variance()
{
return n != 0 ? ( sum2 - sum * sum / n) / (n - 1) : 0;
}
// add method for skewness
double sum, sum2, sum3;
int n;
}
基于您最近的编辑 正如您可能已经意识到的,Decorator不适合这种情况。这是因为它解决的是单个功能的扩充,而不是整个类树的扩充 实现这一目标的一种可能方式是采用战略。策略以算法为中心;它允许您解耦行为代码(如果有一点C#在这里和那里滑落,那就很抱歉了)
样本类
public class S {
private List<Integer> Samples = new List<Integer>();
public void addSample(int x){
Samples.Add(new Integer(x));
}
public void Process(IOp[] operations){
for (Op operation : operations){
Process(operation);
}
}
public void Process(ICollection<IOp> operations){
for (Op operation : operations){
Process(operation);
}
}
public void Process(IOp operation){
operation.Compute(this.Samples);
}
}
公共类S{
私有列表示例=新列表();
公共void addSample(int x){
添加(新的整数(x));
}
公共作废流程(IOp[]操作){
public static void main(String args[]) {
S s = new S();
s.addSample(1);
/* ... */
ComputeMeanOperation op1 = new ComputeMeanOperation();
CheckConvergenceOperation op2 = new CheckConvergenceOperation ();
// Anonymous Operation
Op<Integer> op3 = new Op<Integer>(){
public void Compute(List<Integer> samples){
this.Result = samples[0]; // Gets first value of samples
}
}
s.Process(op1); // Or use overloaded methods
s.Process(op2);
s.Process(op3);
System.out.println("Op1 result: " + op1.Result);
System.out.println("Op2 result: " + op2.Result);
System.out.println("Op3 result: " + op3.Result);
}