По мере того, как я все больше и больше узнаю об ООП и начинаю внедрять различные шаблоны проектирования, я продолжаю возвращаться к случаям, когда люди ненавидят Active Record .
Часто люди говорят, что он плохо масштабируется (ссылаясь на Twitter в качестве основного примера), но на самом деле никто не объясняет, почему он плохо масштабируется; и / или как добиться плюсов АР без минусов (по схожей, но другой схеме?)
Надеюсь, это не превратится в священную войну за шаблоны проектирования - все, что я хочу знать, - это, в частности, ****, что не так с Active Record.
Если он плохо масштабируется, почему бы и нет?
Какие еще проблемы у него есть?
Есть ActiveRecord Design Pattern и ActiveRecord библиотека Rails ORM , а также есть тонна подделок для .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")
Это не рекомендуется, так как это некрасиво, но в тех случаях, когда вам просто нужно просто написать простой SQL, это легко сделать.
@ Тим Салливан
... и вы выбираете несколько экземпляров модели, вы в основном делаете "select * from ..."
Код:
people = Person.find(:all, :select=>'name, id')
При этом будут выбраны только столбцы имени и идентификатора из базы данных, все остальные «атрибуты» в сопоставленных объектах будут равны нулю, если вы не перезагрузите этот объект вручную и т. Д.
Главное, что я видел в отношении жалоб на Active Record, - это то, что когда вы создаете модель вокруг стола и выбираете несколько экземпляров модели, вы в основном делаете «select * from ...». Это хорошо для редактирования записи или отображения записи, но если вы хотите, скажем, отобразить список городов для всех контактов в вашей базе данных, вы можете сделать «выбрать город из ...» и получить только города , Для этого с Active Record потребуется выбрать все столбцы, но только с использованием City.
Конечно, различные реализации будут обрабатывать это по-разному. Тем не менее, это одна проблема.
Теперь вы можете обойти это, создав новую модель для конкретной вещи, которую вы пытаетесь сделать, но некоторые люди утверждают, что это больше усилий, чем выгоды.
Я копаю Active Record. :-)
НТН
Проблема, которую я вижу с активными записями, состоит в том, что это всегда только одна таблица. Это нормально, если вы действительно работаете только с одной этой таблицей, но когда вы работаете с данными, в большинстве случаев у вас будет какое-то соединение.
Да, объединение обычно хуже, чем вообще отсутствие объединения, когда речь идет о производительности, но объединение обычно лучше, чем «поддельное» объединение , сначала читая всю таблицу A, а затем используя полученную информацию для чтения и фильтрации таблицы B.
Мне нравится, как SubSonic выполняет только одну колонку.
Либо
DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)
, или:
Query q = DataBaseTable.CreateQuery()
.WHERE(DataBaseTable.Columns.ColumnToFilterOn,value);
q.SelectList = DataBaseTable.Columns.ColumnYouWant;
q.Load();
Но Linq все еще король, когда дело доходит до ленивой загрузки.
@BlaM: Иногда я просто реализовывал активную запись для результата объединения. Не всегда должно быть отношение Table <-> Active Record. Почему бы не "Результат оператора соединения" <-> Active Record?
Проблема с ActiveRecord заключается в том, что запросы, которые он автоматически генерирует для вас, могут вызвать проблемы с производительностью.
В итоге вы выполняете некоторые не интуитивно понятные приемы для оптимизации запросов, которые заставляют вас задуматься о том, не было бы более эффективным время для написания запроса вручную.
Я всегда обнаруживал, что ActiveRecord хорош для быстрых приложений на основе CRUD, где Модель относительно плоская (например, не так много иерархий классов). Однако для приложений со сложной иерархией OO DataMapper , вероятно, является лучшим решением. Хотя ActiveRecord предполагает соотношение 1: 1 между вашими таблицами и объектами данных, такие отношения становятся громоздкими для более сложных доменов. В своей книге о шаблонах Мартин Фаулер отмечает, что ActiveRecord имеет тенденцию ломаться в условиях, когда ваша модель довольно сложна, и предлагает в качестве альтернативы DataMapper .
Я обнаружил, что это правда на практике. В тех случаях, когда у вас много наследования в вашем домене, сложнее сопоставить наследование с вашей RDBMS, чем сопоставить ассоциации или состав.
Я делаю так, чтобы объекты «домена» обращались к вашим контроллерам через эти классы DataMapper (или «сервисный уровень»). Они напрямую не отражают базу данных, но действуют как ваше ОО-представление для какого-либо реального объекта. Предположим, у вас есть класс User в вашем домене, и вам нужно иметь ссылки или коллекции других объектов, уже загруженных при получении этого объекта User. Данные могут поступать из множества разных таблиц, и шаблон ActiveRecord может сделать это действительно сложно.
Вместо прямой загрузки объекта User и доступа к данным с помощью API-интерфейса в стиле ActiveRecord, ваш код контроллера извлекает объект User, вызывая, например, API метода UserMapper.getUser (). Именно этот преобразователь отвечает за загрузку любых связанных объектов из соответствующих таблиц и возврат завершенного объекта «домен» пользователя вызывающей стороне.
По сути, вы просто добавляете еще один уровень абстракции, чтобы сделать код более управляемым. Независимо от того, содержат ли ваши классы DataMapper необработанный пользовательский SQL или вызовы API уровня абстракции данных, или даже сами обращаются к шаблону ActiveRecord, на самом деле не имеет значения для кода контроллера, который получает красивый, заполненный объект User.
Во всяком случае, так я это делаю.
Хотя все остальные комментарии, касающиеся оптимизации SQL, безусловно, верны, моя главная жалоба на шаблон активной записи состоит в том, что это обычно приводит к несоответствию импеданса . Мне нравится поддерживать мой домен в чистоте и надлежащим образом инкапсулировать, что обычно приводит к тому, что активный шаблон записи разрушает всякую надежду.
Вопрос о шаблоне дизайна Active Record. Не обычный инструмент.
Исходный вопрос помечен с помощью rails и относится к Twitter, который построен на Ruby on Rails. Платформа ActiveRecord в Rails является реализацией шаблона проектирования Active Record Фаулера.
Я думаю, что существует множество причин, по которым люди «ненавидят» ActiveRecord и что с ним «не так».
Что касается ненависти, то ядовито все, что связано с Rails. Что касается того, что с ним не так, вполне вероятно, что оно похоже на все технологии, и есть ситуации, когда это хороший выбор, и ситуации, когда есть лучший выбор. Ситуация, когда вы не можете воспользоваться большинством возможностей Rails ActiveRecord, по моему опыту, это когда база данных плохо структурирована. Если вы обращаетесь к данным без первичных ключей, с вещами, которые нарушают первую нормальную форму, где существует множество хранимых процедур, необходимых для доступа к данным, вам лучше использовать что-то большее, чем просто оболочка SQL. Если ваша база данных относительно хорошо структурирована, ActiveRecord позволяет вам воспользоваться этим.
Чтобы добавить к теме ответа комментаторов, которые говорят, что в ActiveRecord сложно, с помощью фрагмента кода
@Sam McAfee Скажем, у вас есть класс User в вашем домене, и вам нужно иметь ссылки или коллекции других объектов, уже загруженных при извлечении этого объекта User. Данные могут поступать из множества разных таблиц, и шаблон ActiveRecord может сделать это действительно сложно.
user = User.find(id, :include => ["posts", "comments"])
first_post = user.posts.first
first_comment = user.comments.first
Используя опцию включения, ActiveRecord позволяет вам переопределить поведение отложенной загрузки по умолчанию.
Попробуйте сделать многим-многим полиморфные отношения. Не так просто. Особенно, когда вы не используете ИППП.
Мой длинный и поздний ответ, даже не полный, но хорошее объяснение, ПОЧЕМУ я ненавижу эту модель, мнения и даже некоторые эмоции:
1) короткая версия: Active Record создает « тонкий слой » « сильной связи » между базой данных и кодом приложения. Что не решает ни логики, ни каких-либо проблем, никаких проблем вообще. ИМХО, он не предоставляет НИКАКОГО ЗНАЧЕНИЯ, за исключением некоторого синтаксического сахара для программиста (который может затем использовать «синтаксис объекта» для доступа к некоторым данным, которые существуют в реляционной базе данных). Усилия по созданию некоторого комфорта для программистов следует (ИМХО ...) лучше вкладывать в низкоуровневые инструменты доступа к базам данных, например, в некоторые вариации простых, простых, простых hash_map get_record( string id_value, string table_name, string id_column_name="id" )
и аналогичных методов (конечно, концепции и элегантность сильно различаются в зависимости от язык используется).
2) длинная версия: в любых проектах, основанных на базе данных, где у меня был «концептуальный контроль» вещей, я избегал AR, и это было хорошо. Я обычно строю многоуровневую архитектуру (вы рано или поздно делите свое программное обеспечение на слои, по крайней мере, в проектах среднего и крупного размера):
A1) сама база данных, таблицы, отношения, даже некоторая логика, если СУБД позволяет это (MySQL также вырос)
A2) очень часто существует нечто большее, чем хранилище данных: файловая система (большие двоичные объекты в базе данных не всегда являются хорошим решением ...), устаревшие системы (представьте себе, «как» к ним будут обращаться, возможны многие варианты ... но это не в этом суть ...)
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-кода, можно было бы потратить на гораздо более умный интерфейс для чтения списков записей в журнале (во многих отношениях, небо - это предел). Кодеры должны осмеливаться придумывать новые абстракции, чтобы реализовать логику своего приложения, которая соответствует предполагаемому приложению, а не глупо повторно реализовывать глупые шаблоныЗвучит хорошо с первого взгляда!
D) логика приложения - реализует логику взаимодействия объектов и создания, удаления и перечисления (!) Объектов логики приложения (НЕТ, эти задачи редко должны быть привязаны к самим объектам логики приложения: говорит ли лист бумаги на вашем столе Вы называете имена и местонахождение всех других листов в вашем офисе? Забудьте "статические" методы для перечисления объектов, это глупо, плохой компромисс, созданный для того, чтобы человеческий образ мышления вписался в [some-not-all-AR-framework-like) -] АР мышление)
E) пользовательский интерфейс - хорошо, то, что я напишу в следующих строках, очень, очень, очень субъективно, но по моему опыту, проекты, основанные на AR, часто пренебрегали частью пользовательского интерфейса приложения - время было потрачено на создание непонятных абстракций , В конце концов, такие приложения тратят много времени на программистов и чувствуют себя как приложения от кодировщиков для программистов, склонных к технологиям внутри и снаружи. Программисты чувствуют себя хорошо (наконец-то проделана тяжелая работа, все закончено и правильно, в соответствии с концепцией на бумаге ...), а клиенты «просто должны понять, что так должно быть», потому что это «профессионал» .. хорошо, извините, я отвлекся ;-)
Ну, по общему признанию, это все субъективно, но это мой опыт (исключая Ruby on Rails, он может быть другим, и у меня нулевой практический опыт с этим подходом).
В платных проектах я часто слышал требование начать с создания некоторых объектов «активной записи» в качестве строительного блока для логики приложения более высокого уровня. По моему опыту, это заметно частоЭто было своего рода оправдание тому, что у клиента (в большинстве случаев компании-разработчика программного обеспечения) не было хорошей концепции, широкого взгляда, общего представления о том, каким должен быть продукт. Эти клиенты думают в жестких рамках («в проекте десять лет назад он работал хорошо ...»), они могут конкретизировать сущности, они могут определять отношения сущностей, они могут нарушать отношения данных и определять базовую логику приложения, но затем они останавливаются и передать его вам, и подумайте, вот и все, что вам нужно ... им часто не хватает полной концепции логики приложения, пользовательского интерфейса, удобства использования и так далее, и так далее ... им не хватает общего представления и им не хватает любви к детали, и они хотят, чтобы вы придерживались этого подхода AR, потому что ... ну, почему, он работал в этом проекте много лет назад, он заставляет людей быть занятыми и молчать? Я не знаю. Но "детали" отделить мужчин от мальчиков, или .. как был оригинальный рекламный слоган? ;-)
После многих лет (десять лет активного развития), когда клиент упоминает «шаблон активной записи», у меня звонит будильник. Я научился пытаться вернуть их к этой основной фазе зачатия , дать им подумать дважды, попытаться показать свои слабости или вообще избежать их, если они не различают (в конце концов, вы знаете, клиент, который еще не осознал этого) Я знаю, чего он хочет, может быть, даже думает, что знает, но не знает, или пытается донести до меня концептуальную работу бесплатно, стоит мне много драгоценных часов, дней, недель и месяцев моего времени, жизнь слишком коротка ...).
Итак, наконец: ЭТО ВСЕ, поэтому я ненавижу этот глупый «шаблон активной записи», и я делаю и буду избегать его, когда это возможно.
РЕДАКТИРОВАТЬ : Я бы даже назвал это No-Pattern. Это не решает никаких проблем (шаблоны не предназначены для создания синтаксического сахара). Это создает много проблем: корень всех его проблем (упомянутых во многих ответах здесь ...) состоит в том, что он просто скрывает старый добрый хорошо разработанный и мощный SQL-интерфейс за интерфейсом, который по определению шаблонов чрезвычайно ограничен.
Этот шаблон заменяет гибкость синтаксическим сахаром!
Подумайте, какую проблему AR решает для вас?
before_save
обратных вызовов для поддержания согласованности в записи. 4) after_commit
Хуки для внешних сервисных триггеров. 5) Хороший DSL для организации изменений DDL в наборы изменений (миграции). (Там все еще есть боль, но отсутствие шаблона хуже, когда> 1 разработчик.)Я собираюсь поговорить об Active Record как о шаблоне проектирования, я не видел ROR.
Некоторые разработчики ненавидят Active Record, потому что они читают умные книги о написании чистого и аккуратного кода, и в этих книгах говорится, что активная запись нарушает принцип единой повторной переносимости, нарушает правило DDD, что объект домена должен быть невежественным, и многие другие правила из книг такого рода. ,
Вторым предметом доменных объектов в Active Record, как правило, является соотношение «1 к 1» с базой данных, что можно рассматривать как ограничение в некоторых типах систем (в основном n-уровневое).
Это просто абстрактные вещи, я не видел реальной реализации этого шаблона.
Некоторые сообщения сбивают меня с толку. Некоторые ответы идут на «ORM» против «SQL» или что-то в этом роде.
Дело в том, что AR - это просто упрощенный шаблон программирования, в котором вы используете свои доменные объекты для написания кода доступа к базе данных.
Эти объекты обычно имеют бизнес-атрибуты (свойства компонента) и некоторое поведение (методы, которые обычно работают с этими свойствами).
AR просто говорит «добавить некоторые методы к этим объектам домена» к задачам, связанным с базой данных.
И я должен сказать, по моему мнению и опыту, что мне не нравится шаблон.
На первый взгляд это может звучать довольно хорошо. Некоторые современные инструменты Java, такие как Spring Roo, используют этот шаблон.
Для меня настоящая проблема только в озабоченности ООП. Шаблон AR заставляет вас каким-то образом добавить зависимость от вашего объекта в объекты инфраструктуры. Эти инфраструктурные объекты позволяют объекту домена запрашивать базу данных с помощью методов, предложенных AR.
Я всегда говорил, что два уровня являются ключом к успеху проекта. Уровень обслуживания (где находится бизнес-логика или который можно экспортировать с помощью некоторой технологии удаленного взаимодействия, например, веб-служб) и уровень домена. По моему мнению, если мы добавим некоторые зависимости (не требующиеся в действительности) к объектам доменного уровня для разрешения шаблона AR, нашим объектам домена будет сложнее поделиться с другими уровнями или (редкими) внешними приложениями.
Реализация AR в Spring Roo интересна тем, что она основана не на самом объекте, а на некоторых файлах AspectJ. Но если позже вы не захотите работать с Roo и вам придется провести рефакторинг проекта, методы AR будут реализованы непосредственно в объектах вашего домена.
Другая точка зрения. Представьте, что мы не используем реляционную базу данных для хранения наших объектов. Представьте, что приложение хранит наши доменные объекты в базе данных NoSQL или, например, только в файлах XML. Будем ли мы реализовывать методы, которые выполняют эти задачи в наших объектах домена? Я так не думаю (например, в случае XM, мы бы добавили зависимости, связанные с XML, к нашим объектам домена ... Я думаю, что это действительно печально). Почему тогда мы должны реализовывать методы реляционных БД в доменных объектах, как говорит шаблон Ar?
Подводя итог, можно сказать, что шаблон AR может звучать проще и удобнее для небольших и простых приложений. Но когда у нас сложные и большие приложения, я думаю, что классическая многоуровневая архитектура - лучший подход.