Почему все активные записи ненавидят?

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

Часто люди говорят, что он плохо масштабируется (цитируя Twitter как пример), но никто не объясняет, почему почему он плохо масштабируется; и/или как достичь плюсов AR без минусов (с помощью аналогичного, но другого шаблона?)

Надеюсь, что это не превратится в святую войну о шаблонах дизайна - все, что я хочу знать, - это, в частности, **** что случилось с Active Record.

Если он плохо масштабируется, почему бы и нет?

Какие еще проблемы у него есть?

Ответ 1

Здесь ActiveRecord шаблон дизайна и ActiveRecord the Rails ORM Library, а также тонна нокаутов для .NET и других языков.

Все это разные вещи. Они в основном следуют этому шаблону дизайна, но расширяют и изменяют его разными способами, поэтому, прежде чем кто-либо скажет "ActiveRecord Sucks", он должен быть квалифицирован, говоря "какой ActiveRecord есть там?"

Я знаком с ActiveRecord Rails, я попытаюсь разобраться со всеми жалобами, которые были подняты в контексте его использования.

@BlaM

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

код:

class Person
    belongs_to :company
end
people = Person.find(:all, :include => :company )

Это генерирует SQL с LEFT JOIN companies on companies.id = person.company_id и автоматически генерирует связанные объекты Company, поэтому вы можете сделать people.first.company, и ему не нужно ударять по базе данных, поскольку данные уже присутствуют.

@pix0r

Врожденная проблема с Active Record заключается в том, что запросы базы данных автоматически генерируются и выполняются для заполнения объектов и изменения записей в базе данных

код:

person = Person.find_by_sql("giant complicated sql query")

Это обескураживает, так как это уродливо, но для случаев, когда вы просто и просто должны писать raw SQL, это легко сделать.

@Tim Sullivan

... и вы выбираете несколько экземпляров модели, вы в основном делаете "select * from..."

код:

people = Person.find(:all, :select=>'name, id')

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

Ответ 2

Я всегда обнаружил, что ActiveRecord хорош для быстрых приложений на основе CRUD, где модель относительно плоская (как и в, а не в иерархиях классов). Однако для приложений со сложными иерархиями OO DataMapper, вероятно, является лучшим решением. Хотя ActiveRecord предполагает соотношение 1:1 между вашими таблицами и вашими объектами данных, такие отношения становятся громоздкими с более сложными доменами. В своей книге о шаблонах Мартин Фаулер указывает, что ActiveRecord имеет тенденцию ломаться в условиях, когда ваша модель довольно сложна, и предлагает DataMapper в качестве альтернативы.

Я нашел это на практике. В случаях, когда у вас много наследования в вашем домене, сложнее сопоставить наследование с вашей РСУБД, чем отображать ассоциации или состав.

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

Вместо того, чтобы напрямую загружать объект User и получать доступ к данным с помощью API стиля ActiveRecord, ваш код контроллера извлекает объект User, например, путем вызова API метода UserMapper.getUser(). Это тот, кто отвечает за загрузку любых связанных объектов из своих соответствующих таблиц и возвращает завершенный объект "домен" пользователя вызывающему.

По сути, вы просто добавляете еще один уровень абстракции, чтобы сделать код более управляемым. Независимо от того, содержат ли ваши классы DataMapper необработанный пользовательский SQL или обращаются к API уровня абстракции данных или даже к самому шаблону ActiveRecord, на самом деле не имеет значения код контроллера, который получает хороший, заполненный пользовательский объект.

Во всяком случае, как я это делаю.

Ответ 3

Я думаю, что существует очень много разных причин, почему люди "ненавидят" ActiveRecord и что "неправильно" с ним.

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

Чтобы добавить к теме ответа комментаторам, которые говорят, что в ActiveRecord вещи сложны с репликами фрагмента кода

@Sam McAfee Скажите, что у вас есть класс пользователя в вашем домене, и вам нужно иметь ссылки или коллекции других объектов, которые уже загружены при извлечении этого объекта User. Данные могут поступать из разных таблиц, и шаблон ActiveRecord может сделать его действительно трудным.

user = User.find(id, :include => ["posts", "comments"])
first_post = user.posts.first
first_comment = user.comments.first

Используя опцию включения, ActiveRecord позволяет вам переопределить поведение по умолчанию для ленивой загрузки.

Ответ 4

Мой длинный и поздний ответ, даже не полный, но хорошее объяснение ПОЧЕМУ Я ненавижу эту картину, мнения и даже некоторые эмоции:

1) короткая версия: Active Record создает " тонкий слой" сильная привязка "между базой данных и кодом приложения. Который не решает никаких логических, никаких проблем, проблем вообще. IMHO не предоставляет ЛЮБОГО ЗНАЧЕНИЯ, за исключением синтаксического сахара для программиста (который затем может использовать" синтаксис объекта" для доступа к некоторым данным, существующим в реляционной базе данных). Усилия по созданию некоторого комфорта для программистов должны (IMHO...) лучше инвестировать в инструменты доступа к базам данных низкого уровня, например. некоторые вариации простых, простых, простых hash_map get_record( string id_value, string table_name, string id_column_name="id" ) и подобных методов (конечно, понятия и элегантность сильно варьируются в зависимости от используемого языка).

2) длинная версия: в любых проектах с базой данных, где у меня был "концептуальный контроль" вещей, я избегал AR, и это было хорошо. Я обычно создаю многоуровневую архитектуру (вы рано или поздно разделите свое программное обеспечение слоями, по крайней мере, в проектах среднего и крупного размера):

A1) сама база данных, таблицы, отношения, даже некоторая логика, если СУБД это позволяет (MySQL тоже взрослый)

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

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

C) слой прикладных объектов: "объекты приложения" иногда являются простыми строками таблицы в базе данных, но в большинстве случаев они являются составными объектами и имеют некоторую более высокую логику, поэтому время инвестирования в объектах AR на этом уровне просто бесполезно, пустая трата драгоценных кодеров, потому что "реальная ценность", "высшая логика" этих объектов должна быть реализована поверх объектов AR, так или иначе - с AR и без нее! И, например, почему вы хотите иметь абстракцию "Объекты входа в журнал"? Код логики приложения записывает их, но должен ли они обновлять или удалять их? звучит глупо, а App::Log("I am a log message") - некоторые величины, более простые в использовании, чем le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();. Например: использование "объекта записи журнала" в представлении журнала в вашем приложении будет работать для 100, 1000 или даже 10000 строк журнала, но рано или поздно вам придется оптимизировать - и я держал пари в большинстве случаев, вы просто используйте эту небольшую красивую инструкцию SQL SELECT в вашей логике приложения (которая полностью нарушает идею AR..), вместо того, чтобы обернуть этот небольшой оператор в жесткие фиксированные рамки идей AR с большим количеством обертывания кода и скрыть его. Время, затрачиваемое на запись и/или создание AR-кода, могло быть вложено в гораздо более умный интерфейс для чтения списков лог-записей (много, много способов, небо - это предел). Coders должны осмеливаться изобретать новые абстракции, чтобы реализовать свою логику приложений, которые соответствуют предполагаемому приложению, и не глупо повторить использование глупых шаблонов, которые звучат хорошо на первый взгляд!

D) логика приложения - реализует логику взаимодействующих объектов и создает, удаляет и перечисляет (!) объекты логики приложения (НЕТ, эти задачи редко должны быть закреплены в самих объектах логики приложения: ли лист бумаги на ваш стол скажет вам имена и местоположения всех других листов в вашем офисе? забывайте "статические" методы для перечисления объектов, это глупо, плохой компромисс, созданный для того, чтобы человеческий образ мышления вписывался в [не-все-AR- каркасно-подобный-] AR-мышление)

E) пользовательский интерфейс - ну, что я напишу в следующих строках, очень, очень, очень субъективно, но, по моему опыту, проекты, построенные на AR, часто пренебрегали частью пользовательского интерфейса приложения - время было потрачено впустую на создание неясных абстракций. В конце концов, такие приложения потратили много времени на кодировщики и почувствовали себя приложениями из кодеров для кодировщиков, наклоненных внутри и снаружи. Кодеры чувствуют себя хорошо (тяжелая работа наконец-то завершена, все закончено и правильно, в соответствии с концепцией на бумаге...), и клиенты "просто должны узнать, что это должно быть так", потому что это "профессионал".. ОК, извините, я отвлекся; -)

Ну, по общему признанию, это все субъективно, но мой опыт (Ruby on Rails исключен, он может быть другим, и у меня нет нулевого практического опыта с этим подходом).

В платных проектах я часто слышал требование начать с создания некоторых объектов "активной записи" в качестве строительного блока для логики приложения более высокого уровня. По моему опыту, этот явно часто был своего рода оправданием для того, чтобы клиент (в большинстве случаев, компания-разработчик программного обеспечения) не имела хорошей концепции, большого мнения, обзора того, что продукт должен наконец, будет. Эти клиенты думают в жестких рамках ( "в проекте десять лет назад это сработало хорошо" ), они могут создавать объекты, они могут определять отношения сущностей, они могут разрушать отношения данных и определять основную логику приложения, но затем они останавливаются и передайте это вам, и подумайте, что все, что вам нужно... им часто не хватает полной концепции логики приложения, пользовательского интерфейса, удобства использования и т.д. и т.д.... им не хватает большого мнения, и им не хватает любви к детали, и они хотят, чтобы вы следовали этому пути AR, потому что... ну, почему, он работал в этом проекте несколько лет назад, он держит людей занятыми и молчащими? Я не знаю. Но "детали" отделяют мужчин от мальчиков или... как был оригинальный рекламный слоган?; -)

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

Итак, наконец: ЭТО ВСЕ, почему я ненавижу этот глупый "активный шаблон записи", и я делаю и избегаю его, когда это возможно.

EDIT: Я бы даже назвал это No-Pattern. Это не решает никаких проблем (шаблоны не предназначены для создания синтаксического сахара). Это создает множество проблем: корень всех его проблем (упоминается во многих ответах здесь..) заключается в том, что он просто скрывает старый добрый хорошо развитый и мощный SQL за интерфейсом, который по шаблонам определение крайне ограничено.

Этот шаблон заменяет гибкость синтаксическим сахаром!

Подумайте об этом, какая проблема AR разрешает для вас?

Ответ 5

Некоторые сообщения меня путают. Некоторые ответы идут на "ORM" или "SQL" или что-то в этом роде.

Дело в том, что AR - это просто шаблон программирования упрощения, в котором вы используете свои объекты домена для записи там кода доступа к базе данных.

Эти объекты обычно имеют бизнес-атрибуты (свойства bean) и некоторое поведение (методы, которые обычно работают с этими свойствами).

AR просто говорит "добавить некоторые методы к этим объектам домена" к задачам, связанным с базой данных.

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

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

Для меня настоящая проблема касается только проблемы ООП. AR заставляет вас каким-то образом добавить зависимость от вашего объекта к инфраструктуре объектов. Эти объекты инфракрасной области позволяют объекту домена запрашивать базу данных с помощью методов, предложенных AR.

Я всегда говорил, что два уровня - ключ к успеху проекта. Уровень обслуживания (где логика бизнеса находится или может быть экспортирована с помощью какой-либо технологии удаленного доступа, например веб-служб) и уровня домена. На мой взгляд, если мы добавим некоторые зависимости (не очень необходимые) к объектам уровня домена для разрешения шаблона AR, нашим объектам домена будет сложнее поделиться с другими уровнями или (редко) внешними приложениями.

Spring Реализация AR для AR интересна тем, что она не полагается на сам объект, а в некоторых файлах AspectJ. Но если позже вы не хотите работать с Roo и должны реорганизовать проект, методы AR будут реализованы непосредственно в ваших объектах домена.

Другая точка зрения. Представьте, что мы не используем реляционную базу данных для хранения наших объектов. Представьте, что приложение хранит наши объекты домена в базе данных NoSQL или только в файлах XML, например. Будем ли мы реализовывать методы, которые выполняют эти задачи в наших объектах домена? Я так не думаю (например, в случае XM мы добавили связанные с XML зависимости к нашим объектам домена... По-моему, я думаю). Почему же нам нужно реализовать реляционные методы БД в объектах домена, как говорит шаблон Ar?

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

Ответ 6

Вопрос о Active Образец дизайна. Не орм Инструмент.

Оригинальный вопрос помечен рельсами и относится к Twitter, который встроен в Ruby on Rails. Рамка ActiveRecord в Rails представляет собой реализацию шаблона проектирования Fowler Active Record.

Ответ 7

Главное, что я видел в отношении жалоб на Active Record, заключается в том, что когда вы создаете модель вокруг таблицы и вы выбираете несколько экземпляров модели, вы в основном делаете "select * from..".. ". Это прекрасно для редактирования записи или отображения записи, но если вы хотите, например, отобразить список городов для всех контактов в вашей базе данных, вы можете сделать" выбрать Город из..." и получить только города, Для этого с помощью Active Record потребуется выбрать все столбцы, но только с помощью City.

Конечно, различные реализации будут обрабатывать это по-разному. Тем не менее, это одна проблема.

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

Me, я выкалываю активную запись.: -)

НТН

Ответ 8

Мне нравится, как SubSonic делает только один столбец.
Либо

DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)

или:

Query q = DataBaseTable.CreateQuery()
               .WHERE(DataBaseTable.Columns.ColumnToFilterOn,value);
q.SelectList = DataBaseTable.Columns.ColumnYouWant;
q.Load();

Но Linq по-прежнему является королем, когда дело доходит до ленивой загрузки.

Ответ 9

@BlaM: Иногда я делал активную запись для результата соединения. Не всегда должно быть отношение Table ↔ Active Record. Почему бы не "Результат оператора объединения" ↔ Активная запись?

Ответ 10

Я расскажу об Active Record как шаблоне проектирования, я не видел ROR.

Некоторые разработчики ненавидят Active Record, потому что они читают умные книги о написании чистого и аккуратного кода, и в этих книгах указано, что активная запись нарушает принцип единственной resposobility, нарушает правило DDD, что объект домена должен быть неустойчивым, и многие другие правила из этих вид книг.

Второе, что объекты домена в Active Record имеют тенденцию быть 1 к 1 с базой данных, которые могут считаться ограничением в каких-то системах (в основном, n-ярус).

Это просто абстрактные вещи, я не видел рубина на рельсах, фактическое выполнение этого шаблона.

Ответ 11

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

Да, join обычно хуже, чем не присоединяется вообще, когда дело доходит до производительности, но join обычно лучше "fake" присоединяется, сначала прочитав всю таблицу A, а затем используя полученную информацию для чтения и фильтрации таблицы B.

Ответ 12

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

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

Ответ 13

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

Ответ 14

Попробуйте сделать много-много полиморфных отношений. Не так просто. Особенно, если вы не используете STI.