Active Record против Data Mapper-а для сохранения данных

от автора

Эти 2 шаблона проектирования описаны в книге Мартина Фаулера «Шаблоны корпоративных приложений» и представляют собой способы работы с сохранением данных в объектно-ориентированном программировании.

Пример шаблона Active Record

class Foo {     protected $db;     public $id;     public $bar;           public function __construct(PDO $db)     {         $this->db = $db;     }       public function do_something()     {         $this->bar .= uniqid();     }       public function save()     {         if ($this->id) {             $sql = "UPDATE foo SET bar = :bar WHERE id = :id";             $statement = $this->db->prepare($sql);             $statement->bindParam("bar", $this->bar);             $statement->bindParam("id", $this->id);             $statement->execute();         }         else {             $sql = "INSERT INTO foo (bar) VALUES (:bar)";             $statement = $this->db->prepare($sql);             $statement->bindParam("bar", $this->bar);             $statement->execute();             $this->id = $this->db->lastInsertId();         }     } }   //Insert $foo = new Foo($db); $foo->bar = 'baz'; $foo->save(); 

В этом упрощенном примере, дескриптор базы данных вводится в конструкторе Foo (Использование инъекции зависимостей здесь позволяет тестировать объект без использования реальной базы данных), и Foo использует его, чтобы сохранять свои данные. Do_something — просто метод-заглушка, заменяющий бизнес логику.

Преимущества Active Record

  • Писать код с Active Record получается быстро и легко, в том случае, когда свойства объекта прямо соотносятся с колонками в базе данных.
  • Сохранение происходит в одном месте, что позволяет легко изучить, как это работает.

Недостатки Active Record

  • Модели Active Record нарушаю принципы SOLID. В частности, принцип единой ответственности (SRP — «S» в принципах SOLID). Согласно принципу, доменный объект должен иметь только одну зону ответственности, то есть только свою бизнес-логику. Вызывая его для сохранения данных, вы добавляете ему дополнительную зону ответственности, увеличивая сложность объекта, что усложняет его поддержку и тестирование.
  • Реализации сохранения данных тесно связана с бизнес-логикой, а это означает, что если вы позже захотите использовать другую абстракцию для сохранения данных (например для хранения данных в XML-файле, а не в базе данных), то вам придется делать рефакторинг кода.

Пример Data Mapper-а

class Foo {     public $id;     public $bar;       public function do_something()     {         $this->bar .= uniqid();     } }   class FooMapper {     protected $db;       public function __construct(PDO $db)     {         $this->db = $db;     }     public function saveFoo(Foo &$foo)     {         if ($foo->id) {             $sql = "UPDATE foo SET bar = :bar WHERE id = :id";             $statement = $this->db->prepare($sql);             $statement->bindParam("bar", $foo->bar);             $statement->bindParam("id", $foo->id);             $statement->execute();         }         else {             $sql = "INSERT INTO foo (bar) VALUES (:bar)";             $statement = $this->db->prepare($sql);             $statement->bindParam("bar", $foo->bar);             $statement->execute();             $foo->id = $this->db->lastInsertId();         }     } }   //Insert $foo = new Foo(); $foo->bar = 'baz'; $mapper = new FooMapper($db); $mapper->saveFoo($foo); 

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

Преимущества Data Mapper-а

  • Каждый объект имеет свою зону ответственности, тем самым следую принципам SOLID и сохраняя каждый объект простым и по существу.
  • Бизнес-логика и сохранение данных связаны слабо, и если вы хотите сохранять данные в XML-файл или какой-нибудь другой формат, вы можете просто написать новый Mapper, не притрагиваясь к доменному объекту.

Недостатки Data Mapper-а

  • Вам придется гораздо больше думать, перед тем как написать код.
  • В итоге у вас больше объектов в управлении, что немного усложняет код и его отладку.

Сервис-объекты

При использовании шаблона проектирования Data Mapper, вызывающий код должен выбрать Mapper и бизнес-объект и связать их вместе. Если это код вызова в контроллере, то в конечном счете ваша модель «утекает» в контроллер, что может вызвать большие проблемы при поддержке и юнит-тестировании. Эта проблема может быть решена путем введения объекта-сервиса. Сервис является воротами между контроллером и моделью и связывает доменный объект с Mapper-ом по мере необходимости.

Следует помнить, что M в MVC, представляет собой слой абстракции модели, а не объект модели. Так может быть несколько типов объектов в одной модели (в приведенном выше примере, у вас может быть объект сервиса, доменный объект и объект Mapper-а, выступающие в роли единой модели). С другой стороны, если вы используете модели Active Record, ваша модель может быть представлена лишь одним объектом.

Варианты использования

Объекты Active Record исторически были очень популярны из-за того, что они проще, легче в понимании и быстрее в написании, поэтому многие фреймворки и ORM используют Active Record по умолчанию.

Если вы уверены, что вам никогда не понадобиться менять слой сохранения данных (если вы имеете дело с объектом, который представляет из себя INI-файл, например), или вы имеете дело с очень простыми объектами, в которых не так много бизнес-логики, или просто предпочитаете держать все в небольшом количестве классов, тогда шаблон Active Record это то, что вам нужно.

Использование Data Mapper-а хотя и ведет к более чистому, простому в тестировании и поддержке коду, и обеспечивает большую гибкость, — цена этому, — повышение сложности. Если вы еще не пробовали его использовать, то дайте ему шанс, — вам должно понравиться.

Это перевод статьи Рассела Волкера.

ссылка на оригинал статьи http://habrahabr.ru/post/198450/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *