减less构造函数的参数个数

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

通常我的对象需要大量的信息才能工作 – 我应该做一个小的构造函数,然后使用mutator函数来给他们所有的信息? 这似乎并不比使用一个大的构造函数更好。

举个例子,我有一个“MovablePatch”类。 它可以让用户在窗口中拖动一个正方形。 它需要几个参数,包括Radius,Color,Renderer,InitialPosition和Visibility。 目前我从我的GUI收集所有这些,然后打电话给:

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

这些只是我在这堂课中需要的一些东西。 任何人都可以build议我怎么可能打包这个信息传递给构造函数? 我没有看到任何明显的“分成小class”出现在这里。

你可以有

 MovablePatch(Renderer* renderer, CircleAppearance circleAppearance) 

CircleAppearance收集其他信息。

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

最重要的部分是你正在考虑它,并试图保持事情的美好和整洁! 🙂

不要采取像“你的build设者中不超过3个参数”这样的准则。 如果你有一丁点的机会让一个物体不变, 如果它是不可变的,意味着它将有一个具有50个参数的构造函数, 去吧; 甚至不要考虑两次。

即使这个对象是可变的,但是你仍然应该传递它的构造函数作为必要的参数,以便在构build时立即处于有效和有意义的状态。 在我的书中,必须知道在调用其他方法之前,必须调用哪些魔术增变器方法(有时甚至是按照正确的顺序)是绝对不允许的。

如前所述,如果您真的想减less构造函数或任何函数的参数数量,只需传递此方法的一个接口,以便从中获取所需的内容即可工作。

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

我的主要观点是,从OO的angular度来看, radius并不是一个整数,而是一个半径。 你想避免这些长的构造函数列表,因为理解这些东西的上下文令人望而生畏。 如果你把它们收集到一个更大的类中,就像你已经拥有的ColorPosition ,你可以减less传入的参数,并使其更容易理解。

命名参数成语在这里很有用。 在你的情况下,你可能有

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

显然,如果所有参数都是必需的,则这种模式退化为原始问题,在这种情况下,命名参数成语不适用。 问题是,这不是一个适合所有情况的解决scheme,正如Adam在下面的评论中所描述的那样,这样做会带来额外的成本和一些开销。

一个好的select是使用Builder模式,其中每个“setter”方法返回自己的实例,并且可以根据需要链接方法。

在你的情况下,你会得到一个新的MovablePatchBuilder类。

这个方法非常有用,你可以在许多不同的框架和语言中find它。

请参阅这里看一些例子。