CRUD и OOD.Как подойти к этому? - PullRequest
5 голосов
/ 11 января 2012

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

Итак, я переписываю небольшое веб-приложение, которое я недавно сделал.Причина этого в том, что код стал довольно грязным, и я хочу изучить и применить лучший дизайн ОО.Это приложение должно делать просто CRUD.У меня есть база данных с 3 таблицами, companies и partners, которые не имеют отношения друг к другу, и city, которая имеет отношение 1: n к компаниям и партнерам.Очень просто, правда.Теперь у меня есть несколько вопросов, которые я изложу в конце своего поста.Здесь я просто попытаюсь объяснить:

Мой первый подход состоял в том, что я создал классы company, partner и city, извлек все наборы данных из базы данных и создал объекты из этого:

class company {

    private $id   = null;
    private $name = null;
    private $city = null;

    //many more attributes

    function __construct( $id, $name, $city, [...] ) {

        $this->id   = $id;
        $this->name = $name;
        $this->city = $city;

        //huge constructor
    }

   /*
    *  getters + setters here
    *
    *  no need to paste the partner class as it looks just like this one
    *
    */
}

И это все, что сделали эти классы.Я выбрал каждый набор данных из базы данных и сконструировал объекты компании, партнера и города (город атрибутов в этих классах сам является объектом с несколькими атрибутами) и сохранил их в два массива arr_companies и arr_partners, которые затем содержали эти объекты.... и это работало нормально, как это.

Теперь мне нужно обновить, вставить, удалить в базу данных, и все 3 класса (город, компания, партнер) нуждаются в этой функции.Мой подход состоял в том, что я создал новый класс с конструктором, который будет в основном принимать команду и объект из двух строк, например ('update', 'company'), и затем обновлять компанию непосредственно в базе данных, оставляя мои объекты нетронутыми.Это очень огорчило меня, потому что у меня были такие хорошо сконструированные объекты, и я не знал, как их использовать.

Вопросы:

  • Это плохо дляесть такие огромные конструкторы (мой самый большой из них будет принимать 28 параметров)?

  • Если у вас есть отдельный класс для операций с базой данных или лучше иметь абстрактный класс или интерфейс для него и позволить подклассам самим обрабатывать обновление, удаление, вставку?

  • Распространено ли просто писать, удалять из базы данных всякий раз, когда я должен или просто применить эти изменения к моим объектам, и выполнять команды к базе данных только позже, например, когда сеанс заканчивается?

  • Я полагаю, что подобное заявление должно было быть сделано фантастическим разом раньше.Какой правильный подход здесь?создавать объекты, работать с объектами, сохранять их в базе данных?

  • У меня так много вопросов, но я думаю, что многие из них я просто не знаю, как задать.

Обратите внимание, что, если возможно, я бы не хотел использовать ORM на этом этапе.

Большое спасибо за ваше время.

Ответы [ 6 ]

6 голосов
/ 12 января 2012

Вопросы, поставленные в ОП:

«Плохо ли иметь такие огромные конструкторы (мой самый большой из них будет принимать 28 параметров)»?

  • Да.Представьте себе код вызова.Вам нужно будет передать 28 различных значений, не говоря уже о том, что каждый вызов должен соответствовать точному порядку, указанному в конструкторе.Если один параметр был неуместен, вы могли бы нанести ущерб с помощью алгоритмов, зависящих от параметров.Если вам действительно необходимо передать большое количество параметров, я бы порекомендовал передать их в виде массива (отправил пример на другой вопрос SO).

"Если у вас естьотдельный класс для операций с базой данных или лучше иметь абстрактный класс или интерфейс для него и позволить подклассам самим обрабатывать обновление, удаление, вставку? "

  • Вообще говоря, при созданииклассы, вы хотите попытаться определить существительные, которые лучше всего соответствуют потребностям вашего бизнеса.В вашем конкретном случае у вас будет три класса;Компания, Партнер и Город.

  • Теперь внутри каждого класса (существительного) ваши методы будут представлены в форме глаголов, поэтому в качестве кода вашего вызова имеет смысл: if ($company->getName() === 'forbes')

  • Как вы упомянули, каждому классу нужен объект базы данных (dbo) для работы, чтобы вы могли реализовать любое количество шаблонов для предоставления соединений с наборами данных вашим классам;singleton, singleton с фабрикой или внедрение зависимостей и т. д.

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

  • Интерфейсы являются полезным инструментом в определенных ситуациях, но я считаю, что они менее гибкие, чем декларируемыеабстрактные методы в родительском классе.Но хороши в ситуациях, когда классы не имеют общего родителя.

"Обычно ли просто записывать, удалять из базы данных всякий раз, или я просто должен применить эти изменения к своим объектам ивыполнять команды в базе данных только позже, например, когда сеанс заканчивается "?

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

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

«Я полагаю, что подобное приложение должно было быть сделано фантастически много раз. Какой подход здесь нужен? Создавать объекты, работать с объектами, сохранять их в базе данных»?

  • Вы правы, это было сделано раньше, и их обычно называют ORM (сопоставителями объектных отношений).По сути, ORM проанализирует вашу схему базы данных и создаст объекты (и отношения), которые представляют вашу схему.Таким образом, вместо работы с нативным SQL, вы работаете с объектами.Хотя вы можете использовать SQL для пользовательских бизнес-потребностей, но в случае с Doctrine вы бы использовали язык запросов Doctrine (DQL) вместо собственного SQL.

  • ORM, который я настоятельно рекомендую, это Doctrine .

Если вы не хотите использовать ORM, вы можете добавить методы CRUD в свои основные классы.Я выбрал интерфейс, поэтому ваши классы не должны расширяться от родительского элемента, состоящего из операций с базой данных.Кроме того, прочитайте этот пост об использовании синглтона / фабрики для демонстрации ваших объектов базы данных классов.

Примите во внимание следующее:

// Company.php
class Company implements iDatabaseOperation

    public function delete()
    {
        // Lets use a DBO singleton/factory for DB access
        //   Uses PDO, which is strongly recommended
        $dbo = Database::factory(Database::DATABASE_NAME);

        $dbo->beginTransaction();

        try {

            $sql = 
                "DELETE FROM " .
                "    company " .
                "WHERE " .
                "    id = :companyId " .
                "LIMIT 1";

            $stmt = $dbo->prepare($sql);

            $stmt->bindValue(':companyId', $this->getId());

            $stmt->execute();

            $dbo->commit();

        } catch (Exception $e) {

            $dbo->rollback();

            error_log($e->getMessage();

            $e = null; // Php's garbage collection sucks
        }
    }
}

// iDatabaseOperation.php
interface iDatabaseOperation
{
    public function delete();
    public function update();
    public function insert();
}
1 голос
/ 12 января 2012
  1. Это действительно плохо.Код в этом случае нечитаем.У вас есть опции
    • для использования сеттеров (можно добавить логику проверки внутри, лучшую читаемость, нет необходимости заполнять пустые поля нулем)
    • , чтобы иметь отдельный конструктор классов для каждого класса домена (занимает немного памятидля дополнительного объекта).Пример в java Надеюсь, вы понимаете:
      class CompanyBuilder {
      private final Company c;
      public CompanyBuilder() {
       c = new Company();<br>
      }
      CompanyBuilder addId(String id){c.id = id;}
      // id should be package visible and class should be located in the same package with builder
      CompanyBuilder addName(String name){...}
      CompanyBuilder addCity(String city){...}
      Company build(){ return c;}
      }
      
    • гибридное решение, чтобы иметь методы для организации цепочки (хуже отладка, лучшая читаемость).В java будут методы:
      class Company {
      ...
      Company addId(String id){
      this.id = id;
      return this;<br>
      }
      Company addName(String name){...}
      ...
      }
      Usage: 
      Company c = new Company().addId("1").addName("Name1");
      
    • , возможно, вы можете создать более гранулированные объекты, чтобы позже использовать их и добавить определенную логику в правильном месте.Например, это может быть объект Address (Location) для компании.
  2. Следуйте принципу единой ответственности. Твердое описание на вики .Это помогает изменить специфичный для базы данных код, не затрагивая другую часть системы в вашем случае.Ну, отдельный домен и конкретный код базы данных, имеют общий интерфейс или абстрактный класс (если у вас есть общая логика для всех классов домена - принцип Лискова).В подклассах реализуют доменную часть.
  3. Если вы не хотите терять данные, вы должны сохранять их каждый раз, иметь кластер серверов или распределенный кеш.Если все в порядке, сохраните их в конце сеанса как пакет.Это увеличит вашу производительность.Также вы должны сохранять транзакции каждый раз, когда у вас есть параллельные обновления.
  4. Подход - это получать данные из базы данных / создавать объекты из этих данных или новые объекты / рабочие (обновлять) объекты / записывать данные из объектов в базу данных
  5. просто напишите больше кода и прочитайте stackoverflow

Наконец, я предлагаю прочитать «Чистый код: руководство по гибкому программному мастерству» Р. Мартин.

1 голос
/ 12 января 2012

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

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

Ниже приведены конкретные рекомендации:

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

Простая вещь, которую нужно сделать, это использовать статические методы класса сущностей для загрузки и сохранения данных. Э.Г.

class city {

    private $id   = null;
    private $name = null;

    function __construct( $id, $name ) {
        $this->id   = $id;
        $this->name = $name;
    }

    // getters and setters
    ...

    // ---------------------
    // static functions
    // ---------------------

    public static function loadById($cityId) {
        // pull up the city by id
        $retval = new city(row["id"], row["name"]);
        // close db connection
        return $retval;
    }

    public static function loadByCustomerId($customerId) {
        // pull up multiple cities by customer id
        // loop through each row and make a new city object
        // return a hash or array of cities
    }

    public static function update($city) {
        // generate your update statement with $city->values
    }

    // other methods for inserting and deleting cities
    ...
}

Так что теперь код для получения и обновления городов будет выглядеть примерно так:

// loading city data
$city = city::loadById(1); // returns a city instance
$cities = city::loadByCustomerId(1); // returns an array of city instances

// updating city data
$city->name = "Chicago"; // was "chicago"
city::update($city); // saves the change we made to $city

Статические методы - не лучший способ реализовать это, но он направляет вас в правильном направлении. Шаблон хранилища был бы лучше, но это выходит за рамки этого одного ответа. Я нахожу, что часто я не вижу достоинств в более сложном решении, таком как шаблон хранилища, пока не столкнусь с проблемами с более простыми решениями.

0 голосов
/ 12 января 2012

Возможно, вы захотите посмотреть на ruby ​​на рельсах .

Вам не обязательно переключаться на него, но посмотрите, как они реализуют шаблон MVC и достигают CRUD.

0 голосов
/ 11 января 2012

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

Я думаю, вам также следует создать класс, управляющий операциями и базой данных, например, DBHandler.с помощью delete, update и т. д. По моему мнению, важны ли изменения в кортежах в вашей базе данных непосредственно после вызова функций.

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

0 голосов
/ 11 января 2012

То, что вы делаете, выглядит великолепно. Что вы можете добавить, так это промежуточный слой, который отображает ваш бизнес-объект в вашу базу данных (сопоставление отношений объекта). Существует множество API реляционных карт объектов. Проверьте этот список википедии на те, которые вы можете использовать для PHP

...