策略模式,每种算法具有不同的方法签名

时间:2017-10-06 22:07:37

标签: c# algorithm oop design-patterns strategy-pattern

我正在对某些代码进行重构。

我们有一份投资者名单,每个投资者的金额分配。金额总和应该等于另一个总金额,但有时差异会有几美分,因此我们使用不同的算法将这些差异分配给每个投资者。

目前的代码是这样的:

public void Round(IList<Investors> investors, Enum algorithm, [here goes a list of many parameters]) {

   // some checks and logic here - OMMITED FOR BREVITY

  // pick method given algorithm Enum

  if (algoritm == Enum.Algorithm1) {
      SomeStaticClass.Algorithm1(investors, remainders, someParameter1, someParameter2, someParameter3, someParameter4)
  } else if (algoritm == Enum.Algorithm2) {
     SomeStaticClass.Algorithm2(investors, remainders, someParameter3)
  }
}
到目前为止,我们只有两种算法。我必须实施第三个。我有可能重构现有的实现以及做一些通用代码来为未来的算法创建这个函数,也许是为每个客户定制的。

我的第一个念头是“好吧,这是一种策略模式”。但我看到的问题是两种算法都接收不同的参数列表(前两个除外)。未来的算法也可以接收不同的参数列表。 “共同”中唯一的东西是投资者名单和剩余部分。

我如何设计这个以便我有一个更清洁的界面? 我想到了

  1. 建立包含所有可能参数的接口,并共享它 在所有实现中。
  2. 使用具有所有可能参数的对象作为属性,并将该通用对象用作接口的一部分。一世 将有3个参数:投资者名单,剩余物对象和“参数”对象。但在这种情况下,我有类似的问题。实例化每个对象并填充所需的属性取决于算法(除非我设置了所有这些)。一世 必须使用工厂(或其他东西)来实例化它,使用界面中的所有参数,我是对的吗?我会把太多参数的问题转移到那个“工厂”或其他什么。
  3. 使用动态对象而不是静态类型对象。仍然 提出了与之前相同的问题,实例化
  4. 我还想过使用访问者模式,但据我所知,如果我有不同的算法可以使用不同的实体,就像另一类投资者那样。所以我认为这不是正确的方法。

    到目前为止,最让我信服的是第二个,尽管我对它仍然有点沉默。

    有什么想法吗?

    由于

2 个答案:

答案 0 :(得分:1)

策略有不同的实现。当所有备用具体策略需要相同类型的签名时,它很简单。但是当具体实现开始向Context请求不同的数据时,我们必须通过放松封装来优雅地退后一步(“破解封装”是策略的已知缺点),要么我们可以将Context传递给策略方法签名或构造函数取决于需要多少。

通过使用接口并将大对象树分解为较小的包含,我们可以限制对大多数Context状态的访问。

以下代码演示了如何通过方法参数。

    public class Context {
        private String name;
        private int id;
        private double salary;
        Strategy strategy;
        void contextInterface(){
            strategy.algorithmInterface(this);
        }
        public String getName() {
            return name;
        }
        public int getId() {
            return id;
        }
        public double getSalary() {
            return salary;
        }
    }

    public interface Strategy {
    // WE CAN NOT DECIDE COMMON SIGNATURE HERE
    // AS ALL IMPLEMENTATIONS REQUIRE DIFF PARAMS
    void algorithmInterface(Context context);
    }

    public class StrategyA implements Strategy{
        @Override
        public void algorithmInterface(Context context) {
            // OBSERVE HERE BREAKING OF ENCAPSULATION 
            // BY OPERATING ON SOMEBODY ELSE'S DATA
            context.getName();
            context.getId();
        }
    }

    public class StrategyB implements Strategy{
        @Override
        public void algorithmInterface(Context context) {
            // OBSERVE HERE BREAKING OF ENCAPSULATION 
            // BY OPERATING ON SOMEBODY ELSE'S DATA
            context.getSalary();
            context.getId();
        }
    }

答案 1 :(得分:0)

好吧,我可能会朝着错误的方向前进......但是你将所有算法的参数和实际算法的标识符传递给你似乎有点奇怪使用。 Round()函数不应该理想地得到它需要运行的东西吗?

我想象调用 Round()的功能看起来像:

if (something)
    algToUse = Enum.Algorithm1;
else
    if (otherthing)
        algToUse = Enum.Algorithm2;
    else
        algToUse = Enum.Algorithm3;
Round(investors, remainder, algToUse, dayOfMonth, lunarCycle, numberOfGoblinsFound, etc);

...如果你做了这样的事情,那该怎么办:

public abstract class RoundingAlgorithm
{
    public abstract void PerformRounding(IList<Investors> investors, int remainders);
}
public class RoundingRandomly : RoundingAlgorithm
{
    private int someNum;
    private DateTime anotherParam;
    public RoundingRandomly(int someNum, DateTime anotherParam)
    {
        this.someNum = someNum;
        this.anotherParam = anotherParam;
    }
    public override void PerformRounding(IList<Investors> investors, int remainder)
    {
        // ... code ...
    }
}
// ... and other subclasses of RoundingAlgorithm

// ... later on:
public void Round(IList<Investors> investors, RoundingAlgorithm roundingMethodToUse)
{
    // ...your other code (checks, etc)...

    roundingMethodToUse.Round(investors, remainders);
}    

...然后您的早期功能看起来像:

RoundingAlgorithm roundingMethod;
if (something)
    roundingMethod = new RoundingByStreetNum(1, "asdf", DateTime.Now);
else
    if (otherthing)
        roundingMethod = new RoundingWithPrejudice(null);
    else
        roundingMethod = new RoundingDefault(1000);
Round(investors, roundingMethod);

...基本上,不是填充Enum值,而是创建一个RoundingAlgorithm对象并将其传递给Round()。