2011-12-29 2 views
41

Я читаю «Чистый код» и не могу понять, как сохранить некоторые из моих функций (обычно конструкторов) в их МАКСИМАЛЬНО из 3 параметров.Уменьшение количества аргументов конструктору

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

В качестве примера у меня есть класс «MovablePatch». Он позволяет пользователю перетаскивать квадрат в окне. Он нуждается в нескольких параметрах, включая Radius, Color, Renderer, InitialPosition и Visibility. В настоящее время я собираю все это из моего GUI, а затем вызвать:

MovablePatch(int radius, Renderer* renderer, Color color, Position initial, bool visibility) 

Это лишь некоторые из вещей, которые мне нужно в этом классе. Может ли кто-нибудь предположить, как еще я могу упаковать эту информацию, чтобы перейти к конструктору? Я не вижу никаких очевидных «разбить его на более мелкие классы».

+7

Посмотрите шаблон строителя. – Bashwork

+1

Вам нужно следовать этой линии? В противном случае я бы довольно сильно сказал, игнорируя его, где не очевидно, как его применять. Имо, если изменения в соответствии с рекомендациями не являются интуитивными, обычно не стоит реорганизовывать код, чтобы гарантировать, что руководство соблюдено, несмотря ни на что. Для решения unintuitve, которое может легко привести к более сложному (и, следовательно, к ошибке) коду, который даже не имеет лучшей читаемости (примером для этого является упомянутый небольшой конструктор, за которым следуют вызовы функций мутатора или шаблон компоновщика, что упрощает забудьте установить некоторое значение). – Grizzly

+6

Использование простой 'struct' для связанных атрибутов, которые должны быть переданы, поскольку один параметр был выполнен, так как до того, как я родился, я уверен. – AJG85

ответ

31

Вы могли бы

MovablePatch(Renderer* renderer, CircleAppearance circleAppearance) 

где CircleAppearance собирает другую информацию.

Однако чистый код и другие книги, которые обобщают, как выглядит хороший код, нацелены на 80 процентов кода. Ваш код кажется «ближе к металлу», чем типичный вариант LoB (Line of Business). Таким образом, вы можете столкнуться с местами, где определенные идеалы кодирования неприменимы.

Важнейшая часть состоит в том, что вы думаете об этом и пытаетесь сохранить все хорошо и аккуратно! :)

20

Некоторые из вещей, которые вы проходите, могут быть абстрагированы в большую конструкцию. Например, visibility, color и radius могут иметь смысл быть помещенными в объект, который вы определяете. Затем экземпляр этого класса, вызывающий его ColoredCircle, может быть передан в конструктор MovablePatch. ColoredCircle не заботится о том, где он находится, или о том, что он делает, но движок MovablePatch.

Мое главное, что с точки зрения ОО radius на самом деле не является целым числом, это радиус. Вы хотите избежать этих длинных списков конструкторов, потому что сложно понять контекст этих вещей. Если вы соберете их в более крупный класс, вроде как у вас уже есть Color и Position, у вас может быть меньше параметров, и вы сможете их легко понять.

+2

Разве это не только проблема? Вы также должны инициализировать класс «ColoredCircle», и осталось еще четыре параметра ... – MartinStettner

+2

@MartinStettner, да, сначала вы должны инициализировать «ColoredCircle», но это должно быть четыре параметра. Это означает, что «MovablePatch» будет принимать только «ColoredCircle» и «Renderer *». –

+1

Должно ли 'Position' хранить в патче или в круге? Я думаю, что патч имеет положение, а не круг. –

8

Одним из хороших вариантов является использование шаблона Builder, где каждый метод «setter» возвращает собственный экземпляр, и вы можете связать методы по мере необходимости.

В вашем случае вы получите новый класс MovablePatchBuilder.

Этот подход очень полезен, и вы можете найти его во многих различных фреймворках и языках.

См. Примеры here.

12

Полезно здесь Named Parameter Idiom.В вашем случае, вы можете иметь

class PatchBuilder 
{ 
public: 
    PatchBuilder() { } 
    PatchBuilder& radius(int r) { _radius = r; return *this; } 
    PatchBuilder& renderer(Renderer* r) { _renderer = r; return *this; } 
    PatchBuilder& color(const Color& c) { _color = c; return *this; } 
    PatchBuilder& initial(const Position& p) { _position = p; return *this; } 
    PatchBuilder& visibility(bool v) { _visibility = v; return *this; } 

private: 
    friend class MovablePatch; 
    int _radius; 
    Renderer* _renderer; 
    Color _color; 
    Position _position; 
    bool _visibility; 
}; 

class MovablePatch 
{ 
public: 
    MovablePatch(const PatchBuilder& b) : 
     _radius(b._radius); 
     _renderer(b._renderer); 
     _color(b._color); 
     _position(b._position); 
     _visibility(b._visibility); 
    { 

    } 

private: 
    int _radius; 
    Renderer* _renderer; 
    Color _color; 
    Position _position; 
    bool _visibility; 
}; 

затем использовать его как так

int 
main() 
{ 
    MovablePatch foo = PatchBuilder(). 
     radius(1.3). 
     renderer(asdf). 
     color(asdf). 
     position(asdf). 
     visibility(true) 
    ; 
} 

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

class PatchBuilder 
{ 
public: 
    PatchBuilder(const Foo& required) : _foo(required) { } 
    ... 
}; 

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

+1

Я взял на себя смелость добавить выражения 'return * this' в методы' PatchBuilder';) ... – MartinStettner

+11

Эта новая свободная фиксация строителя опасна. Вам нужно иметь проверку времени выполнения, чтобы узнать, все ли заполнено. Я бы не рекомендовал этого. Ненужная сложность, которая будет стоить для поддержания. –

+1

@AdamDymitruk Я обновил ответ с некоторыми формулировками на основе вашего предложения. –

28

Не принимайте максимы как «у вас не более трех параметров в ваших конструкторах» по номиналу. Если у вас есть хоть малейшая вероятность сделать объект неизменным, сделайте это; и если это неизменное означает, что у него будет конструктор с 50 параметрами, пусть будет так; Действуй; даже не думайте об этом дважды.

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

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

+7

+1 для этого. Невосприимчивость намного более выгодна, чем списки малых параметров. – vocaro

Смежные вопросы