Language agnostic 没有单一代码的DRY?

Language agnostic 没有单一代码的DRY?,language-agnostic,dry,Language Agnostic,Dry,我不想重复我自己(干),但我不能有一段代码。例如,这里的代码重复了3次,出现了相同的错误: class StarWars : Movie { //Calculate "base ^ exponent" public float Power(float base, float exponent) { return (base * exponent); } } class Customer: Object { //Calculate "base ^ exp

我不想重复我自己(干),但我不能有一段代码。例如,这里的代码重复了3次,出现了相同的错误:

class StarWars : Movie
{
   //Calculate "base ^ exponent"
   public float Power(float base, float exponent)
   {
      return (base * exponent);
   }
}

class Customer: Object
{
   //Calculate "base ^ exponent"
   public float Exponential(float base, float exponent)
   {
      return (base ^ exponent);
   }
}

class Student: Person
{
   //Calculate "base ^ exponent"
   public float CalculateExpoential(float base, float exponent)
   {
      return CalculateExponential(2.7182818, exponent * Ln(base));
   }
}
现在,理想情况下,我会将这个公共函数提取到它自己的助手中:

class LibraryOfHelperCode
{
    public static float Exponentiation(float base, float exponent)
    {
       return Exp(2.71828183, base * Ln(exponent));
    }
}
并将现有代码转换为使用它:

class StarWars : Movie
{
   //Calculate "base ^ exponent"
   public float Power(float base, float exponent)
   {
      return LibraryOfHelperCode.Exponentiation(base, exponent);
   }
}

class Customer: Object
{
   //Calculate "base ^ exponent"
   public float Exponential(float base, float exponent)
   {
      return LibraryOfHelperCode.Exponentiation(base, exponent);
   }
}

class Student: Person
{
   //Calculate "base ^ exponent"
   public float CalculateExpoential(float base, float exponent)
   {
      return LibraryOfHelperCode.Exponentiation(base, exponent);
   }
}
现在我已经从中提取了重复的代码

  • 权力
  • 指数型
  • 计算费用
转换为单个函数。这意味着,如果有任何错误,它们只需修复一次。在这种情况下这是好的,因为有一个bug:

   public float CalculateExpoential(float base, float exponent)
   {
      //19971012: Oops, should be natrual log of base, not exponent
      return Exp(2.71828183, exponent * Ln(base));    
   }
几年后:

   public float CalculateExpoential(float base, float exponent)
   { 
      //19990321: Oops, need to handle when exponent is zero
      if (exponent == 0)
         return 1.0;

      //19971012: Oops, should be natrual log of base, not exponent
      return Exp(2.71828183, exponent * Ln(base));    
   }
后来:

   public float CalculateExpoential(float base, float exponent)
   { 
      //19990321: Oops, need to handle when exponent is zero
      if (exponent == 0)
         return 1.0;

      //20040523: Another special case
      if (Base = 0.0) && (Exponent > 0.0) then
         return 0.0; // 0**n = 0, n > 0

      //19971012: Oops, should be natrual log of base, not exponent
      return Exp(2.71828183, exponent * Ln(base));    
   }
最后:

   public float CalculateExpoential(float base, float exponent)
   { 
      //20101027: Microsoft just release a method in .NET framework 4.0 that does
      //what we need. Use it:
      return Math.Pow(base, exponent);
   }
每个人都能得到修复。另一方面,我不能保证这些增量修复中的任何一个都不会破坏现有代码

想象一下有个人在打电话:

char ps = Math.Trunc(Exponential(ProblemSize, ProblemComplexity));
并且从未期望值大于128。他错了。虽然代码一直都是错误的,但它确实起作用了

现在我来解决问题,突然代码由于溢出和/或换行而崩溃


我今天面临的问题是,对干式通用代码的更改会影响到它使用的所有地方。唯一可接受的(poliotic)解决方案是为使用库类的每个可执行文件/moduble/namespace/类保留库类的副本

消除干燥

有没有办法摆脱这种混乱?当我不能重复我自己,但继续得到修复和改进,因为他们被添加到单一干代码


我的意思是…我应该共享代码,但在每个版本中都要分支吗?但问题是,没有人希望每个代码都是反向集成的

唯一可接受的(polotical) 解决办法是保留一份 图书馆为每一个人上课 可执行/模块化/命名空间/类 那就用它

DRY是特定软件解决方案的设计原则,但并不总是在组件或域范围内有意义。域驱动设计的方法使用诸如有界域上下文之类的概念来处理跨程序集和项目的可共享代码问题

虽然您在通用语言中给了我们一个问题,但对于这个问题没有通用的解决方案


Dan G在组合方面提出了一个很好的观点(使主根对象包含一个子对象,该子对象可以实现所需的行为,而无需使用实现)。微软的《架构指南》提倡这种方法而不是继承,只要它有意义



如果我能够的话,我会投票给米格尔和丹G,他们都给出了很好的评价。

我不认为在每个使用此功能的类中都有一个helper类真的会消除枯燥感。即使您在任何地方都重复包含/声明helper,这也让您不必重复任何进一步的功能


您所做的任何架构更改(如继承或帮助器)都将以某种方式影响使用它们的所有内容,如果继承没有意义,使用某种日期对象或帮助器进行组合可能是一个好方法。

只有更改库的接口,您的程序才会中断。如果更改库的实现会中断程序,则可能是程序与库的绑定太强。程序不应该依赖于库的内部工作。 如果您不断更改库的接口并破坏项目,则可能需要花更多时间设计库

您还应该对库使用版本控制。根据库的特定分支/版本生成代码。如果库接口发生显著变化,并且您不想更新现有项目,请为新接口创建一个新分支,新项目可以使用该分支,而旧项目可以继续使用旧分支。Bug修复可以针对一个分支编写,然后合并到另一个分支中


Git在这方面特别擅长。使用子模块将您的项目链接到库的特定提交。

您的问题似乎不是干涸,而是依赖项的版本控制。因此,您的helper类(带有bug修复)是每个主要类的依赖项。但是,只有一个主类实际引用了包含修复的helper类的版本。其他两个类可以自由选择何时升级到改进的helper类(通过依赖关系管理过程)

助手团队发布一个更新来修复东西,其他团队决定何时升级


您仍然处于干涸状态,但您可以管理更改。

这里的部分问题似乎是需求更改的正常结果。三段代码依赖于常见的业务概念“今天”。有些事情已经发生了变化,以至于“今天”不再是一个单一的概念。一些代码需要继续基于银河系标准日历的当前理解,而一些代码现在需要基于本地恒星系统日历的更灵活的概念(谈论本地化头痛!)

这是很正常的事情。用SRP术语来说,由一段代码处理的单一职责现在是两个职责,需要由单独的代码段处理,因为已经确定了一个新的变更向量。是时候重构了

Piskvor提出了一种解决问题的方法,这种方法在某些情况下很好。如果您使用的是IOC容器,或者至少是某种形式的DI,另一种方法是引入IHelper接口(希望它从一开始就存在)。然后,您只需要新的接口实现和一些配置来连接正确的i
StarWars-1.0.0.jar -> Helpers-1.0.0.jar
Empire-1.0.1.jar -> Helpers-1.0.1.jar
Jedi-1.0.0.jar -> Helpers-1.0.0.jar