Хороший дизайн программного обеспечения, когда бизнес-правила в коде полагаются на значения в базе данных - PullRequest
2 голосов
/ 30 ноября 2011

Иногда возникают ситуации, когда мне нужно взять значение из таблицы поиска в базе данных, например:

Id | Description
----------------
1  | descriptive text for 1
2  | descriptive text for 2

Значение Id часто хранится в переменной:

private int DB_LookUp_Id = GetIdFromDB();

Это значение DB_LookUp_Id затем используется для выполнения проверки или другой бизнес-логики, такой как:

if (DB_LookUp_Id == 1)
    return true

Этот код сломается, если значение Id изменится, скажем, с 1 на 3

Какой хороший способ устранить эту проблему?

Спасибо

Ответы [ 4 ]

2 голосов
/ 30 ноября 2011

Лучшая практика, которую я видел, требует больше семантической информации в базе данных.Поместить все справочные данные в базу данных, и для нормализации данных используются несколько таблиц.

Представьте себе систему онлайн-заказов, которая должна знать, в каком состоянии находится клиент, и должна знать, в каких штатах указаны действительные места доставки.

1) Возможно, у вас есть таблицы базы данных с именем STATES

  KEY | NAME
 --------------------
  AL  | Alabama
 --------------------
  AK  | Alaska
 --------------------
  PR  | Puerto Rico
 --------------------

2) Создайте объект справочных данных для переноса строки в базе данных.

class StateReference {
    string key
    string name
}

3) Создайте реляционную таблицу мест доставки, называемую SHIP_LOC, с ключом кода состояния («WA», «ИЛИ и т.создайте метод, подобный следующему:

bool isValidShippingLocation(StateReference object) {

     return dbLookup('SHIP_LOC', object.key)
}

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

Более того, справочные данные теперь имеют семантически значимые имена.Устраняет догадки о том, что означает «1» или код «X» в исходном коде.

2 голосов
/ 30 ноября 2011

Имеем основанный на коде ключ вместе со статическим идентификатором базы данных, для этого мы склонны использовать либо Guids, либо перечисляемые значения:

Id | Key | Description
----------------
1  | 1   | descriptive text for 1
2  | 2   | descriptive text for 2

Тогда в коде вы могли бы иметь:

enum DescriptiveTextParts
{
    Part1 = 1,
    Part2 = 2,
}

if (DB_LookUp_Key == DescriptiveTextParts.Part1)
    return true;

Не уверен, что это "лучшая практика", но она работает для нас. Первичные ключи базы данных не всегда должны быть известны или использованы приложением, хотя в большинстве случаев они используются.

В качестве альтернативы, если это всего лишь текст, отправьте все это в файл ресурсов. Это дает вам преимущества локализации, времени компиляции и производительности. В ущерб доступу базы данных к строкам ресурсов (практически нет доступа) и возможности выпускать новые строки без повторной компиляции.

0 голосов
/ 02 декабря 2011

Очевидно, что если эта таблица базы данных просто служила таблицей строк для хранения одного текста, то то, что другой сказал относительно передачи этого к ресурсам и т. Д., Является правильным ответом.

Но я подозреваю, что это не так. Данные могут иметь и имеют особое значение для бизнеса, хотим мы этого или нет. В какой базе данных нет столбца с именем STATUS со значениями, такими как «P» для ожидающих, «O» для открытых и «C» для закрытых. Вы можете поместить эти значения в таблицу STATUS_CODES со столбцами CODE и DESCRIPTION (и вам, вероятно, следует это сделать), но это не меняет того факта, что где-то в вашем приложении (или, что более вероятно, в десятках приложений) находится код, который принимает логические решения против "P", "O" и "C".

Ваши вопросы были

Этот код сломается, если значение Id изменится, скажем, с 1 на 3. Что хороший способ устранить эту проблему?

Ответ - то, что мы называем здесь "ДДТ". Не делай этого. P всегда должен быть P и никогда не должен заменяться чем-то другим, кроме P. Точка, конец предложения. Не поймите меня неправильно, подобные вещи, эти волшебные куки должны быть сведены к минимуму, задокументированы и использованы со слоем косвенности в ваших приложениях (подробнее через минуту). Но мы все живем в реальном мире с реальными системами.

При написании кода для таких типов значений вам никогда не нужно жестко кодировать их в своих методах. При голом минимуме это константа или перечисление. Мне лично нравится относиться к ним как к первоклассным гражданам в моих объектных моделях кода. Например, значение STATUS, которое мы видели ранее, становится объектом OrderStatus.

class OrderStatus

   public static readonly OrderStatus ClosedStatus = new OrderStatus("C");

   public static readonly OrderStatus OpenStatus = new OrderStatus("O");

   public static readonly OrderStatus PendingStatus = new OrderStatus("P");

   public static OrderStatus FromCode(string code)
   {
      if (code == "C")
         return ClosedStatus;
      else if (code == "O")
         return OpenStatus;
      else if (code == "P")
         return PendingStatus;
      else
         throw something;
   }

   private string _code;

   private OrderStatus(string code)
   {
      // private so cannot be created externally
      _code = code;
   }

   public string StatusCode { get { return code; } }

   // etc, etc.  Helpful to make ToString() return the inner status code.

}

Тогда в вашем объекте Order у вас будет свойство типа OrderStatus. И вы можете выполнять свои логические тесты, не жестко программируя те надоедливые односимвольные значения столбцов, которые, похоже, нравятся администраторам баз данных.

if (someOrder.Status == OrderStatus.Closed)
   MessageBox.Show("Cannot modify closed orders.");
0 голосов
/ 30 ноября 2011

Это плохой шаблон, так как он устанавливает зависимость от вашего кода C # от вашей базы данных таким образом, что внесение изменений в базу данных может привести к непредвиденным последствиям для работы кода.

Существует множестворазные подходы, но они наиболее уместны при определенных обстоятельствах.Например, если DB_LookUp_Id является первичным ключом и имеется конечное число записей, он может хорошо отображаться в перечисления и производные классы и лучше обслуживаться с помощью фабричного шаблона.Если DB_LookUp_Id может занимать много строк и будет продолжать расти в течение жизненного цикла приложения, структура классов, содержащая эту логику, может быть более общей, чем производные классы.Существует множество возможностей.

В общих чертах я бы начал с изучения характера того, что представляют эти данные, и того, что происходит с этими данными с течением времени.Как только вы начнете точно определять, как эти данные будут себя вести (в отличие от того, как они используются), вы сможете лучше справиться с этим аспектом своего приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...