【问题标题】:Decorator pattern loses virtual method lookups. Any good alternatives or workarounds?装饰器模式丢失了虚拟方法查找。有什么好的替代方案或解决方法吗?
【发布时间】:2012-08-26 07:38:22
【问题描述】:

背景

我的情况如下:

我有一个Analyzer 类型,大致如下所示

interface Analyzer {
    int computeValue();
    String performAnalysis();
}

由类似的东西实现

class AnalyzerImpl implements Analyzer {

    @Override
    public int computeValue() {
        return 5;
    }

    @Override
    public String performAnalysis() {
        return "Result: " + computeValue();
    }
}

(在我的实际代码中,performAnalysis 使用几种不同的 computeValue 不同复杂度的方法执行许多计算。)

现在我需要在运行时选择性地调整 Analyzer 对象的行为(或使用调整后的行为创建包装器)。

我尝试了什么:

我添加了调整方法:

public Analyzer tweakAnalyzer(Analyzer untweakedAnalyzer) { ... }

并尝试使用decorator pattern解决它:

class AnalyzerDecorator implements Analyzer {
    Analyzer delegate;
    public AnalyzerDecorator(Analyzer delegate) {
        this.delegate = delegate;
    }

    @Override
    public int computeValue() {
        return delegate.computeValue();
    }

    @Override
    public String performAnalysis() {
        return delegate.performAnalysis();
    }
}

然后我实现tweakAnalyzer如下:

public Analyzer tweakAnalyzer(Analyzer untweakedAnalyzer) {
    return new AnalyzerDecorator(untweakedAnalyzer) {
        @Override
        public int computeValue() {
            return 1337;              // tweaked value!
        }
    };
}

但是,当做

tweakAnalyzer(new AnalyzerImpl()).performAnalysis();

我得到未调整的值Result: 5,而不是所需的Result: 1337

(这是因为调整后的分析器与未调整的分析器不是同一个对象,它只是一个包装器,因此在AnalyzerImpl 中对computeValue 的调用无法按预期工作。)

Full ideone.com example.

TL;DR:

我想在运行时调整对象的行为。我使用了装饰器模式,但“丢失”了所有虚拟方法。

问题:

解决此问题的最佳方法是什么,即我应该如何调整 Analyzer 的行为,以便我不会丢失虚拟方法查找,或者我是否无关紧要做什么?

【问题讨论】:

  • 这可能是一个愚蠢的问题,但是当你在做return new AnalyzerDecorator(untweakedAnalyzer) 时为什么不覆盖performAnalysis()
  • @Sujay,被覆盖的performAnalysis 应该如何看待?在我的实际代码中,AnalyzerImpl.performAnalysis 是一个相当长且复杂的方法。我不想复制它:-/
  • 也许你可以改用策略模式。

标签: java oop decorator virtual-functions


【解决方案1】:

问题是performAnalysis 没有调用你的装饰器,因为它不知道你装饰了这个类。

您可能应该将performAnalysiscomputeValue 分成两个类(AnalyzerperformAnalysisCalculatorcomputeValue

这样,你可以独立装饰它们,并将装饰的计算器传递给Analyzera'la performAnalysis(Calculator calculator)

一个例子at IDEOne。是的,它基本上是 Strategy Pattern 在行动。

【讨论】:

  • 如果您将计算器命名为:ComputationStrategy,那么您就有了一个策略设计模式。
  • 这是一个有趣的选项。我会试一试,然后回复你。谢谢,尤其是 ideone 代码!
  • 我现在仔细研究了这一点。不幸的是,我的computeValue 方法相互调用(例如getTotalCost 调用getVariableCostsgetDirectCosts)。因此我相信问题仍然存在于Calculator 类中。
【解决方案2】:

您可以访问分析器对象的代码吗?如果是这样,您可以使用策略模式将其设计为可调整的。

替代方案(具有多种分派的模板模式):

abstract class Analyzer {

   public final int ComputeValue () {
      return doComputeValue (this);
   }

   public final string PerformAnalysis () {
      return doPerformAnalysis (this);
   }

   protected abstract int doComputeValue (Analyzer myself);
   protected abstract string doPerformAnalysis (Analyzer myself);
}

class AnalyzerImpl extends Analyzer {

   protected int doComputeValue (Analyzer myself) {
      ...
   }

   protected string doPerformAnalysis (Analyzer myself) {
      return "Result: " + myself.ComputeValue();
   }
}

class AnalyzerDecorator extends Analyzer {
    Analyzer delegate;
    ...
    protected string doPerformAnalysis (Analyzer myself) {
        return delegate.doPerformAnalysis (myself);
    }
    protected int doComputeValue (Analyzer myself) {
        return delegate.doComputeValue (myself);
    }
}

【讨论】:

  • 谢谢。是的,这种方法也让我印象深刻,但是Analyzer 中的方法太多了,这不可行。仍然 +1。
  • 请注意,您只需将 Analyzer 中的方法加倍,因为其中一半是最终的。
  • 啊,你把接口改成了抽象类。这当然很好。
猜你喜欢
  • 1970-01-01
  • 2021-09-11
  • 2010-10-14
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 2021-09-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多