#c# #design-patterns #dns #crud
#c# #шаблоны проектирования #dns #crud
Вопрос:
По моему опыту, на простом примере пользователя я обнаружил, что всегда существует немного отличающаяся часть проверки / логики для выполнения в зависимости от того, создаете вы пользователя или обновляете его (эта проблема распространяется и на обновление других объектов домена). Код быстро превращается в спагетти, когда вы добавляете либеральные дополнения с обоснованием, таким как «Просто сделайте эту проверку для пользователя при их обновлении» и «ооо, просто сделайте эту проверку, logic, insert при создании пользователя»
В сумме все это представляет собой монолитное чудовище кода, смотреть на которое чертовски неприятно. Я полагаю, что это просто здравый смысл, чтобы полностью реорганизовать / отделить эту логику. Или я сошел с ума.
Спасибо,
Ответ №1:
Ну, в основном есть два шаблона на выбор:
- Создайте совершенно отдельные варианты для добавления и обновления.
- Создайте единый случай, который обрабатывает как добавление, так и обновление.
Преимущество наличия отдельных случаев заключается в том, что код становится чище, недостатком является то, что вы повторяете большую часть кода. Использование одного случая, чтобы избежать повторения кода, также означает, что код становится более сложным.
Попытка создать что-то промежуточное, скорее всего, закончится недостатками обоих шаблонов.
Ответ №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)
Где этот метод обновил бы имя пользователя с возможностью использования бизнес-правил в рамках операции.