我有一个算法,我有两种不同的算法实现。应该从许多地方调用这些实现,具体取决于用户选择的模式。我不想在所有调用实现的地方编写条件语句。所以,我创建了一个抽象类,实现继承它。我可以在一个地方设置所需的模式,如下所示:
if(firstMode){
list = new ListForm1();
}
else{
list = new LiastForm2();
}
之后在其他所有地方,我都可以享受多态性带来的所有好处。 它运作良好,但我想摆脱以下原因的继承:
据我所知,战略模式在这里使用没有意义。 以下是战略模式的例子:
//abstract strategy
interface Strategy {
int execute(int a, int b);
}
// concrete strategy1
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
return a + b;
}
}
// concrete strategy2
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
return a - b;
}
}
//concrete strategy3
class ConcreteStrategyMultiply implements Strategy {
public int execute(int a, int b) {
return a * b;
}
}
class Context {
private Strategy strategy;
public Context() {
}
// Set new concrete strategy
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
// use strategy
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
它有同样的问题。战略应该相互联系。如果我将它们与界面而不是抽象类链接,那将更糟糕。接口将包含许多方法,但第一种形式的算法不需要其中的许多方法。此外,一般方法必须在所有具体策略中重复。我无法在界面中提供默认实现。
但是,我不明白如何在这里使用作文。据我了解,战略模式已经使用了组合。类Context包括Strategy的实例作为字段。但也许是代表团。
所以,这是我的问题:
我可以摆脱所有上述问题(抽象类的方法太多,强连接,因此很难添加新形式的算法),但仍然使用条件语句只在一个地方,而不是在我需要某种形式的算法的所有情况下。
UPD: 我想说明我如何调用一些方法,这些方法以SECOND形式的算法实现,但不需要第一种形式的算法:
if (list.getCurrentLeaders().contains(ballIdx))
方法getCurrentLeaders()
的默认实现返回null。所以,如果我使用FIRST形式的算法调用它,那么我将得到一个错误。我明白这很糟糕。但是我该如何解决呢?
答案 0 :(得分:1)
从一开始就需要根据用户选择的不同模式调用不同的算法,您可以创建一种工厂类来为整个代码提供算法。我认为,如果它只是一个算法,并且如果您使用的是Java 8,则可以将Function或Predicate或Supplier与地图结合使用以避免使用if语句,例如:
Map<String, Predicate<Whatever>> map = new HashMap<>();
map.put("mode_one", (w) -> true);
map.put("mode_two", (w) -> false);
然后简单地调用算法:
map.get("mode_one").test()
如果您需要提供与您发布的示例中不同的表单,则可以使用供应商而不是谓词。 根据您的简单要求,我认为实用功能将是最好的选择......
答案 1 :(得分:1)
如果你没有实现所有的方法(即如果你要在抽象类中有15个方法来实现,而你只需要实现10),那么你就违反了Liskov替换原则:
https://en.wikipedia.org/wiki/Liskov_substitution_principle
基本上,这是一件坏事。
尝试将非常用方法转换为传递给构造函数的其他类型的对象(在摘要中)。
答案 2 :(得分:1)
我听说组合比继承好得多。
并非总是 - 很多时候继承是正确的结构。您必须以has a
和is a
条款来考虑它。足球队有一个收集球员。它还有教练,日程表,姓名等。所以Team : List<Player>
不是正确的结构。
Car
是 Vehicle
,因此继承是正确的构造。
所以以这种方式思考你的设计:
我的课程是否有共同基础?是否有一个基类可以说ListForm1
是 ListBase
而ListForm2
是 ListBase
。应该在案例类型中的那些类型有哪些共同的方法和属性?哪些方法和属性应该是虚拟的,以便我可以覆盖它们?
第一种形式的算法比第二种形式容易得多。在第一种形式中,我只有3种方法,在第二种形式中,我有15种方法。抽象类必须包括所有15种(和5种常用方法)。事实证明,12种方法没有使用第一种形式。
因此,您的基本类型可能只有3种方法,并且您可以根据需要在子类型中添加方法。请记住,链中可以有多个基本类型,但它是一个链,而不是一个树,这意味着您可以拥有一个拥有另一个父级的父级,但您不能拥有两个父级。
或者你可能有正交接口,因为你可以实现多个接口。
理论上,可能有一种新形式的算法,与其他两种方法的共同点更少,但它会带来10个新方法,所有这些方法都必须添加一个抽象类。
为什么呢?为什么新算法不能只定义自己需要的方法,只要客户端在继承链(或适当的接口)中选择适当的级别,以便它知道应该的方法实施。
if (list.getCurrentLeaders().contains(ballIdx))
方法
getCurrentLeaders()
的默认实现返回null。所以,如果我使用FIRST形式的算法调用它,那么我将得到一个错误。我明白这很糟糕。但是我该如何解决呢?
那么你需要检查这个特定的list
是否实现了 实现该方法的接口(或继承基类)?
答案 3 :(得分:1)
您可以实施某种责任链模式。
interface IStrategy {
void Run();
bool CanHandle(IContext context);
}
class StrategyChecker {
IStrategy GetStrategy(IContext context) {
foreach(var strategy in strategies) {
if(strategy.CanHandle(context)
return strategy;
}
return defaultStrategy;
}
}
class Director {
void Run() {
strategyChecker.AddStrategy(strategy1);
strategyChecker.AddStrategy(strategy2);
var strategy = strategyChecker.GetStrategy(someContext);
strategy.Run();
}
}
抱歉c#伪代码。
答案 4 :(得分:0)
为什么不将IStrategy
用作类型?
interface IStrategy {
int execute(int a, int b);
}
class Strategy1 implements IStrategy {}
class Strategy2 implements IStrategy {}
static class StrategyFactory {
IStrategy Create(bool first) {
return first ? new Strategy1() : new Strategy2();
}
}
然后在您的用户代码中:
void doStuff()
{
IStrategy myStrategy = StrategyFactory.Create(true);
myStrategy.execute(1, 2);
}