简体   繁体   English

如何摆脱遗产?

[英]How to get rid of the inheritance?

I have an algorithm, and I have 2 different implementations of the algorithm. 我有一个算法,我有2种不同的算法实现。 These implementations should be called from many places, depending on the mode selected by the user. 应该从许多地方调用这些实现,具体取决于用户选择的模式。 I wouldn't like to write conditional statements at all places where implementations called. 我不想在所有调用实现的地方编写条件语句。 So, I create an abstract class and Implementations inherit it. 所以,我创建了一个抽象类,实现继承它。 I can set the desired mode in one place like this: 我可以在一个地方设置所需的模式,如下所示:

if(firstMode){
    list = new ListForm1();
}
else{
    list = new LiastForm2();
}

And after that in all other places I can enjoy all the benefits of polymorphism. 之后在其他所有地方,我都可以享受到多态性带来的所有好处。 It works good but I want to get rid of the inheritance of the following reasons: 它工作得很好,但我想摆脱以下原因的继承:

  1. I heard that composition is much better than inheritance. 我听说组成比继承好得多。
  2. The first form of the algorith is much easier then the second form. 第一种形式的算法比第二种形式容易得多。 In the first form I have only 3 methods and in second form I have 15 methods. 在第一种形式中,我只有3种方法,在第二种形式中,我有15种方法。 The abstract class had to include all 15 (and 5 common methods). 抽象类必须包括所有15种(和5种常用方法)。 It turns out that the 12 methods not using by the first form. 事实证明,12种方法没有使用第一种形式。
  3. Theoretically, there may be a new form of the algorithm, which will have even less in common with the other two, but it will bring 10 new methods and all of them will have to add an abstract class. 从理论上讲,可能会有一种新的算法形式,与其他两种形式的共同点更少,但它会带来10种新​​方法,所有这些方法都必须添加一个抽象类。

The Strategy Pattern, as I understand, does not make sense to use here. 据我所知,策略模式在这里使用没有意义。 Here is the example of Strategy Pattern: 以下是战略模式的示例:

//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);
    }
}

It has the same problems. 它有同样的问题。 Strategies should be linked with each other. 战略应该相互联系。 If I link them with the interface instead of an abstract class it will be even worse. 如果我将它们与界面而不是抽象类链接,那将更糟糕。 Interface will contain a lot of methods but many of them will not be needed for the first form of the algorithm. 接口将包含许多方法,但第一种形式的算法不需要其中的许多方法。 In addition, general methods have to duplicate in all concrete strategies. 此外,一般方法必须在所有具体策略中重复。 I can not provide a default implementation in the interface. 我无法在界面中提供默认实现。

Moreever, I don't understand how to use composition here. 不过,我不明白如何在这里使用作文。 As I understand, Strategy Pattern already used composition. 据我了解,战略模式已经使用了组合。 Class Context includes the instance of Strategy as a field. 类Context包括Strategy的实例作为字段。 But maybe it is delegation. 但也许是代表团。

So, here is my question: 所以,这是我的问题:

Can I get rid of all the above problems (too many methods of an abstract class, the strong connection, because of which it will be difficult to add a new form of an algorithm), but still use conditional statements in only one place, not in all cases when I need some form of algorithm. 我可以摆脱所有上述问题(抽象类的方法太多,强连接,因此很难添加新形式的算法),但仍然只在一个地方使用条件语句,而不是在所有情况下我需要某种形式的算法。

UPD: I want to show how I called some methods, which implemented in SECOND form of the algorithm, but not need for the FIRST form of algorithm: UPD:我想展示我如何调用一些方法,这些方法以SECOND形式的算法实现,但不需要FIRST形式的算法:

if (list.getCurrentLeaders().contains(ballIdx))

The default implementation of method getCurrentLeaders() return null. 方法getCurrentLeaders()的默认实现返回null。 So, if I called it with instance of the FIRST form of the algorithm then I will get an error. 所以,如果我使用FIRST形式的算法调用它,那么我将得到一个错误。 I understand that it is bad. 我明白这很糟糕。 But how can I solve it? 但是我该如何解决呢?

Starting from the beginning in the case you need to call a different algorithm based on a different mode chosen by the user you could create a kind of factory class to supply the algorithm throughout your code. 从一开始就需要根据用户选择的不同模式调用不同的算法,您可以创建一种工厂类来为整个代码提供算法。 I think that if it is only an algorithm and if you are on Java 8 you can use a Function or a Predicate or a Supplier in combination with a map to avoid the if statement, for example : 我认为,如果它只是一个算法,并且如果您使用的是Java 8,则可以将函数谓词供应商与映射结合使用以避免使用if语句,例如:

Map<String, Predicate<Whatever>> map = new HashMap<>();
map.put("mode_one", (w) -> true);
map.put("mode_two", (w) -> false);

Then to call the algorithm, simply : 然后调用算法,只需:

map.get("mode_one").test()

In the case you need to supply a different form like in the example you posted, you could use a Supplier instead of a predicate. 如果您需要提供与您发布的示例中不同的表单,则可以使用供应商而不是谓词。 Based on your simple requirement, I think that going functional would be the best bet ... 基于您的简单要求,我认为实用功能将是最好的选择......

If you are not implementing all the methods (ie. if you have 15 methods in the abstract class to be implemented, and you only need to implement 10), then you are breaking the Liskov Substitution Principle : 如果你没有实现所有的方法(即如果你要在抽象类中有15个方法要实现,而你只需要实现10),那么你就违反了Liskov Substitution Principle:

https://en.wikipedia.org/wiki/Liskov_substitution_principle

Basically, that is a bad thing. 基本上,这是一件坏事。

Try and convert the non-common methods into some other kind of object that gets passed into the constructor (on the abstract). 尝试将非常用方法转换为其他类型的对象,并将其传递给构造函数(在摘要中)。

I heard that composition is much better than inheritance. 我听说组成比继承好得多。

Not always - many times inheritance is the right construct. 并非总是 - 很多时候继承是正确的结构。 You have to think about it in has a and is a terms. 你必须考虑它has a并且is a术语。 A football team has a collection pf players. 足球队有一个球员集合。 It also has a coach, a schedule, a name, etc. So Team : List<Player> is not the right construct. 它还有一个教练,一个时间表,一个名字等。所以Team : List<Player>不是正确的结构。

A Car is a Vehicle , so inheritance is the right construct. Car 是一种 Vehicle ,所以继承是正确的结构。

So think about your design this way: 所以以这种方式思考你的设计:

Do my classes share a common base? 我的班级有共同的基础吗? Is there a base class that makes sense to say ListForm1 is a ListBase and ListForm2 is a ListBase . 是否有一个基类有意义地说ListForm1 ListBaseListForm2 ListBase What methods and properties are common to those types that should be in the case type? 应该在案例类型中的那些类型有哪些共同的方法和属性? What methods and properties should be virtual so that I can override them? 哪些方法和属性应该是虚拟的,以便我可以覆盖它们?

The first form of the algorithm is much easier then the second form. 第一种形式的算法比第二种形式容易得多。 In the first form I have only 3 methods and in second form I have 15 methods. 在第一种形式中,我只有3种方法,在第二种形式中,我有15种方法。 The abstract class had to include all 15 (and 5 common methods). 抽象类必须包括所有15种(和5种常用方法)。 It turns out that the 12 methods not using by the first form. 事实证明,12种方法没有使用第一种形式。

So maybe your base type only 3 methods, and you add methods in the sub-types as necessary. 因此,您的基本类型可能只有3种方法,并且您可以根据需要在子类型中添加方法。 Remember that you can have multiple base types in the chain, but it's a chain, not a tree, meaning you can have a single parent that has another parent, but you can't have two parents. 请记住,链中可以有多个基本类型,但它是一个链,而不是一个树,这意味着您可以拥有一个拥有另一个父级的父级,但您不能拥有两个父级。

Or maybe you have orthogonal interfaces since you can implement multiple interfaces. 或者你可能有正交接口,因为你可以实现多个接口。

Theoretically, there may be a new form of the algorithm, which will have even less in common with the other two, but it will bring 10 new methods and all of them will have to add an abstract class. 从理论上讲,可能会有一种新的算法形式,与其他两种形式的共同点更少,但它会带来10种新​​方法,所有这些方法都必须添加一个抽象类。

Why? 为什么? Why can't the new algorithm just define its own methods that it needs, so long as clients pick the appropriate level in the inheritance chain (or appropriate interface(s)) so that it knows what methods should be implemented. 为什么新算法不能只定义自己需要的方法,只要客户端在继承链(或适当的接口)中选择适当的级别,以便它知道应该实现哪些方法。

if (list.getCurrentLeaders().contains(ballIdx))

The default implementation of method getCurrentLeaders() return null. 方法getCurrentLeaders()的默认实现返回null。 So, if I called it with instance of the FIRST form of the algorithm then I will get an error. 所以,如果我使用FIRST形式的算法调用它,那么我将得到一个错误。 I understand that it is bad. 我明白这很糟糕。 But how can I solve it? 但是我该如何解决呢?

So do you need to check that this particular list implements an interface (or inherits a base class) that does implement that method? 所以,你需要检查这个特殊list实现一个接口(或继承的基类), 执行该方法?

You can implement some kind of Chain Of Responsibility pattern. 您可以实施某种责任链模式。

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();
  }
}

Sorry for c# pseudo-code. 抱歉c#伪代码。

Why not just use your IStrategy as a type? 为什么不直接使用你的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();
    }
}

And then in your user code: 然后在您的用户代码中:

void doStuff()
{
    IStrategy myStrategy = StrategyFactory.Create(true);
    myStrategy.execute(1, 2);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM