减少构造函数的参数数量

时间:2011-12-29 18:58:53

标签: oop design-patterns language-agnostic

我正在阅读“清洁代码”,并且无法弄清楚如何将我的一些功能(通常是构造函数)保持为最多3个参数。

我的对象通常需要大量的信息才能工作 - 我是否应该制作一个小的构造函数,然后使用mutator函数为它们提供所有信息?这似乎没有比使用大型构造函数更好。

作为一个例子,我有一个“MovablePatch”类。它允许用户在窗口中拖动方块。它需要几个参数,包括Radius,Color,Renderer,InitialPosition和Visibility。目前,我从我的GUI收集所有这些,然后致电:

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

这些只是我在这堂课中需要的一些东西。任何人都可以建议我如何打包这些信息传递给构造函数?我没有看到任何明显的“打破小班”这里出现的。

5 个答案:

答案 0 :(得分:31)

你可以

MovablePatch(Renderer* renderer, CircleAppearance circleAppearance)

CircleAppearance收集其他信息。

然而,干净的代码和其他关于优秀代码应该是什么样子的书籍,目标是80%的代码。 您的代码似乎比典型的LoB(业务线)品种“更接近金属”。因此,您可能会遇到某些编码理想不适用的地方。

最重要的部分是你正在考虑它并试图让事情变得干净整洁! :)

答案 1 :(得分:29)

不要采取像“你的构造者中不要超过3个参数”这样的格言来表面。如果你有一点机会使一个对象不可变,那就去做吧;如果它是不可变的意味着它将有一个包含50个参数的构造函数,那就这样吧;去吧;甚至不考虑它两次。

即使对象是可变的,仍然应该根据需要传递其构造函数,以便在构造时立即处于有效且有意义的状态。在我的书中,绝对不允许必须知道哪些是必须被调用的神奇的mutator方法(有时甚至是以正确的顺序),然​​后才能调用任何其他方法,在段错误的惩罚下。

如果您真的想减少构造函数或任何函数的参数数量,只需将此方法传递给它可以调用的接口,以便从中获取所需的东西以便工作

答案 2 :(得分:20)

你传递的一些东西可以被抽象成一个更大的构造。例如,visibilitycolorradius可以放入您定义的对象中。然后,可以将此类的实例(称为ColoredCircle)传递给MovablePatch的构造函数。 ColoredCircle不关心它在哪里或者它正在使用什么渲染器,但是MovablePatch会这样做。

我的主要观点是,从OO的角度来看,radius实际上不是一个整数,它是一个半径。您希望避免使用这些长构造函数列表,因为理解这些内容的上下文是令人畏惧的。如果您将它们收集到更大的课程中,就像您已经使用ColorPosition一样,您可以传入更少的参数并使其更容易理解。

答案 3 :(得分: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) { }
    ...
};

显然,如果需要所有参数,这种模式会退化为原始问题,在这种情况下,命名参数习惯用法不适用。关键是,这不是一个适合所有解决方案,并且正如亚当在下面的评论中描述的那样,还有额外的成本和一些开销。

答案 4 :(得分:8)

一个不错的选择是使用Builder模式,其中每个“setter”方法返回自己的实例,您可以根据需要链接方法。

在您的情况下,您将获得一个新的 MovablePatchBuilder 类。

这种方法非常有用,您可以在许多不同的框架和语言中找到它。

请参阅here查看一些示例。