web-dev-qa-db-ja.com

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

私はまだ自分は見習いプログラマーであると考えているので、私はいつも典型的なプログラミングのための「より良い」方法を学びたいと思っています。今日、私の同僚は私のコーディングスタイルがいくつかの不要な作業を行うと主張しており、他の人からの意見を聞きたいです。通常、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だけに限定されず、他の言語の回答も歓迎されます。

14
Caladbolgll

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

  1. エラーが発生した場合は、できるだけ早く発生し、診断が最も簡単です。たとえば、nullが無効な引数値である場合、コンストラクタでテストして失敗します。
  2. オブジェクトはalwaysが有効な状態です。同僚は間違いを犯すことができずinitMyClass1()を呼び出すのを忘れますそこに"最も安価で、最も速く、最も信頼できるコンポーネントは、そこにないものです。"
  3. それが理にかなっている場合、オブジェクトを作成することができます immutable これには多くの利点があります。
28
user949300

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

ワンショットで実行できるものを2つに分割するのはなぜですか?

追加の初期化は、APIを使用するプログラマが覚えておかなければならない特別なものであり、正しく実行しないと失敗する可能性が高くなりますが、この余分な負担に対してどのような価値がありますか?

あなたは完全にシンプルで使いやすく、間違った抽象化になりにくいものを提供したいと考えています。プログラミングは、覚えておくべき不必要な事柄やジャンプするためのフープなしでは十分に困難です。 APIユーザー(自分のAPIを使用しているだけの場合でも)を 成功の秘訣 に分類する必要があります。

2
Erik Eidt

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

1
zzz777

オブジェクトに2つのカテゴリに分類できる多くの初期化がある場合があります。

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

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

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

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

0
Ramakant

他の人が言ったように、コンストラクタで初期化することは一般的に良い考えです。

ただし、特定の場合にそれが当てはまらない場合や当てはまらない場合もあります。

エラー処理

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

初期化でエラーが発生する合理的な可能性がある場合。 IOまたはそのパラメータはユーザー入力である可能性があります。その場合、開かれる唯一のメカニズムは、例外を発生させることです。場合によっては、これが意図したものとは異なる場合があり、より意味のある場合がありますエラーが発生しやすいコードを別の初期化関数に分離します。

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

ステートマシン

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

この場合、オブジェクト構築(および削除)がメモリ指向の属性(ファイル名、ポートなど)のみを処理するのが一般的です。次に、状態遷移を具体的に管理するための関数があります。 open、closeは効果的に初期化され、機能を破棄します。

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

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

0
Alex