我正在阅读“清洁代码”,并且无法弄清楚如何将我的一些功能(通常是构造函数)保持为最多3个参数。
我的对象通常需要大量的信息才能工作 - 我是否应该制作一个小的构造函数,然后使用mutator函数为它们提供所有信息?这似乎没有比使用大型构造函数更好。
作为一个例子,我有一个“MovablePatch”类。它允许用户在窗口中拖动方块。它需要几个参数,包括Radius,Color,Renderer,InitialPosition和Visibility。目前,我从我的GUI收集所有这些,然后致电:
MovablePatch(int radius, Renderer* renderer, Color color, Position initial, bool visibility)
这些只是我在这堂课中需要的一些东西。任何人都可以建议我如何打包这些信息传递给构造函数?我没有看到任何明显的“打破小班”这里出现的。
答案 0 :(得分:31)
你可以
MovablePatch(Renderer* renderer, CircleAppearance circleAppearance)
CircleAppearance收集其他信息。
然而,干净的代码和其他关于优秀代码应该是什么样子的书籍,目标是80%的代码。 您的代码似乎比典型的LoB(业务线)品种“更接近金属”。因此,您可能会遇到某些编码理想不适用的地方。
最重要的部分是你正在考虑它并试图让事情变得干净整洁! :)
答案 1 :(得分:29)
不要采取像“你的构造者中不要超过3个参数”这样的格言来表面。如果你有一点机会使一个对象不可变,那就去做吧;如果它是不可变的意味着它将有一个包含50个参数的构造函数,那就这样吧;去吧;甚至不考虑它两次。
即使对象是可变的,仍然应该根据需要传递其构造函数,以便在构造时立即处于有效且有意义的状态。在我的书中,绝对不允许必须知道哪些是必须被调用的神奇的mutator方法(有时甚至是以正确的顺序),然后才能调用任何其他方法,在段错误的惩罚下。
如果您真的想减少构造函数或任何函数的参数数量,只需将此方法传递给它可以调用的接口,以便从中获取所需的东西以便工作
答案 2 :(得分:20)
你传递的一些东西可以被抽象成一个更大的构造。例如,visibility
,color
和radius
可以放入您定义的对象中。然后,可以将此类的实例(称为ColoredCircle
)传递给MovablePatch
的构造函数。 ColoredCircle不关心它在哪里或者它正在使用什么渲染器,但是MovablePatch会这样做。
我的主要观点是,从OO的角度来看,radius
实际上不是一个整数,它是一个半径。您希望避免使用这些长构造函数列表,因为理解这些内容的上下文是令人畏惧的。如果您将它们收集到更大的课程中,就像您已经使用Color
和Position
一样,您可以传入更少的参数并使其更容易理解。
答案 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查看一些示例。