【问题标题】:code in the middle is different, everything else the same中间的代码不一样,其他的都一样
【发布时间】:2011-12-10 23:36:54
【问题描述】:

我经常有需要做的情况:

function a1() {
 a = getA;
 b = getB;
 b.doStuff();
 ....  // do some things
 b.send()
 return a - b;
}

function a2() {
 a = getA;
 b = getB;
 b.doStuff();
 ....  // do some things, but different to above
 b.send()
 return a - b;
}

我觉得我在重复自己,但是我有....的地方,方法不同,签名不同等等。

人们通常会做什么?添加一个 if (这种类型) 做这个东西,否则做其他不同的东西?这似乎也不是一个很好的解决方案。

【问题讨论】:

    标签: oop design-patterns inheritance refactoring polymorphism


    【解决方案1】:

    Polymorphism 可能还有抽象和封装是你的朋友。

    你应该在.... // do some things 部分更好地指定你有什么样的指令。如果您总是使用相同的信息,但用它做不同的事情,那么使用简单的多态性解决方案相当容易。请参阅我对此答案的第一次修订。我假设您需要不同的信息来完成每种情况下的特定任务。

    您也没有指定这些函数是否在同一个类/模块中。如果不是,您可以使用继承来共享公共部分,并使用多态性在特定部分中引入不同的行为。如果它们在同一个类中,则不需要继承或多态。


    在不同的班级

    考虑到您在问题中指出,您可能需要根据实现子类调用具有不同签名的函数(例如,根据情况传递ab 作为参数),并假设您需要在具体实现中对中间局部变量(即ab)做一些事情:

    短版:多态性+封装:将每个子类可能需要的所有可能的输入和输出参数传递给抽象函数。如果将它们封装在一个对象中,可能会不那么痛苦。

    加长版 我会将中间状态存储在泛型类的成员中,并将其传递给实现方法。或者,您可以从实现方法中获取状态,而不是将其作为参数传递。然后,您可以创建它的两个子类来实现doSpecificStuff(State) 方法,并从超类的中间状态中获取所需的参数。如果超类需要,子类也可以修改状态。

    (接下来是 Java 细节,抱歉)

    public abstract class Generic {
      private State state = new State();
      public void a() {
         preProcess();
         prepareState();
         doSpecificStuf(state);
         clearState();
         return postProcess();
      }
      protected void preProcess(){ 
         a = getA;
         b = getB;
         b.doStuff();
      }
      protected Object postProcess(){ 
         b.send()
         return a - b;
      }
      protected void prepareState(){ 
         state.prepareState(a,b);
      }
      private void clearState() { 
         state.clear();
      }
      protected abstract doSpecificStuf(State state);
    }
    public class Specific extends Generic { 
       protected doSpecificStuf(State state) { 
           state.getA().doThings();
           state.setB(someCalculation);
       }
    }
    public class Specific2 extends Generic { 
       protected doSpecificStuf(State state) { 
           state.getB().doThings();
       }
    }
    

    同班

    另一种可能性是让preProcess() 方法返回一个State 变量,并在a1()a2() 的实现中使用它。

    public class MyClass {
      protected State preProcess(){ 
         a = getA;
         b = getB;
         b.doStuff();
         return new State(a,b);
      }
      protected Object postProcess(){ 
         b.send()
         return a - b;
      }
      public void a1(){
          State st = preProcess();
          st.getA().doThings();
          State.clear(st);
          return postProcess();
      }
      public void a2(){
          State st = preProcess();
          st.getB().doThings();
          State.clear(st);
          return postProcess();
      }
    }
    

    【讨论】:

      【解决方案2】:

      好吧,don't repeat yourself。我的黄金法则(诚然,我有时会按时打破)基于ZOI 规则:所有代码必须准确地存在零次、一次或无限次。如果您看到重复的代码,您应该将其重构为一个共同的祖先。

      也就是说,不可能给你一个明确的答案,如何重构你的代码;有无数种方法可以做到这一点。例如,如果a1()a2() 位于不同的类中,那么您可以使用多态性。如果它们位于同一个类中,您可以创建一个接收匿名函数作为参数的函数,然后 a1()a2() 只是该函数的包装器。也可以使用(颤抖)参数来改变函数行为。

      【讨论】:

      • 好点,但 OP 正在谈论必须使用具有不同签名的方法。公共部分应该如何知道将哪些参数传递给这个匿名函数?如果这些参数是公共部分的局部变量会怎样?
      • @XaviLópez - 共同的部分不需要知道任何事情。 (比如)a_common() 函数在被a1()a2() 调用时接收已经咀嚼过的所有内容。但这在很大程度上是推测性的,因为我们没有任何代码。
      • 我不确定我理解你所说的“收到所有已经咀嚼过的东西”是什么意思。但是,我同意您的观点,即如果 OP 不添加更多上下文,这个问题不太可能取得任何进展。
      【解决方案3】:

      您可以通过以下两种方式之一解决此问题。 a1a2 都将调用 a3a3 将执行共享代码,并且:
      1. 调用它作为参数接收的函数,该函数执行a1 的中间部分或a2 的中间部分(它们将传递正确的参数),
      - 或 -
      2. 接收一个标志(例如布尔值),它会告诉它它需要做什么,并且使用 if 语句将执行正确的代码。

      【讨论】:

      • 您能否详细说明“他们将传递正确的参数”部分?如果要传递的参数是公共部分定义的局部变量会怎样?
      • @XaviLópez:在大多数现代语言中,您可以创建Function 类型的对象,并将它们作为参数传递。因此,例如在a1 中,您将创建一个执行原始a1 内容的对象,并将此对象作为参数传递。
      • 我明白你的意思,Eran,但话说回来,如果你传递的函数 a1() 需要 a_common() x 的局部变量,而在另一个实例中,你传递的函数 a2()需要a_common() 的局部变量y。如果您必须将xy 作为参数传递给您收到的函数,您如何知道a_common()?您仍然需要将所有可能的变量封装在一个对象中,并将其用作a1()a2() 的参数。
      • @XaviLópez:然后两者都通过。使用ab(公共部分中声明的唯一本地变量)同时调用a1()a2()。但是,我确实同意,如果局部变量太多,这可能是一件坏事。在这种情况下,也许应该首选第二个选项...
      【解决方案4】:

      这为设计模式“模板方法”大声疾呼

      通用部分在超类中:

      package patterns.templatemethod;
      
      public abstract class AbstractSuper {
      
      public Integer doTheStuff(Integer a, Integer b) {
          Integer x = b.intValue() + a.intValue();
      
          Integer y = doSpecificStuff(x);
      
          return b.intValue() * y;
      }
      
      protected abstract Integer doSpecificStuff(Integer x);
      }
      

      具体部分在子类中:

      package patterns.templatemethod;
      
      public class ConcreteA extends AbstractSuper {
      
      @Override
      protected Integer doSpecificStuff(Integer x) {
          return x.intValue() * x.intValue();
      }
      
      }
      

      对于每个特定的解决方案,您都实现了一个具有特定行为的子类。

      如果您将它们全部放在一个集合中,您可以遍历它们并始终调用通用方法,并且每个类都可以做到这一点。 ;)

      希望对你有帮助

      【讨论】:

      • OP 在问题中说这些方法甚至可以具有不同的签名,并且需要不同类型的信息。也许在一种情况下,他想做doSpecificStuff(a),而在另一种情况下,他想做a = doSpecificStuff(b)
      • @XaviLópez:如果之前的一切都一样,他可以将所有变量交给模板方法。所以他可以访问所有可用的值,并在方法中做任何他需要的事情。所以这个要求是没有停止的。如果您需要更多数据,则必须从其他地方检索它(例如数据库、文件、内存数据模型......)。
      • 这正是我在回答中所建议的。传递模板方法在其所有实现中可能使用的所有可能变量。封装它们可能是一个不错的选择。
      猜你喜欢
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      • 1970-01-01
      • 2017-07-27
      • 2018-01-28
      • 2013-06-21
      • 1970-01-01
      • 2013-11-28
      相关资源
      最近更新 更多