СУХОЙ без единого кода? - PullRequest
       11

СУХОЙ без единого кода?

3 голосов
/ 27 октября 2010

Я не хочу повторяться (СУХОЙ), но у меня не может быть ни одного куска кода.Например, вот код, повторенный 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);
   }
}

Значение в том, что теперь я извлек повторный код из

  • Мощность
  • Экспоненциальная
  • CalculateExpoential

в одну функцию.Это означает, что если есть какие-либо ошибки, их нужно исправить только один раз.Что хорошо в этом случае, потому что - это ошибка:

   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. Он ошибся.И хотя код был неправильным все это время: случилось сработало.

Теперь я пришел и исправил вещи, и вдруг кодпроисходит сбой из-за переполнения и / или переноса.


Проблема, с которой я сталкиваюсь сегодня, заключается в том, что изменение в общем коде DRY влияет везде, где он используется.Единственное приемлемое (политическое) решение - сохранить копию библиотечного класса для каждого исполняемого файла / модуля / пространства имен / класса, который его использует.

Отмена любой СУХОСТИ.

Есть ли выход?этого беспорядка?Когда я не могу повторить себя, но продолжаю получать исправления и улучшения по мере их добавления в один код DRY?


я имею в виду ... должен ли я иметь общий код, но ветвить его при каждом выпуске?Но проблема в том, что никто не хочет, чтобы код каждого был обратно интегрирован.

Ответы [ 5 ]

1 голос
/ 27 октября 2010

Ваши программы должны ломаться, только если вы измените интерфейс вашей библиотеки. Если изменение реализации библиотеки нарушает вашу программу, вы, вероятно, слишком сильно привязываете программу к библиотеке. Программа не должна зависеть от внутренней работы библиотеки. Если вы постоянно меняете интерфейс своей библиотеки и ломаете свои проекты, вам, вероятно, нужно больше времени уделять разработке своей библиотеки.

Вы также должны использовать контроль версий для своих библиотек. Создайте свой код для определенной ветви / версии библиотеки. Если интерфейс библиотек существенно меняется, и вы не хотите обновлять существующие проекты, создайте новую ветвь для нового интерфейса, которую могут использовать новые проекты, в то время как старые проекты могут продолжать использовать старую ветку. Исправления ошибок могут быть написаны для одной ветви и объединены с другой.

Git особенно хорош в таких вещах. Используйте субмодули, чтобы связать ваш проект с определенным коммитом библиотеки.

0 голосов
/ 27 октября 2010

Кажется, что частью проблемы здесь является нормальный результат изменения требований. Три фрагмента кода опираются на общую бизнес-концепцию «сегодня». Что-то изменилось так, что больше нет единой концепции для «Сегодня». Некоторый код должен соответствовать текущему пониманию, основанному на стандартном календаре галактики, а некоторый код теперь нуждается в более гибкой концепции, основанной на календаре локальной звездной системы (поговорим о головной боли локализации!)

Это просто нормальная вещь, которая случается. Если говорить в терминах SRP, то, что было единственной обязанностью, выполняемой одним фрагментом кода, теперь является двумя обязанностями, которые должны обрабатываться отдельными частями кода, поскольку был определен новый вектор изменений. Время рефакторинга.

Писквор предложил один из способов решения проблемы, который хорош в некоторых сценариях. Другой подход, если вы используете контейнеры IOC или, по крайней мере, какую-то форму DI, заключается во введении интерфейса IHelper (надеюсь, он был там с самого начала). Тогда все, что вам нужно, - это новая реализация интерфейса и некоторая конфигурация для подключения правильной реализации для каждой системы.

wllmsaccnt также правильно, что DRY часто не подходит через границы системы, где вы не хотите создавать зависимости. Тот факт, что разные системы имеют разных владельцев продуктов, является в значительной степени вектором изменений, который выступает за отдельные базы кода, даже если одна из них начинается как форк другой. База общего кода означает совместное владение, и если это не является политически реалистичным, то это технически нецелесообразно.

0 голосов
/ 27 октября 2010

Может показаться, что ваша проблема не столько в СУХОЙ, а скорее в управлении версиями зависимостей. Итак, у вас есть ситуация, когда ваш вспомогательный класс (с исправлением ошибки) является зависимостью каждого из ваших основных классов. Однако только один из первичных классов фактически ссылается на версию вспомогательного класса, содержащего исправление. Два других класса могут свободно выбирать, когда они будут обновлены до улучшенного вспомогательного класса (через процесс управления зависимостями).

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

Команда помощников выпускает обновление, чтобы исправить вещи, а другие команды решают, когда они обновятся.

Вы все еще СУХОЙ, но вы управляете изменением.

0 голосов
/ 27 октября 2010

Я не думаю, что наличие вспомогательного класса в каждом из ваших классов, которые используют эту функциональность, действительно устраняет СУХОСТЬ.Даже если вы повторяете включение / объявление помощника повсюду, это дает вам возможность не повторять какие-либо дополнительные функции.

Любые изменения архитектуры, которые вы вносите (например, наследование или помощник), должны будут каким-то образом повлиять на все, что их использует, и, если наследование не имеет смысла, составьте с каким-либо объектом Dateили Хелпер, вероятно, хороший путь.

0 голосов
/ 27 октября 2010

Единственно приемлемый (политический) Решение состоит в том, чтобы сохранить копию класс библиотеки для каждого исполняемый файл / moduble / пространство имен / класс который использует это.

DRY - это принцип разработки для конкретного программного решения, но он не всегда имеет смысл в рамках сборки или границ домена. Методология Domain Driven Design использует такие концепции, как контексты ограниченного домена, для решения проблем общего кода в сборках и проектах.

Хотя вы задали нам проблему на родовом языке, для этой проблемы не существует общего решения.


Дэн Г делает хорошее замечание о композиции (заставляя основной корневой объект содержать подобъект, который может реализовывать необходимое поведение без использования реализации). Руководство по архитектуре Microsoft поддерживает этот подход, а не наследование, когда оно имеет смысл.


Я бы проголосовал за Мигера и Дэна Дж, если бы я мог, они оба сделали хорошие комментарии.

...