Сокращение количества аргументов для конструктора - PullRequest
41 голосов
/ 29 декабря 2011

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

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

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

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

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

Ответы [ 5 ]

31 голосов
/ 29 декабря 2011

Вы могли бы иметь

MovablePatch(Renderer* renderer, CircleAppearance circleAppearance)

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

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

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

29 голосов
/ 29 декабря 2011

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

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

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

20 голосов
/ 29 декабря 2011

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

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

12 голосов
/ 29 декабря 2011

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

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

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

8 голосов
/ 29 декабря 2011

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

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

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

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

...