假设您具有以下与统计相关的类的层次结构,其结构类似于Template方法模式:
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
}
Run Code Online (Sandbox Code Playgroud)
现在假设我们想要扩展这些类中的每一个,例如检查度量的收敛.出于我的目的,我不需要在运行时进行此扩展.我可以想到以下替代方案:
创建子类S0C,S1C,S2C和S3C从S0,S1,S2并S3与检查收敛的代码的副本分别,每个:
使用装饰器模式:
S接口上添加例如收敛检查方法,这完全破坏了任何模块化感.解除此要求的唯一方法是禁止在我的代码中嵌套装饰器.如果Java支持多重继承,我可能已经能够通过继承统计信息和基本收敛检查(或其他)类来处理这种情况.唉,Java不支持多重继承(不,接口不计算!).
有没有更好的方法来处理Java中的这个问题?也许是不同的设计模式?更技术性的解决方案?某种特殊的仪式舞蹈?
PS:如果我误解了某些东西,请随意(轻轻地)指出它......
编辑:
我似乎需要澄清一下我的目标:
我不需要运行时对象组合.我想要的是S*用新方法扩展类的功能.如果我可以根据需要创建子类而不需要代码重复,我可能会这样做.如果我能在使用地点(不太可能)这样做,甚至更好.
我宁愿不要一遍又一遍地写相同的代码.注意:委托方法和构造函数很好,我想,实现算法的方法不是.
我想保持我的接口模块化.这是我的Decorator模式的主要问题 - 除非放置非常特定的嵌套约束,否则最终会得到所有接口的超级接口...
编辑2:
要解决一些意见:
这些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 ...;
}
}
Run Code Online (Sandbox Code Playgroud)我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 ...;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意hasConverged()方法的重复.
汇聚检查装饰器将是这样的:
class CC<T extends S> implements S {
T o = ...;
int addSample(double x) {
o.addSample(x);
...;
}
double getMean() {
return o.getMean();
}
boolean hasConverged() {
return ...;
}
}
Run Code Online (Sandbox Code Playgroud)
问题:如果我想除收敛检查之外还要组合另一个分隔符行为,我需要一个单独的装饰器,例如NB- 并且为了能够访问例如hasConverged()方法,新装饰器需要:
CCCC与其包装对象类型相同的界面...S*方法,如果我想能够使用NB与S*对象不使用CC我选择的装饰师模式只是因为缺乏更好的选择.这是迄今为止我发现的最干净的解决方案.
扩展S*课程时,我仍然需要完整的原件.将汇聚功能放在一个共同的超类中意味着相关的行为(及其性能影响)现在将存在于所有子类中,这绝对不是我想要的.
基于您最近的编辑。
正如您可能已经意识到的那样,装饰器不适合于此。这是因为它解决的是单个功能的增强,而不是整个类树的增强。
实现这一目标的一种可能方法是采用策略。策略以算法为重点;它允许你解耦行为代码(很抱歉,如果这里和那里有一些 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);
}
}
Run Code Online (Sandbox Code Playgroud)
运营
public interface IOp {
// Interface is optional. Just for flexibility.
public void Compute(List<Integer> data);
}
public class Op<T> implements IOp{
// Generics is also optional. I use this to standardise data type of Result, so that it can be polymorphically accessed.
// You can also put in some checks to make sure Result is initialised before it is accessed.
public T Result;
public void Compute(List<Integer> data);
}
class ComputeMeanOperation extends Op<double>{
public void Compute(List<Integer> data){
/* sum and divide to get mean */
this.Result = /* ... */
}
}
class CheckConvergenceOperation extends Op<boolean>{
public void Compute(List<Integer> data){
/* check convergence */
this.Result = /* ... */
}
}
Run Code Online (Sandbox Code Playgroud)
用法
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);
}
Run Code Online (Sandbox Code Playgroud)
优点:
缺点/限制:
希望这符合您的要求:)