关于java:如何摆脱继承?

How to get rid of the inheritance?

我有一个算法,我有两个不同的算法实现。根据用户选择的模式,应该从许多地方调用这些实现。我不想在所有调用实现的地方编写条件语句。所以,我创建了一个抽象类,实现继承它。我可以在这样一个地方设置所需的模式:

1
2
3
4
5
6
if(firstMode){
    list = new ListForm1();
}
else{
    list = new LiastForm2();
}

然后在其他地方,我可以享受多态性的所有好处。它很好,但我想摆脱继承的原因如下:

  • 我听说作文比继承好得多。
  • 算法的第一种形式比第二种形式容易得多。第一种形式我只有3种方法,第二种形式我有15种方法。抽象类必须包含所有15个(和5个常用方法)。结果发现12个方法没有被第一个表单使用。
  • 从理论上讲,可能会有一种新的算法形式,它与另外两种算法的共同点更少,但它会带来10种新的方法,所有方法都需要添加一个抽象类。
  • 据我所知,战略模式在这里没有意义。以下是战略模式的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    //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);
        }
    }

    它也有同样的问题。战略应该相互联系。如果我将它们链接到接口而不是抽象类,情况会更糟。接口将包含许多方法,但对于算法的第一种形式,不需要其中许多方法。此外,在所有具体策略中,一般方法都必须重复。我不能在接口中提供默认实现。

    更重要的是,我不明白怎么用作文。据我所知,策略模式已经使用了组合。类上下文将策略实例作为字段包含在内。但也许是代表团。

    所以,我的问题是:

    我能摆脱以上所有的问题吗(抽象类的方法太多,连接很强,因此很难添加新形式的算法),但仍然只在一个地方使用条件语句,而不是在所有情况下,当我需要某种形式的算法时。

    UPD:我想说明如何调用一些方法,这些方法是以算法的第二种形式实现的,但不需要第一种形式的算法:

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

    方法getCurrentLeaders()的默认实现返回空。所以,如果我用算法的第一种形式的实例调用它,那么我会得到一个错误。我知道这很糟糕。但我该怎么解决呢?


    您可以实现某种责任链模式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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伪代码。


    I heard that composition is much better than inheritance.

    不总是-很多时候继承是正确的构造。你必须用has ais a的术语来考虑。一支足球队有一批足球运动员。它还有一个教练,一个时间表,一个名字等等,所以Team : List不是正确的结构。

    一个Car是一个Vehicle,所以继承是正确的构造。

    因此,请这样考虑您的设计:

    我的课程有共同的基础吗?有没有一个基本类可以说是ListForm1ListBaseListForm2ListBase。对于那些应该在case类型中的类型,哪些方法和属性是通用的?哪些方法和属性应该是虚拟的,以便我可以重写它们?

    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. The abstract class had to include all 15 (and 5 common methods). It turns out that the 12 methods not using by the first form.

    因此,也许您的基类型只有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.

    为什么?为什么新算法不能只定义自己需要的方法,只要客户机在继承链中选择适当的级别(或适当的接口),这样它就知道应该实现什么方法。

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

    The default implementation of method getCurrentLeaders() return null. So, if I called it with instance of the FIRST form of the algorithm then I will get an error. I understand that it is bad. But how can I solve it?

    那么,您是否需要检查这个特定的list实现了一个实现该方法的接口(或继承了一个基类)?


    如果您没有实现所有方法(即,如果抽象类中有15个方法要实现,而您只需要实现10个),那么您将打破liskov替换原则:

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

    基本上,这是件坏事。

    尝试将非常见方法转换为其他类型的对象,这些对象将传递给构造函数(抽象)。


    从一开始,在这种情况下,您需要根据用户选择的不同模式调用不同的算法,您可以创建一种工厂类来在整个代码中提供算法。我想如果它只是一个算法,如果你在Java 8上,你可以使用一个函数或者一个谓词或者一个供应商和一个映射来避免IF语句,例如:

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

    然后调用算法,只需:

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

    在您需要提供不同的表单的情况下,比如在您发布的示例中,您可以使用供应商而不是谓词。基于你的简单要求,我认为功能化是最好的选择…


    为什么不把你的IStrategy作为一个类型呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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();
        }
    }

    然后在您的用户代码中:

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