Я ошибаюсь, думая, что необходимость чего-то вроде AutoMapper является признаком плохого дизайна?

35

Automapper - это «объект-объектный маппер» для .Net, что означает копирование объектов из класса в другой класс, представляющий то же самое.

Почему это всегда полезно? Является ли дублирование классов полезным / хорошим дизайном?

static_rtti
источник
Небольшое упоминание: devtrends.co.uk/blog/… Особенно читайте раздел «почему мы не можем использовать autopper?»
Яни Hyytiäinen

Ответы:

28

Быстрый поиск в Google показал этот пример:

http://www.codeproject.com/Articles/61629/AutoMapper

демонстрируя совершенно правильное использование AutoMapper, которое определенно не является примером плохого дизайна. В многоуровневом приложении у вас могут быть объекты в ваших данных или бизнес-уровне, и вам иногда требуется просто подмножество атрибутов этих объектов данных или какой-то вид на них в вашем уровне пользовательского интерфейса. Таким образом, вы создаете модель представления, которая содержит объекты с именно теми атрибутами, которые вам необходимы в вашем пользовательском интерфейсе, не более, и используете AutoMapper, чтобы обеспечить содержание этих объектов с меньшим количеством стандартного кода.

В такой ситуации ваши «объекты просмотра» не являются дубликатами исходного класса. У них разные методы и, возможно, несколько дублирующих атрибутов. Но это нормально, если вы используете это представление объектов только для целей отображения пользовательского интерфейса и не начинаете злоупотреблять ими для манипулирования данными или бизнес-операций.

Еще одна тема, которую вы можете прочитать, чтобы лучше понять это, - это шаблон разделения ответственности командного запроса Фаулера , в отличие от CRUD. Он показывает ситуации, когда различные объектные модели для запроса данных и обновления их в базе данных имеют смысл. Здесь отображение из одной объектной модели в другую также можно выполнить с помощью такого инструмента, как AutoMapper.

Док Браун
источник
1
Что касается вашего первого примера: не могли бы вы составить объекты вместо того, чтобы дублировать их? В этом случае ваши объекты пользовательского интерфейса будут иметь ссылку на ваши исходные объекты данных и будут предоставлять только необходимые элементы. Разве это не имеет смысла?
static_rtti
1
@static_rtti в теории да, но вы не обязательно хотите, чтобы ваш первоначальный класс был публичным или / и был представлен на собрании
Jalayn
2
@static_rtti: да, это совершенно другой подход. Основное различие между этими двумя проектами заключается в времени жизни данных / атрибутов. Использование копий предоставляет вам снимок данных, а использование свойств, ссылающихся на исходные данные, - нет. У обоих подходов есть свои плюсы и минусы, но ИМХО нет «лучше или хуже» в целом. Кроме того, могут быть соображения производительности, которые могут влиять или не влиять на решение о том, что использовать.
Док Браун
3
Разделение как можно больше - это всегда хорошая идея. Не потому, что это особенно выгодно даже для небольших проектов, а потому, что проекты растут.
Тамас Селеи
6
@fish: Я согласен, в основном, разделение часто является хорошей идеей, но ИМХО существует достаточно ситуаций, в которых простота в обращении перевешивает многослойный супер-разобщенный-для-развязки подход. Самое сложное - не упустить момент роста проекта, когда нужно начать рефакторинг.
Док Браун
45

Является ли дублирование классов полезным / хорошим дизайном?

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

Что касается использования AutoMapper, я лично избегаю этого по 3 причинам:

Тихие неудачи чаще встречаются

Поскольку AutoMapper сопоставляет свойства автоматически, изменение имени свойства в одном классе, а не в другом, приведет к пропуску отображения свойств. Компилятор не будет знать. Automapper не волнует.

Отсутствие статического анализа

Итак, вы получили большую кодовую базу для работы. Есть миллион классов с миллионами свойств. Много похоже, что они не используются или являются дубликатами. Этот инструмент «Найти все ссылки» в Visual Studio поможет вам увидеть, где используются свойства, и поможет составить карту того, как все приложение соединяется вместе. Но подождите, нет явных ссылок на половину свойств, потому что используется Automapper. Моя работа сейчас намного сложнее.

Поздние требования, которые увеличивают сложность

Automapper отлично работает, когда все, что вы хотите сделать, это скопировать значения из ОДНОГО класса в другой (как это часто бывает при НАЧАЛЕ разработки), но помните те требования, которые со временем меняются? Что если вам теперь нужно получить значения из других частей вашего приложения, возможно, специфичные для пользователя, который вошел в систему, или какое-то другое контекстное состояние?

Шаблон AutoMapper по созданию сопоставлений классов «один-к-одному» при запуске приложения не очень хорошо подходит для такого рода специфичных для контекста изменений. Да, возможно, есть способы заставить это работать, но я обычно нахожу чище, проще и выразительнее, чтобы писать логику самостоятельно.

Таким образом, прежде чем обратиться к Automapper, чтобы сэкономить 30 секунд, вручную сопоставляя один класс с другим, подумайте об этом.

Программирование - это искусство рассказывать другому человеку, чего хочет компьютер. - Дональд Кнут

Имея это в виду, спросите себя: "AutoMapper полезен сегодня и будет ли он завтра?"

Дейв Б 84
источник
[если вы занимаетесь модным веб-разработчиком] Итак, если вы пишете веб-сервис REST, вы всегда проверяете каждое свойство, чтобы убедиться, что ваша объектная модель JavaScript соответствует вашей объектной модели .NET?
Ден
3
@ Ден - да. И вы пишете тесты, чтобы убедиться, что все свойства указаны правильно (т. Е. Ваш тест покажет все свойства, которые вы добавили в 1 месте, которые не будут распространены на другие уровни)
gbjbaanb
@gbjbaanb Я бы, вероятно, просто использовал рефлексию, чтобы сделать эту проверку в общем виде. Возможно, изучить возможность расширения AutoMapper, чтобы добавить некоторые проверки. Определенно избегал бы большого количества ручных заданий.
День
Полностью согласен с вами и думаю, что только очень простые приложения могут использовать automapper, но когда все становится более сложным, нам нужно написать новую конфигурацию для automapper, тогда почему бы не написать ее в ручном помощнике или службе mapper.
ahmedsafan86
21

По моему опыту, когда кто-то жаловался на «слишком много шаблонов» и хочет использовать AutoMapper, это было одно из следующих:

  1. Они находят раздражающим писать один и тот же код снова и снова.
  2. Они ленивы и не хотят писать код, который делает Ax = Bx
  3. Им не хватает времени, чтобы на самом деле задуматься о том, является ли написание Ax = Bx снова и снова хорошей идеей, а затем написать хороший код для этой задачи.

Тем не мение:

  1. Если вы действительно хотите избежать повторения, вы можете создать объект или метод для его абстрагирования. Вам не нужен AutoMapper.
  2. Если вы ленивы, вы будете ленивы и не узнаете, как работает AutoMapper. Вместо этого вы примете шаблон «программирование по совпадению» и напишите ужасный код, в котором вы добавляете бизнес-логику в профиль AutoMapper. В этом случае вам следует изменить свое отношение к программированию или найти себе занятие по профессии. Вам не нужен AutoMapper.
  3. Если у вас слишком мало времени, ваша проблема не имеет ничего общего с самим программированием. Вам не нужен AutoMapper.

Если вы выбрали статически типизированный язык, воспользуйтесь этим языком. Если вы пытаетесь обойти средства управления на языке, которые помогают предотвратить ошибки из-за чрезмерного использования рефлексии и магических API, таких как AutoMapper, это просто означает, что вы выбрали язык, который не удовлетворяет вашим потребностям.

Кроме того, только то, что Microsoft добавила функции в C #, не означает, что они являются честной игрой в любой ситуации или что они поддерживают лучшие практики. Например, Reflection и ключевое слово «dynamic» следует использовать в тех случаях, когда вы просто не можете выполнить то, что вам нужно, без них. AutoMapper не является решением для любого случая использования, который не может быть решен без него.

Итак, является ли дублирование классов плохим дизайном? Не обязательно.

AutoMapper - плохая идея? Да, конечно.

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

Шаши Пенумарти
источник
1
Хорошо сказано, в частности 2.
Mardoxx
2
Я знаю, что это очень старая тема, но я не согласен с 1. Вы не можете создать метод для абстрактного отображения. Вы можете для 1 класса, но если есть 2, вам нужно 2 метода. 3 - 3 метода. С AM это 1 строка кода - определение отображения. Кроме того, я действительно боролся со сценарием, где у меня был List <BaseType>, заполненный 10 подтипами DTO. Если вы хотите отобразить это, вам нужно включить тип И метод для копирования значений полей. А что если одно из полей должно быть отображено отдельно? Ваш ответ хорош только если у вас мало простых занятий. AutoMapper выигрывает руки в более сложных сценариях.
user3512524
1
Я думаю, что единственная ловушка AM - это возможность изменить имя объекта в одном классе, а не в другом. Но на самом деле, как часто вы делаете это после того, как дизайн сделан? И вы можете написать общий тестовый метод, который использует отражение и сравнивает имена свойств. Как и все инструменты, AM должен использоваться правильно - не вслепую, не во всех ситуациях, я согласен. Но говорить «ты не должен использовать это» просто неправильно.
user3512524
7

Здесь есть более глубокая проблема: тот факт, что C # и Java настаивают на том, что большинство / все типы должны различаться по имени, а не по структуре: например, class MyPoint2Dи class YourPoint2Dявляются отдельными типами, даже если они имеют точно такие же определения. Когда типом, который вам нужен, является «какая-то анонимная вещь с xполем и yполем» (то есть с записью ), вам не повезло. Поэтому, когда вы хотите превратить YourPoint2Dв MyPoint2D, у вас есть три варианта:

  1. Напишите несколько утомительных, повторяющихся, мирских шаблонов в духе this.x = that.x; this.y = that.y
  2. Сгенерируйте код как-нибудь.
  3. Используйте отражение.

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

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

Это оставляет вас с отражением. Вы можете сделать это самостоятельно, или вы можете использовать AutoMapper.

Doval
источник
3

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

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

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

user148298
источник