18

選択肢は 2 つあります。コンストラクターで多くの引数を受け入れるクラスを作成するか、多くのセッター メソッドと init メソッドを作成します。一部の引数はコンストラクターで受け入れられるべきですが、他の引数はセッターを介して手動で設定できますか? それとも私はこれを考えすぎていますか?

これも私による関連する質問です: Conflicts between member names and constructor argument names

4

9 に答える 9

28

オブジェクトを作成した後、それを呼び出す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*/)
于 2012-10-10T15:11:27.003 に答える
4

クラス invariantを維持するために必要なすべてのメンバーにコンストラクター引数を提供する必要があります。つまり、オブジェクトは、作成された瞬間から破棄されるまで、有効で一貫した状態にある必要があります。他のすべてが問題を引き起こしています。

そうは言っても、たとえば、タイプ固有の初期化を提供するために仮想メソッドを呼び出す必要がある階層の場合など、譲歩が行われることがあります。多くの場合、これはテンプレート クラス/メソッド (つまり、静的ポリモーフィズム)を使用することで回避できます。

クラスの不変条件に影響を与えないクラス メンバーがある場合は、後でセッターまたは他のメソッドを介して設定できます。

于 2012-10-10T15:33:38.573 に答える
3

ビルダーパターンは、ここでもパラメーターを合体させて、ビルダーのセットアップ中に意味を持たせるのに役立ちます

于 2012-10-10T19:08:03.167 に答える
1

ラチェットフリークのビルダーパターンの提案に同意しますが、典型的なビルダーパターンでは、すべての引数が含まれていることを保証するコンパイル時のチェックが提供されず、不完全/不正確な結果になる可能性があるというトレードオフがあります。構築されたオブジェクト。

これは私にとって十分な問題だったので、余分な機械を許していただけるなら、コンパイル時のチェックバージョンを作成して、あなたのために仕事をするかもしれません. (確かに最適化も必要です)

#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;
}
于 2013-07-23T20:24:06.963 に答える
0

それはむしろあなたがしていることに依存します。通常、コンストラクターで設定するのが最善です。これらは、オブジェクトがライフサイクルの後半でどのように使用されるかを形作るのに役立ちます。オブジェクトが作成されたら値を変更することもあります(計算係数やファイル名など)。これは、オブジェクトをリセットする機能を提供する必要があることを意味する場合があります。これは非常に面倒です。

コンストラクターの後に呼び出される初期化関数を提供するための引数がある場合があります(純粋な仮想を呼び出すと、コンストラクターから直接初期化するのが難しくなります)が、オブジェクトの状態の記録を保持する必要があり、複雑さが増します。

オブジェクトがストレートのステートレスデータコンテナである場合、アクセサとミューテータは問題ないかもしれませんが、それらは多くのメンテナンスオーバーヘッドを追加し、とにかくすべてが使用されることはめったにありません。

コンストラクターで値を設定してから、必要になる可能性のある引数への読み取り専用アクセスを許可するときにアクセサーを追加することに固執する傾向があります。

于 2012-10-10T15:20:38.940 に答える
0

それはあなたのアーキテクチャとツールに依存します:

大規模なOO階層を開発/プロトタイプ化する予定の場合、優れたIDE /エディターがなければ、コンストラクターを介して多くの情報を渡すことには消極的です。この場合、リファクタリングの各ステップで多くの作業が発生する可能性があり、コンパイラによってキャッチされずにエラーが発生する可能性があります。

1つの大きな階層にまたがらず、強力な反復性を持つ、十分に統合されたオブジェクトのセットを使用する場合(たとえば、デザインパターンを強力に使用する場合)、コンストラクターを介してより多くのデータを渡すことは良いことです。すべての子コンストラクターを壊さないでください。

于 2012-10-10T15:20:56.873 に答える
0

私の経験では、ゲッターやセッターではなく、コンストラクターに引数があることを示しています。多くのパラメーターがある場合は、オプションのパラメーターをデフォルトにすることができますが、必須/必須のパラメーターはコンストラクターパラメーターです。

于 2012-10-10T19:18:55.577 に答える
0

設定が必要であり、デフォルト値を指定できない場合は、コンストラクターで必須にします。そうすれば、実際に設定されることがわかります。

設定が不要で、デフォルト値を指定できる場合は、そのためのセッターを作成します。これにより、コンストラクターが非常に簡単になります。

たとえば、メールを送信するクラスがある場合、コンストラクターで「To」フィールドが必要になる場合がありますが、それ以外はすべてセッター メソッドで設定できます。

于 2012-10-10T19:14:22.187 に答える