构造函数应该接受参数还是应该创建setter?

时间:2012-10-10 15:10:03

标签: c++ oop class

我有两个选择。要么创建一个在其构造函数中接受大量参数的类,要么创建许多setter方法和init方法。我不确定哪个是首选选项,是否应该在构造函数中接受一些参数,而其他参数可以通过setter手动设置?还是我过度思考这个?

这是一个相关问题,我也是:Conflicts between member names and constructor argument names

9 个答案:

答案 0 :(得分:26)

如果在创建对象后,您必须调用setinit来实际使用它......好吧,这只是一个糟糕的设计。

如果对象可用而没有按照您希望的方式初始化某些成员,您可以稍后设置它们。

这里的黄金法则是 - 如果您创建一个对象,您应该能够在不进行任何其他初始化的情况下使用它

扩展答案:

假设您的形状有10个边,10个角,颜色和名称,可以连接到不同的形状。构造函数应该如下所示:

 MyShape(Point c1, Point c2,...., Point c10, Color c, Name n)

如您所见,我省略了连接的形状,因为如果当前对象未连接,它可以合理地设置为NULL。但是,在没有任何其他参数的情况下,该对象无效,因此应在构造函数中设置它们。

可能的重载(或者是默认参数)可以是:

 MyShape(Point c1, Point c2,...., Point c10, Color c, Name n, 
                                      MyShape* connectedShape /*=NULL*/)

答案 1 :(得分:4)

您应该为保留class invariant所需的所有成员提供构造函数参数。换句话说,对象从创建到销毁之前应处于有效且一致的状态。其他一切都在呼唤麻烦。

话虽如此,有时会作出让步,例如:在层次结构的情况下,需要调用虚方法以提供特定于类型的初始化。通常,这可以通过使用模板类/方法(即static polymorphism

来避免

如果有类成员不影响类不变量,可以稍后通过setter或其他方法设置它们。

答案 2 :(得分:2)

the builder pattern这里也会帮助我们尝试合并参数,让它们在构建构建器时有意义

答案 3 :(得分:0)

这取决于你在做什么。通常最好在构造函数中设置内容,因为这些有助于塑造对象在其生命周期的后期使用的方式。一旦创建了对象(例如计算因子或文件名),也可能意味着你必须提供重置对象的功能 - 这非常麻烦。

有时会提供一个初始化函数的参数,该函数在构造函数之后调用(当调用纯虚函数会使得很难直接从构造函数初始化时),但是你必须保留对象状态的记录,这会增加复杂性

如果对象是一个直接的无状态数据容器,那么访问器和增变器可能没问题,但它们会增加很多维护开销,并且很少都会被使用。

我倾向于坚持在构造函数中设置您的值,然后添加访问器以及何时允许您只读取您可能需要的参数。

答案 4 :(得分:0)

这取决于您的架构和工具:

如果你计划开发/原型一个大的OO层次结构,如果你没有一个好的IDE /编辑器,我不愿意通过构造函数传递大量信息。在这种情况下,每个重构步骤可能会导致很多工作,这可能会导致错误,而不会被编译器捕获。

如果您计划使用一个集成良好的 set 对象(例如,通过强烈使用设计模式),这些对象不跨越一个大型层次结构,而是具有强大的迭代次数,那么通过构造函数传递更多数据是一件好事,因为更改一个对象构造函数不会破坏所有子构造函数。

答案 5 :(得分:0)

如果需要设置且无法给出默认值,请在构造函数中使其成为必需项。这样你知道它实际上会被设置。

如果不需要该设置并且可以给出默认值,请为其设置一个setter。这使得构造函数更加简单。

e.g。如果您有一个发送电子邮件的类,构造函数中可能需要“收件人”字段,但其他所有内容都可以在setter方法中设置。

答案 6 :(得分:0)

我的经验指出我在构造函数中有参数而不是getter和setter。如果你有很多参数,它建议可选的那些参数可以默认,而必需/强制参数是构造函数参数。

答案 7 :(得分:0)

根据经验,拥有大量构造函数参数是一个做太多的类的标志,所以首先尝试将它拆分为更小的类。

然后尝试将一些参数分组为较小的类或结构,每个类或结构都有自己的,更简单的构造函数。

如果你有合理的默认值,你可以使用一个构造函数,它只为在构造一个新对象时必须给出的值提供参数,然后添加setter,或者使用复制“starter”对象的静态函数,改变在此过程中的一部分。这样,你总是有一致的对象(不变量OK),以及更短的构造函数或函数调用。

答案 8 :(得分:0)

我同意ratchet freaksuggestion构建器模式,除了需要权衡之外,典型的构建器模式不提供编译时检查以确保包含所有参数,你最终可能会得到一个不完整/不正确的对象。

这对我来说是个问题,如果你可以原谅额外的机器,我会编写一个可以为你完成工作的编译时检查版本。 (当然也有优化)

#include <boost/shared_ptr.hpp>

class Thing
{
    public:

        Thing( int arg0, int arg1 )
        {
            std::cout << "Building Thing with   \n";
            std::cout << "    arg0: " << arg0 << "\n";
            std::cout << "    arg1: " << arg1 << "\n";
        }

        template <typename CompleteArgsT>
        static
        Thing BuildThing( CompleteArgsT completeArgs )
        {
            return Thing( completeArgs.getArg0(), 
                          completeArgs.getArg1() );
        }


    public:

        class TheArgs
        {
            public:
                int arg0;
                int arg1;
        };

        class EmptyArgs
        {   
            public:    
                EmptyArgs() : theArgs( new TheArgs ) {};
                boost::shared_ptr<TheArgs> theArgs;    
        };

        template <typename PartialArgsClassT>
        class ArgsData : public PartialArgsClassT
        {
            public:
                typedef ArgsData<PartialArgsClassT> OwnType;

                ArgsData() {}
                ArgsData( const PartialArgsClassT & parent ) : PartialArgsClassT( parent ) {}

                class HasArg0 : public OwnType
                {
                    public:
                        HasArg0( const OwnType & parent ) : OwnType( parent ) {}
                        int getArg0() { return EmptyArgs::theArgs->arg0; }
                };

                class HasArg1 : public OwnType
                {
                    public:
                        HasArg1( const OwnType & parent ) : OwnType( parent ) {}                    
                        int getArg1() { return EmptyArgs::theArgs->arg1; }
                };

                ArgsData<HasArg0>  arg0( int arg0 ) 
                { 
                    ArgsData<HasArg0> data( *this ); 
                    data.theArgs->arg0 = arg0;
                    return data; 
                }

                ArgsData<HasArg1>  arg1( int arg1 )
                { 
                    ArgsData<HasArg1> data( *this ); 
                    data.theArgs->arg1 = arg1;                    
                    return data; 
                }
        };

        typedef ArgsData<EmptyArgs> Args;
};



int main()
{
    Thing thing = Thing::BuildThing( Thing::Args().arg0( 2 ).arg1( 5 ) );
    return 0;
}