Существуют ли какие-либо хорошие шаблоны / принципы для разделения логики создания и обновления

#c# #design-patterns #dns #crud

#c# #шаблоны проектирования #dns #crud

Вопрос:

По моему опыту, на простом примере пользователя я обнаружил, что всегда существует немного отличающаяся часть проверки / логики для выполнения в зависимости от того, создаете вы пользователя или обновляете его (эта проблема распространяется и на обновление других объектов домена). Код быстро превращается в спагетти, когда вы добавляете либеральные дополнения с обоснованием, таким как «Просто сделайте эту проверку для пользователя при их обновлении» и «ооо, просто сделайте эту проверку, logic, insert при создании пользователя»

В сумме все это представляет собой монолитное чудовище кода, смотреть на которое чертовски неприятно. Я полагаю, что это просто здравый смысл, чтобы полностью реорганизовать / отделить эту логику. Или я сошел с ума.

Спасибо,

Ответ №1:

Ну, в основном есть два шаблона на выбор:

  1. Создайте совершенно отдельные варианты для добавления и обновления.
  2. Создайте единый случай, который обрабатывает как добавление, так и обновление.

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

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

Ответ №2:

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

IRep

 class IRep
{
   void Add(object a);
   void Remove(object a);
}
  

BaseRep

 abstract class BaseRep : IRep
{
    void Add(object a)
    {
       if(OkToAdd(a))
       {
          // Common Rep code here
       }
    }

    void Remove(object a)
    {
       if(OkToRemove(b))
       {
          // Common Rep code here
       }
    }

    abstract bool OkToAdd(object a);
    abstract bool OkToRemove(object a);
}
  

MyRep1

 class MyRep1 : BaseRep
{
    bool OkToAdd(object a)
    {
       // Add specific checks here for MyRep1
    }

    bool OkToRemove(object a)
    {
       // Add specific checks here for MyRep1
    }
}
  

MyRep2

 class MyRep2 : BaseRep
{
    bool OkToAdd(object a)
    {
       // Add specific checks here for MyRep2
    }

    bool OkToRemove(object a)
    {
       // Add specific checks here for MyRep2
    }
}
  

Комментарии:

1. Спасибо. Это тот ответ, который я искал.

Ответ №3:

Мне нравится, чтобы они были полностью разделены. Даже если это может выглядеть как дублирование кода, это позволяет независимо развивать эти действия в будущем. И они могут сильно отличаться друг от друга: при создании могут быть некоторые поля, которые не являются обязательными или даже не разрешены для установки, и в каждом случае может быть множество разных правил, которые необходимо проверять. Если вы полностью разделите эти действия, то у вас будет полная свобода в их развитии.

Ответ №4:

Я обнаружил, что всегда существует немного отличающаяся часть проверки / логики для выполнения в зависимости от того, создаете ли вы или обновляете пользователя

Конечно, здесь задействована другая логика.

Создание объекта и внесение изменений в объект — это две совершенно разные операции. Не пытайтесь выполнять с ними одну и ту же операцию.

Комментарии:

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

2. И я говорю, разделите их. Кроме того, наличие простого «Обновления» также далеко от общего в большинстве случаев. Что обновлять и почему? В случае пользователя вы можете изменять его имя пользователя, адрес электронной почты или пароль — все из которых, вероятно, будут иметь очень разные требования и, следовательно, не должны представлять собой единую функцию «Обновления» (которая, предположительно, может изменить какие-либо данные).

Ответ №5:

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

Похоже, то, что я описывал, является симптомом анемичной модели предметной области и сценария транзакции. Где бизнес-правила, скрытые в методе / операции, используемых для сохранения, быстро запутываются, если их включить в операцию создания / upsert / update в репозитории или в хранилище сохраняемости.

Намерение и бизнес-правила быстро теряются при таком подходе.

Способ облегчить это — использовать DDD-подход и тщательно моделировать поведение так, чтобы изменялось и затем сохранялось только соответствующее состояние (скорее всего, для агрегата или объекта entity / value в этом агрегате). Это помогает гарантировать наличие проверяемой истории благодаря операции, явно указывающей, какое состояние изменяется. Например:

 Customer.AddUsername(string username)
  

Где этот метод обновил бы имя пользователя с возможностью использования бизнес-правил в рамках операции.