OOPコーディングスタイル:コンストラクターですべてを初期化しますか?


14

私はまだ見習いプログラマーであると考えているので、私はいつも典型的なプログラミングのための「より良い」方法を学ぼうとしています。今日、私の同僚は私のコーディングスタイルが不必要な仕事をしていると主張しており、他の人から意見を聞きたいと思っています。通常、OOP言語(通常はC ++またはPython)でクラスを設計するとき、初期化を2つの異なる部分に分けます。

class MyClass1 {
public:
    Myclass1(type1 arg1, type2 arg2, type3 arg3);
    initMyClass1();
private:
    type1 param1;
    type2 param2;
    type3 param3;
    type4 anotherParam1;
};

// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
    : param1(arg1)
    , param2(arg2)
    , param3(arg3)
    {}

// Any other procedure is done in a separate initialization function 
MyClass1::initMyClass1() {
    // Validate input arguments before calculations
    if (checkInputs()) {
    // Do some calculations here to figure out the value of anotherParam1
        anotherParam1 = someCalculation();
    } else {
        printf("Something went wrong!\n");
        ASSERT(FALSE)
    }
}

(または、同等のpython)

class MyClass1:

    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
        #optional
        self.anotherParam1 = None

    def initMyClass1():
        if checkInputs():
            anotherParam1 = someCalculation()
        else:
            raise "Something went wrong!"

このアプローチについてのあなたの意見は何ですか?初期化プロセスの分割は控えるべきですか?質問はC ++とPythonだけに限定されず、他の言語の回答も歓迎します。





なぜ通常それを行うのですか?習慣?あなたはそれをする理由を与えられましたか?
ジェフ16

@JeffO MFCライブラリを使用してGUIを作成するために働いていたときに、この習慣がありました。CApp、CWindow、CDlgなどのほとんどのUI関連クラスには、上書き可能なOnInit()関数があり、対応するメッセージに応答します。
Caladbolgll

回答:


28

問題がある場合もありますが、コンストラクターですべてを初期化することには多くの利点があります。

  1. エラーが発生している場合は、できるだけ早く発生し、診断が最も簡単です。たとえば、nullが無効な引数値である場合、コンストラクターでテストして失敗します。
  2. オブジェクトは常に有効な状態です。同僚がミスを犯すことはできないとの通話に忘れてinitMyClass1()いるので、それはありません「最も安価で、最速で、最も信頼性の高いコンポーネントは、存在しないコンポーネントです。」
  3. 理にかなっている場合は、オブジェクトを不変にすることができ、多くの利点があります。

2

ユーザーに提供する抽象化について考えてください。

なぜ1ショットでできることを2つに分割したのですか?

余分な初期化は、APIを使用するプログラマーが覚えておくための特別なものであり、正しくしない場合は間違った方向に進みますが、この余分な負担に対してどのような価値がありますか?

完全にシンプルで使いやすく、間違った抽象化を行いにくいようにする必要があります。プログラミングは、覚えておく必要のないもの/ジャンプするためのフープなしでは十分に困難です。APIユーザーは(あなた自身のAPIを使用しているだけであっても)成功落とし穴に落ちてほしい。


1

ビッグデータ領域を除くすべてを初期化します。静的分析ツールは、コンストラクターで初期化されていないフィールドにフラグを付けます。ただし、最も生産的で安全な方法は、すべてのメンバー変数にデフォルトコンストラクターを設定し、デフォルト以外の初期化が必要なメンバー変数のみを明示的に初期化することです。


0

オブジェクトには多くの初期化があり、次の2つのカテゴリに分けることができます。

  1. 不変であるか、リセットする必要がない属性。

  2. ジョブを実行した後の何らかの条件に基づいて、元の値(またはテンプレート化された値)に戻す必要があるかもしれない属性、ソフトリセットのようなもの。たとえば、接続プール内の接続。

ここで、InitialiseObject()などの別の関数に保持されている初期化の2番目の部分は、ctorで呼び出すことができます。

ソフトリセットが必要な場合は、オブジェクトを破棄して再作成せずに、後で同じ関数を呼び出すことができます。


0

他の人が言ったように、一般的にはコンストラクタで初期化することをお勧めします。

ただし、特定の場合に適用される場合とされない場合があります。

エラー処理

多くの言語では、コンストラクターでエラーを通知する唯一の方法は例外を発生させることです。

初期化にエラーが発生する可能性が十分にある場合(たとえば、IOに関連している場合、またはパラメーターがユーザー入力である場合)、唯一のメカニズムは例外を発生させることです。場合によっては、これはあなたが望むものではないかもしれず、エラーが発生しやすいコードを別の初期化関数に分離する方が意味があるかもしれません。

プロジェクト/組織の標準が例外をオフにすることである場合、おそらくこれの最も一般的な例はC ++です。

ステートマシン

これは、明示的な状態遷移を持つオブジェクトをモデリングしている場合です。たとえば、開いたり閉じたりできるファイルまたはソケット。

この場合、オブジェクトの構築(および削除)では、メモリ指向の属性(ファイル名、ポートなど)のみを処理するのが一般的です。その後、状態遷移を具体的に管理する機能があります。たとえば、オープン、クローズは、効果的に初期化およびティアダウン機能です。

上記のように、エラー処理に利点がありますが、構成を初期化から分離する場合もあります(たとえば、ファイルのベクトルを構築して非同期で開く場合)。

欠点は、他の人が言ったように、クラスのユーザーに状態管理の負担をかけることです。構築だけで管理できれば、たとえばRAIIを使用してこれを自動的に行うことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.