【问题标题】:How to efficiently share functions between classes without violating the Liskov Substitution Principle如何在不违反 Liskov 替换原则的情况下在类之间有效地共享函数
【发布时间】:2016-09-22 13:17:02
【问题描述】:

我有一个代码库,它最初是用许多不同的选项创建的,这些选项允许您让代码以稍微不同的方式执行相同的过程,如下所示:

public class MainFunction {
  public void main(String option){
     if (option.equals("vanilla mode")){
        this.firstFunction();
     }else{
        this.differentVersionOfFirstFunction();
     }
     this.secondFunction();
  }

  public void firstFunction(){
    //First functions code
  }

  public void secondFunction(){
     //Second functions code
  }

  public void differentVersionOfFirstFunction(){
     // different code from First functions code that produces the same type of end result using a slightly different method
  }
}

随着各种不同选项的添加和代码变得越来越复杂,这变得越来越复杂。

为了解决这个问题,我最初计划创建一个 Parent 对象,然后可以在需要时让孩子们在他们的 Parent 方法上有细微的不同变化。问题是我知道这会违反 Liskov 替换原则,实际上我可能有孩子应该共享他们父母可能不存在的相同方法。

所以我只能在同一个类对象中使用不同的方法,以稍微不同的方式完成相同的工作。

public class MainFunction {
  public void main1(){
    this.firstFunction();
    this.secondFunction();
  }

  public void main2(){
    this.differentVersionOfFirstFunction();
    this.secondFunction();
  }

  public void firstFunction(){
    //First functions code
  }

  public void secondFunction(){
    //Second functions code
  }

  public void differentVersionOfFirstFunction(){
    // different code from First functions code that produces the same type of end result using a slightly different method
  }
}

我想我可以创建一个单独的实用程序类来保存我所有的各种功能,但我不确定是否有更优雅的解决方案?

【问题讨论】:

    标签: java oop solid-principles liskov-substitution-principle


    【解决方案1】:

    我看不出您的示例如何违反 Liskov 替换原则。然而,我看到的是它们可能违反了开放/封闭原则,这意味着每次你需要修改你的程序时,你编辑一些MainFunction.java文件并且有机会影响所有可能的场景运行程序。更好的解决方案是使用大量解耦的组件,这样当您需要修改某些内容时,您只需修改一小部分,这不太可能影响程序可能遇到的所有场景。这就是单一职责原则的意义所在。

    正如另一个答案中提到的,您的方案似乎很适合应用策略模式。这可能如下所示:

    1. 使用void main()方法创建接口MainFunction,不带任何选项。
    2. 使用public abstract void main() 成员创建一个抽象策略类,例如AbstractMainFunction,不带任何选项。该类将实现MainFunction 接口。
    3. 根据需要创建 AbstractMainFunction 的单独实现,例如VanillaModeMainFunctionDifferentMainFunction。您可能会发现在AbstractMainFunction 中保留一些共享代码很有用。
    4. 创建一个策略切换器类,例如MainFunctionService。它将有一个方法,public void main(String option),就像您的第一个示例一样,并且可能会有这样的 switch 语句:

      MainFunction strategy = defaultFunction;
      switch(option) {
          case "vanilla":
               strategy = vanillaFunction;
               break;
          case "different":
               strategy = differentFunction;
               break;
      }
      strategy.main();
      

    看起来要做很多事情,但最终您会看到这如何真正简化维护和进一步开发。

    【讨论】:

    • 这个策略切换类是不是也违反了开闭原则?
    • @jaco0646,很好的问题,答案可能取决于:-)。如果选项太多并且选项的格式很复杂,那么可以,最好将选项解析功能构建到策略本身并应用 责任链 模式而不是策略模式。但是,如果逻辑相对简单,在我看来,程序的入口点有一个 switch 语句就很好了,它会遵守单一责任原则——只有一个改变的理由,即当一个新的选项进来。
    • 谢谢,我有一个可以运行的演示,现在转过我所有的代码!
    • 我已经实现了这个,虽然我不确定我是否正确,当我制作一个香草模式主函数的版本时,一个关键函数需要不同数量的参数,然后必须在子级及其继承的所有类中创建一个新函数,但是这个新函数对于不需要额外参数的其他子级毫无意义?
    • 如果函数如此不同以至于它们的参数也不同,请直接在策略上调用 main 方法,例如case "a": strategy1.main(a, b, c); break; case "b": streategy2.main(x, y, z);。还考虑拥有一个Context 类的想法,您可以将其作为单个参数传递给任何策略。然后该策略将自己从Context 中提取所需的数据,或者Context 将有一些常用的参数解析方法。
    【解决方案2】:

    也许您可以尝试使用策略模式,在您的 MainFunction 中注入您每次需要使用的 Strategy 对象。看看here

    【讨论】:

    • 谢谢,这看起来是正确的轨道,但我仍然担心当我有两个非常相似的实现时可能会有一些代码重复,我会试一试看看它是如何工作的出来。
    • 如果您担心代码重复,请查看 模板方法 模式,可能会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 2015-01-01
    • 2021-11-17
    • 1970-01-01
    • 2012-01-12
    相关资源
    最近更新 更多