3

私はOOPの「データ隠蔽」の概念を知っていますが、実際の開発では、仕様が変更されると常に問題が発生します。

例えば:

class role
{
    std::string name;
    int level;
    public:
        const std::string& get_name() { return name; }
        void set_name(const std::string& value) { name = value; }
        void set_level(int value) { level = value; }
        int get_level() const { return level; }
}

もちろん、このコードには何の問題もありません。しかしnamelevelまったくカプセル化されていないと思いました。

私の意見は次のとおりです。

  1. ユーザーはセッター機能でデータを変更できます。
  2. setter / getterは、これらのデータメンバーのデータ型を公開します。これらのデータメンバーがタイプを変更する必要がある場合、setter/getter関数メンバーもインターフェイスを変更する必要があります。
  3. メンバーに別の操作が必要な場合はlevel、操作メンバー(eqなど)関数を追加するadd_level(int value)sub_level(int value)が唯一の方法です。
  4. 1つだけ問題ありません。それらにいくつかの判断を追加する必要がある場合get/set、これらのインターフェイスはうまく機能する可能性があります。

では、どのような種類のデータメンバーをカプセル化する必要がありますか?これらのデータメンバーでは、規模と使用量をまったく予測できません。それらを直接公開すると、それらの操作は完全に単純で、明確で、意味のあるものになります。それらをカプセル化すると、多くの操作メンバーが作成され、いつかそれらのタイプが仕様(int-> class、class-> int)によって変更された場合、操作メンバーはインターフェイスを変更するか、すべてを直接強制終了する必要があります(それらをパブリックゾーンに送信します。

4

4 に答える 4

3

私は多くの異なる言語、特に関数型言語を読んでいますが、セッターのアイデアにゆっくりと疑問を投げかけるようになりました。

私がクラスを持っているとするとPerson、これは私が数年前に書いたものです:

class Person {
public:
    std::string const& name() const { return _name; }
    void name(std::string const& n) { _name = n; }

    unsigned age() const { return _age; }
    void age(unsigned a) { _age = a; }

private:
    std::string _name;
    unsigned _age;
};

あなたはそれがあなた自身のクラスにどれほど似ているかに気付くでしょう。

  • Personオブジェクトの存続期間中に名前が変更されますか?
  • Personオブジェクトの存続期間中に変更の年齢は変更されますか?この状況を回避するために何ができるでしょうか。
  • 私はタイプに関して自分自身を縛りました_name:それを変えることはname()ゲッターを壊すでしょう...

そして今、私が今日書くものである代替の実装:

class Person {
public:
    Person(std::string name, Time birth): _name(name), _birth(birth) {}

    std::string name() const { return _name; }

    Duration age(Time now) { return now - _birth; }

private:
    std::string _name;
    Time _birth;
};

このクラスにはセッターがありません。このクラスは、内部にハンドルを返さなくなりました(したがって、コピーの価格を支払うので、とにかく多くはないでしょう)。

ただし、最も注目すべき点は、情報の記憶方法を変更したことです。これageは、生年月日と現在の日付から導出される変動値です。したがって、なぜソースではなく副産物を記憶するのですか?

そして、値を変更したいですか?上手:

Time const now = Time::Now();
person = Person("John R. Smith", now - person.age(now));

十分に機能します。そして、少なくとも私は不変条件を(コンストラクターで)一度だけ書きます。

明らかに、これは必ずしもどこにでも当てはまるわけではありません。クラスにいくつかのフィールドしかない場合、それはうまく機能します。クラスがより多くのフィールドを取得したら、それらのいくつかを独自のクラスに抽出するときが来たのかもしれません。

于 2012-10-14T13:58:05.977 に答える
1

アイデアは、クラスがインターフェースを定義するということです。これは、その作成者とそれを使用する開発者の間のコントラクトです。インターフェイスを変更すると、それを使用するコードを変更する必要がある場合があるため、インターフェイスは可能な限り一定に保つ必要があります。

セッターとゲッターを作成すると、プライベートメンバーへのアクセスを検証でき、クライアントコードに影響を与えることなく、基盤となる実装を変更できます(たとえば、内部データ型を変更できます)。

たとえば、timeクラスを作成したとします。セッターは、呼び出し元が、、、およびメンバーhoursに無効な値を格納するのを防ぐことができます。そして、値がリアルタイムクロックから取得されると仮定します。ゲッターはRTCにクエリを実行し、アクセスできない場合や壊れている場合はエラーコードを返すことができます。minutesseconds

別の例として、32ビット整数を返すゲッターを定義したとします。後で、64ビット整数を使用するように実装を変更することで修正できる不正確さを発見します。(おそらく、32ビットに丸められるある種の計算を実行しています。)ゲッターは実装をカプセル化するため、内部で64ビットを使用するように自由に更新できますが、(現在はより正確な)32ビット値を返します。新しいクラスは、古いクラスのドロップイン代替品です。

于 2012-10-14T13:20:28.000 に答える
1

ゲッターとセッターは通常、メンバー変数を割り当てる以外のことを行う場合にのみ役立ちます。

class A {
public:
  int get_value () const;
  void set_value (int v);

private:
  int _value;
};

1)実装を変更するつもりがない(基本的にはインターフェイスに対してコーディングしていない)、2)実行する検証がない場合、上記は実際には何も購入しません。

クラスが不変条件を共有しないデータで構成されている場合(基本的に、クラスは異なる値を収集するだけですが、メンバー間にルールはありません)、ゲッターとセッターが役立つかどうかを議論できます。

たとえば、Dateクラスの場合、メンバー(日、月、年)は不変条件を共有するため、実際にはセッターとゲッターを使用する必要があります(たとえば、月が12を超えると、年もインクリメントする必要があります)。

ただし、XとYが不変条件を共有しないPointクラスの場合、本当にインターフェイスにしてポリモーフィズムを使用する場合を除いて、ゲッターとセッターをスキップできます。

また、仕様が変更されるため(とにかく常に実行される)、OOPが難しいことに同意しませんが、正しく使用されるOOPは、変更に対処し、プログラムの一部を相互に分離して変更を行うのに役立つ手法(他と組み合わせて)です。コードベース全体を細流化することはありません。これがうまくいくようにあなたが従うことができる簡単なルールがあるとは言いませんが、これは経験の問題です。

于 2012-10-14T13:46:50.807 に答える
0
  1. set / getを使用して、クラス外(別のクラスなど)の値を変更します。
  2. クラスの外部に公開する必要のない関数(プライベート付き)を非表示にします。たとえば、さまざまなメソッドを使用して統合を計算する関数を持つクラスがあります。ただし、これらのアルゴリズムはすべて、sumIt()という関数に依存しています。この関数は、他のクラスで明示的に使用しないでください。
  3. 毎回独自のゲッター/セッターを手動で作成することを避けたい場合は、テンプレートを使用してC ++を簡単に拡張し、修飾子のような「プロパティ」を許可できます。クラス/アプリケーションの設計によって異なります。
于 2012-10-14T13:18:53.223 に答える