Это четвёртый пост из серии о Poka-yoke проектировании – также известном, как инкапсуляция.
Недавно, я прочитал из какого-то технологического события Microsoft пост, написанный с энтузиазмом:
Атрибут [Required] в коде автоматически создаёт запись в БД, которая не может принимать null, а также создаёт валидацию на веб-странице – симпотично […]
Я представил, что это может выглядеть как-то так:
public class Smell
{
[Required]
public int Id { get; set; }
}
Каждый раз, когда я вижу нечто подобное, я немного приближаюсь к смерти. Если вы уже читали мой предыдущий пост, то теперь должно быть до боли ясно, почему это ломает инкапсуляцию. Несмотря на наличие атрибута [Required], нет никакой гарантии того, что свойству Id когда-либо будет присвоено значение. Атрибут это просто кусок мусора, который провозглашает нечто, что нельзя вернуть обратно.
Такой код не является отказоустойчивым.
Я понимаю, что атрибут, упомянутый в твите, предназначен для того, чтобы сигнализировать какому-нибудь инструменту (возможно Entity Framework), что свойство должно быть отображено но схему БД, как не принимающее null, но оно всё равно излишне. Атрибуты это неправильный способ декларировать утверждение об инвариантах
Исправленный дизайн
Атрибут [Required] излишний, потому что существует лучший способ для того, чтобы задекларировать то, что часть данных является необходимой (требуемой). Это было возможно ещё с версии .NET 1.0. Вот Poka-yoke версия той же декларации:
public class Fragrance
{
private readonly int id;
public Fragrance(int id)
{
this.id = id;
}
public int Id
{
get { return this.id; }
}
}
Этот простой структурный дизайн показывает, что поле является действительно необходимым (и если поле ID может быть только положительным значением, то можно добавить защитное выражение). Экземпляр класса Fragrance может быть создан только с ID. Так как это структурная конструкция, то данное требование только усиливается, благодаря компилятору, который даёт нам быструю обратную связь.
Я понимаю, что атрибут [Required], упомянутый ранее, предназначен для решения проблем с маппингом объектов на реляционные данные, но вместо закрытия дыры несоответствия между объектами и реляционным структурами, он только расширяет её.
Вместо того, чтобы вносить дополнительный излишний атрибут, команда должна заставить свой инструментарий понимать простые идиомы для инкапсуляции, такую как описанная выше.
Это совсем не так сложно сделать. В качестве примера, DI-контейнеры расцветают на структурной информации, закодированной в конструкторах (это называется авто-подключением). Команда вполне может сделать такое за кулисами атрибута [Required]. Атрибут [Required] примитивный и ядовитый хак.
Это главная причина того, почему я никогда не буду использовать Entity Framework. Он заставляет разработчиков нарушать инкапсуляцию – принцип, который я отказываюсь компрометировать.
Автор: EngineerSpock