Які недоліки у шаблоні ActiveRecord?


30

Мені цікаво, які недоліки використовується у шаблоні ActiveRecord для доступу до даних та бізнес-об’єктів. Єдине, про що я можу придумати верхній частині голови, - це те, що він порушує Принцип єдиної відповідальності, але модель АР досить поширена, що ця причина сама по собі не здається «достатньо хорошою», щоб виправдати її використання (звичайно, моя подання може бути перекошеним, оскільки часто жоден з кодів, з якими я працюю, не відповідає жодному із принципів SOLID).

Особисто я не прихильник ActiveRecord (за винятком написання програми Ruby on Rails, де AR відчуває себе "природно"), тому що відчуває, що клас робить занадто багато, і доступ до даних не повинен бути відповідним самому класу справлятися. Я вважаю за краще використовувати сховища, які повертають бізнес-об’єкти. Більшість кодів, з якими я працюю, прагнуть використовувати варіацію ActiveRecord у вигляді (я не знаю, чому метод є булевим):

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

або іноді більш "традиційний" спосіб створення public static Foo FindFooByID(int fooID)методу для шукачів і чогось у порядку public void Save()збереження / оновлення.

Я розумію, що ActiveRecord, як правило, набагато простіший у застосуванні та використанні, але це здається занадто простим для складних додатків, і ви могли б мати більш надійну архітектуру, інкапсулювавши свою логіку доступу до даних у сховищі (не кажучи вже про те, що простіше заміняти стратегії доступу до даних, наприклад, можливо, ви використовуєте Stored Procs + DataSets і хочете перейти на LINQ або щось подібне)

Отже, які ще є недоліки цієї моделі, які слід враховувати, вирішуючи, чи найкращим кандидатом на роботу є ActiveRecord?

Відповіді:


28

Головний недолік - ваші «сутності» усвідомлюють власну наполегливість, що призводить до багатьох інших поганих дизайнерських рішень.

Інші проблеми полягають у тому, що найактивніші набори інструментів запису в основному відображають 1 - 1 на поля таблиці з нульовими шарами непрямості. Це працює в невеликих масштабах, але розпадається, коли у вас виникають складніші проблеми.


Що ж, те, що ваші об’єкти знають про їх стійкість, означає, що вам потрібно робити такі речі:

  • легко мати підключення до бази даних скрізь. Зазвичай це призводить до неприємного жорсткого кодування або якогось статичного з'єднання, яке потрапляє звідусіль.
  • ваші об'єкти мають вигляд більше схожих на SQL, ніж об’єкти.
  • важко зробити що-небудь у програмі відключене, тому що база даних настільки вроджена.

Зрештою, це цілий ряд інших поганих рішень.


2
чи можете ви детальніше розглянути «інші погані дизайнерські рішення»?
Кевін Клайн

2
Спасибі. Я не вважав, що ці проблеми є проблемою в розвитку Ruby on Rails. Ще можна перевірити поведінку та наполегливість окремо. ІМО, що відокремлює стійкість від поведінки, має мало практичного значення.
Кевін Клайн

@kevin: ці речі є меншим недоліком із рубіновими функціями, такими як міксин та набір качок. Зі статичними мовами - на зразок C #, що використовується ОП у своєму питанні, - розділити їх трохи складніше.
Wyatt Barnett

@Wayne: для мене класи - це лише поле для введення методів - я можу відокремити бізнес-логіку від стійкості, помістивши їх в окремі класи, або я можу просто розділити їх концептуально, переконавшись, що бізнес-методи не роблять я / О. У мовах з поганою підтримкою делегації (наприклад, Java) це економить величезну кількість коду. ОТО, я просто впадаю в сплячку, щоб я могла бути абсолютно помиляюсь.
Кевін Клайн

4
Я додав би дві речі; 1. З'єднання з механізмом збереження ускладнює код, якщо не неможливо, тестування належним чином. 2. Active Record приклеює ваш код до відносин за допомогою централізованого механізму стійкості, що робить його дуже важко розділити ваш моноліт, якщо ви коли-небудь вирішите.
istpaniukuk

15

Найбільшим недоліком активного запису є те, що ваш домен зазвичай щільно поєднується з певним механізмом збереження. Якщо цей механізм потребуватиме глобальних змін, можливо, від файлової бази до постійної бази даних, або між рамками доступу до даних, КОЖЕН клас, який реалізує цю модель, може змінитися. Залежно від мови, рамки та дизайну, навіть щось таке просте, як зміна місця розташування БД або кому належить ", може знадобитися перегляд кожного об'єкта для оновлення методів доступу до даних (це рідкість у більшості мов, які забезпечують простий доступ для налаштування файлів з рядками з'єднання).

Це також вимагає від вас повторення. Більшість механізмів стійкості мають багато спільного коду, щоб підключитися до БД та розпочати транзакцію. DRY (не повторюй себе) сказав би вам як кодер для централізації такої логіки.

Це також робить атомні операції складними. Якщо група об'єктів повинна бути збережена повністю або нічого (наприклад, рахунок-фактура та її рахунки-фактури та / або записи клієнта та / або GL), будь-який об’єкт повинен знати про всі ці інші об'єкти і контролювати їх стійкість ( який розтягує сферу керуючого об'єкта; великі взаємопов'язані записи можуть легко стати "об'єктами бога", які знають все про свої залежності), або контроль над усією транзакцією повинен здійснюватися за межами домену (і в цьому випадку для чого ви використовуєте AR?)

Це також "неправильно" з об'єктно-орієнтованої точки зору. У реальному світі рахунок-фактура не знає, як себе подати, тому чому об’єкт коду рахунку-фактури міг знати, як зберегти себе в БД? Звичайно, надмірно релігійна прихильність до "об'єктів повинна лише моделювати те, що можуть зробити їх колеги в реальному світі" призведе до анемічних моделей домену (рахунок-фактура також не знає, як обчислити власний загальний обсяг, але розділивши обчислення цієї сукупності на інший об’єкт взагалі вважається поганою ідеєю).


У Ruby, де стійкість може бути визначена або абстрагована за дуже простим ключовим словом або командою, це, мабуть, менше проблем. У .NET, який вимагає більше LoC для налаштування різних механізмів збереження, зазвичай зайвим є з'єднання SQL для кожного об'єкта, особливо якщо в одній атомній транзакції необхідно зберегти кілька об'єктів. Підключення до БД виглядає однаково, незалежно від того, зберігаєте Ви рахунок-фактуру чи Клієнта. Ви або абстрагуєте загальні речі до базового класу, спільного для всіх об'єктів ActiveRecord у вашій кодовій базі, або витягніть його до власного класу (репозиторія)
KeithS

чи можете ви навести приклади використання Ruby ActiveRecord, які ілюструють ваші бали? Я не відчував цих проблем, але моя заява була досить невеликою. Я міг записати міграції в Ruby та розгорнути їх для різних реалізацій БД. Я виявив, що ActiveRecord Ruby був дуже корисним для усунення повторень, тому не знаю, чому ви стверджуєте, що використання ActiveRecord матиме протилежний ефект. У мене не було проблем із збереженням: я просто змінив об'єктну модель і дозволив AR оновити БД, щоб відображати об'єктну модель.
кевін клайн

2

Основний недолік полягає в тому, що вона робить вашу модель домену складною, оскільки вона містить не лише ділову логіку, але і постійну інформацію.

Таким чином, рішення полягає у використанні Data Mapper реалізації ORM. Це відокремлює стійкість шару, і ми зараз більше зосереджені на логіці бізнесу юридичної особи. Вчення - це Data Mapper ORM.

Але цей підхід також має певну складність, тому що для запиту ви занадто сильно залежите від даних Mapper, робить запит орієнтованим середовищем. Для його спрощення вводиться ще один шар між моделлю домену та картографією даних , що називається репозиторієм .

Анотація сховища виділяє стійкий шар. Це відчуває об'єктно-орієнтоване програмування в тому сенсі, це колекція всіх об'єктів одного типу (як і всі об'єкти, що зберігаються в таблиці бази даних), і ви можете виконувати операції над ними як операцію збору, додавання , видалення . містить тощо.

Наприклад, для Entity User , буде UserRepository, який представляє колекцію однотипних об’єктів користувача (які зберігаються в таблиці користувачів), над якими можна виконувати операції. Для запиту до таблиці користувачів він використовує User Data Mapper, але це абстрагується до доменної моделі User .

Шаблон репозиторію - це тип рівня доступу до даних , інший - відмінності лише в об'єкті доступу до даних. У сховищі є функція агрегатного кореня.


Шаблони сховища та DAO відрізняються, DAO - це загальний доступ до даних, сховище призначене для постійного збору всіх об'єктів одного типу. Тобто всі сховища повинні мати однаковий інтерфейс (наприклад, масив чи список). Інший король методів не належить до сховища. DAO нижчого рівня, сховище може використовувати DAO. Часто програмісти (особливо PHP) використовують Repository як DAO, але це не правильно.
xmedeko
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.