我正在对某些代码进行重构。
我们有一份投资者名单,每个投资者的金额分配。金额总和应该等于另一个总金额,但有时差异会有几美分,因此我们使用不同的算法将这些差异分配给每个投资者。
目前的代码是这样的:
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)
}
}
到目前为止,我们只有两种算法。我必须实施第三个。我有可能重构现有的实现以及做一些通用代码来为未来的算法创建这个函数,也许是为每个客户定制的。
我的第一个念头是“好吧,这是一种策略模式”。但我看到的问题是两种算法都接收不同的参数列表(前两个除外)。未来的算法也可以接收不同的参数列表。 “共同”中唯一的东西是投资者名单和剩余部分。
我如何设计这个以便我有一个更清洁的界面? 我想到了
我还想过使用访问者模式,但据我所知,如果我有不同的算法可以使用不同的实体,就像另一类投资者那样。所以我认为这不是正确的方法。
到目前为止,最让我信服的是第二个,尽管我对它仍然有点沉默。
有什么想法吗?
由于
答案 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()。