конструктор против слишком чертовски много получает и устанавливает - PullRequest
3 голосов
/ 20 июля 2011

У меня есть класс записей с 18 свойствами.

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

Поскольку я ООП, еслиработая процедурное веб-приложение, я ходил по этому поводу задом наперед.

Сначала я рассмотрел рабочий процесс для изменения существующих записей.В то время имело смысл добавить все 18 свойств в метод __construct и избегать дерьмовых загрузок сеттеров.Отдельный класс loader обрабатывает бизнес базы данных и может возвращать либо отдельные объекты, либо массив объектов записей.Это все работало нормально.

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

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

не кажется большим улучшением!: - /

Как настоящие программисты справляются с этим?(Я всего лишь хобби-любитель ...)

Ответы [ 5 ]

2 голосов
/ 20 июля 2011

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

С другой стороны, вы можете сделать зацикливание массива:

private $prop1;
private $prop2;
// more properties here.

function __construct( array $props ) // `array` here is for type-hinting.
{
    foreach( array( 'prop1', 'prop2' /*, all of the props for this object */
             as $property )
    {
        // basically, this will assign all of the properties as they exist in the
        // props array
        if( isset( $props[ $property ] ) )
            $this->$property = $props[ $property ];
    }
}

Или, если вы хотите сохранить свою старую подпись конструктора:

function __construct( $prop1, $prop2 = NULL, $prop3 = NULL /* ... */ ) 
{
    if( is_array( $prop1 ) )
    {
         $this->array_prop_assignment( $prop1 );
    }
    else
    {
        $args = func_get_args();
        // this ensures that anything which is passed to the constructor
        // will go to the "new" old constructor
        call_user_func_array( array( $this, 'param_prop_assignment' ), $args );
    }
}

function param_prop_assignment( $prop1, $prop2 /* ... */ )
{
    //your old constructor can go here.
}

function array_prop_assignment( array $props )
{
    // foreach example above would go here.
}

Новая версия также дает вам возможность просто:

$k = new DataClass(); // no idea what the real class name is.
$k->param_prop_assignment( 1, 2, 3 /* ... */ );
1 голос
/ 20 июля 2011

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

Вот почему я обычно предпочитаю проводить занятия с геттерами и сеттерами. Да, это больше печатать, но с помощью getter и setters пользователи могут легко увидеть, какие свойства они могут получить и установить, плюс getter и setters удобны для автозаполнения IDE.

Теперь, следующая проблема: вы не хотите постоянно вызывать сеттеры, когда читаете существующую запись из базы данных? Вы сказали, что у вас класс Loader, вы не можете просто централизовать все вызовы сеттеров в классе Loder?

class Loader{
  public function getMyObject(){
    $dbData = $this->getDataFromDB();
    $myObj = $this->createMyObjectFromDbData($dbData);  
    return $myObj;
  }

  private createMyObjectFromDbData($dbData){
    $myObj = new MyObject();
    /* 18 setter calls */

    return $myObj;
  }
}

Поэтому, когда вы хотите работать с существующим, вы можете просто позвонить Loader.getMyObject();

Если вам не хочется набирать все 18 вызовов сеттера в createMyObjectFromDbData, тогда, пока ваш сеттер следует некоторому соглашению по именованию, вы можете сделать

for ($dbData as $columnName => $columnValue){
  $propertyName = $columnName;
  $propertyValue = $columnValue;
  $myObj->set{$columnName}($propertyValue); /* you can't do this in java */
}

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

1 голос
/ 20 июля 2011

Вы можете приковать их к конструктору.Где вы это делаете ...

$Record = Record()->Name('Mark')->Location('A-Town, NY')->Phone('123-345-6789');

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

function Record() {
    return new Record;
}

class Record {
    private $Name;
    private $Location;
    private $Phone;

    public function __get($property) {
        return (isset($this->$property)) ? $this->$property : FALSE;
    }

    public function &__call($property, $arguments)
    {
        if (property_exists($this, $property))
            $this->$property = array_shift($arguments);
        return $this;
    }
}

$FilledRecord = Record()->Name('Mark')->Location('A-Town')->Phone('123-456-7890');
$EmptyRecord = Record();

print_r($FilledRecord);
print_r($EmptyRecord);

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

0 голосов
/ 20 июля 2011

Если у вас много свойств для объекта и вы не хотите включать их все в конструктор, вы можете использовать сеттеры, возвращающие сам объект, чтобы сделать немного более читабельный код:

MyClass obj = (new MyClass())
    .setName(name)
    .setLocation(location)
    ...
    .setPhone(phone)

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

MyClass obj = (new MyClassFactory())
    .setName(name)
    .setLocation(location)
    ...
    .setPhone(phone)
    .construct();
0 голосов
/ 20 июля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...