Подтвердить что ты не робот

Уменьшение количества аргументов конструктору

Я читаю "Чистый код" и не могу понять, как сохранить некоторые из моих функций (обычно конструкторов) в их MAXIMUM из 3 параметров.

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

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

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

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

4b9b3361

Ответ 1

У вас может быть

MovablePatch(Renderer* renderer, CircleAppearance circleAppearance)

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

Именованный идентификатор параметров полезен здесь. В вашем случае у вас может быть

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) { }
    ...
};

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

Ответ 5

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

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

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

Обратитесь здесь, чтобы увидеть некоторые примеры.