web-dev-qa-db-ja.com

Get-Setメソッドはカプセル化の違反ですか?

可能性のある重複:
ゲッターとセッターが正当化される場合

オブジェクト指向のフレームワークでは、厳密なカプセル化が必要であると信じています。したがって、内部変数は外部アプリケーションに公開されません

しかし、多くのコードベースでは、もともと厳密に禁止されていた内部変数を変更するために本質的に形式的なウィンドウを開くget/setメソッドのtonsが見られます。それはカプセル化の明らかな違反ではないですか?そのような慣行はどの程度広く見られ、それに対して何をすべきか?

編集:

私は極端に2つの意見があるいくつかの議論を見てきました。一方で、get/setインターフェースはパラメーターの変更に使用されるため、カプセル化に違反していないと見なされるという人もいます。一方、違反していると信じている人もいます。

これが私のポイントです。メソッド-get_URL()、set_URL()を使用して、UDPサーバーの場合を取り上げます。 URL(リッスンする)プロパティは、アプリケーションが提供および変更する必要があるかなりのパラメータです。ただし、同じケースで、get_byte_buffer_length()やset_byte_buffer_length()などのプロパティが明らかに内部の値を指している場合。それはそれがカプセル化に違反していることを意味しませんか?実際、そうでなければオブジェクトを変更しないget_byte_buffer_length()でも、カプセル化のポイントを逃します。なぜなら、確かに知っているアプリが内部バッファを持っているからです!明日、内部バッファがpacket_listのようなものに置き換えられると、メソッドが機能しなくなります。

Get setメソッドに共通のはい/いいえはありますか? いつがカプセル化に違反し、いつ違反しないかについてプログラマ(特に後輩)に伝える強力なガイドラインはありますか?

12
Dipan Mehta

いいえ

ゲッターとセッターは違反カプセル化せず、それらを提供します。アクセサメソッドを使用している場合、定義により、オブジェクトの変数に直接アクセスしますnot。代わりに、オブジェクトがいくつかのプロパティを操作するために提供するインターフェイスを使用しており、それらのプロパティがivarに格納されているか、ディクショナリに格納されているか、他の値から計算されるかなどは問題ではありません。問題のクラスの実装者は無料のままですインターフェイスを維持している限り、既存のクライアントコードを壊すことなく、クラスの動作を変更します。

インターフェイスがインスタンス変数のアクセサのセットで構成されている場合、クラスはその存在を正当化するのに十分ではない、またはクラスが外部コードから機能する方法を隠すのに十分ではないと言う人もいます。クラスによっては、そのすべてが当てはまる場合がありますが、内部状態(ivar)へのアクセスがクラス自体のメソッドに制限されているためカプセル化されているという事実は変わりません。

カプセル化という用語の意味について、以下のコメントには明らかに不一致があります。私は論文から以下の定義を提供します オブジェクト指向プログラミング言語でのカプセル化と継承pdf ):

プログラミング言語の定義によってクライアントがその定義された外部インターフェースを介してのみモジュールにアクセスするように制限されている場合、モジュールはカプセル化です。したがって、カプセル化により、互換性のある変更を安全に行うことができ、プログラムの進化と保守が容易になります。これらのメリットは、大規模なシステムや長期間有効なデータにとって特に重要です。

アクセサがクラスへの「定義された外部インターフェース」としての資格がないと主張するのは難しいことです。また、クラスの内部状態への外部直接アクセスを防ぐための手順を実行する(つまり、ivarを「プライベート」にする)場合、上記の定義によってカプセル化が達成されます。

しかし、実用的な観点からすると、物事はそれほど単純ではありません。論文のまさに次の段落は続けて言う:

カプセル化の利点を最大化するには、外部インターフェイスでの実装の詳細の露出を最小化する必要があります。プログラミング言語は、minimal外部インターフェイスを定義して適用できる程度までカプセル化をサポートしています。

他のテクニックと同様に、カプセル化は上手に使えたり、上手く使えなかったりします。カプセル化が「壊れる」か「壊れない」かに関する意見のほとんどの違いの中心にあると思います。データの内部表現を外部インターフェースの背後に隠すことが適切である場合は、クラスがその仕事をするために必要なものだけを明らかにする方が良いでしょう。プロパティアクセサーとivarが1対1で対応しているクラスは、その状態をカプセル化すると正しく言えますが、そのカプセル化を使用して最善の効果を得られない場合があります。この状況でコードに多くのクラスが含まれている場合は、システムの設計が不十分である可能性があります。

カプセル化を効果的に使用しているかどうかをどのように判断できますか?論文からの続き:

このサポートは、モジュールの実装に対して安全に行うことができる変更の種類によって特徴付けることができます。たとえば、オブジェクト指向言語の特徴の1つは、クライアントに影響を与えずにインスタンス変数の名前を変更できるように、設計者がクラスを定義できるかどうかです。

そもそもカプセル化を重視する理由に直接対応する簡単なガイドラインを提供するため、このテストが好きです。また、カプセル化によって得られる利点は、バイナリ値ではなく継続的な値であるという考え方も明らかになります。この論文がカプセル化を言語機能として説明しているという事実を見失うことはありませんが、特定のコードにテストを適用して、そのコードが言語によって提供されるカプセル化をどの程度うまく使用しているかを判断するのに役立つと私は安全だと思います。

例1:「どのような変更を安全に行うことができますか?」 OPの質問の例をテストすると、コードはおそらくカプセル化をうまく活用していないようです。 get_byte_buffer_length()およびset_byte_buffer_length()メソッドが内部状態をカプセル化することは技術的には真実かもしれませんが、「バイトバッファ」がUDPサーバーの重要な外部プロパティではなく実装の詳細である場合、バッファをまったく公開しないでください。それが公開されているという事実自体が、UDPサーバーを変更できる方法を制限しています。

例2:サブクラスは通常(言語に応じて)親クラスの内部状態に直接アクセスするため、継承によってカプセル化が解除されるとよく​​言われます。これにもかかわらず、プログラマは、ivar自体を使用する代わりに、アクセサ(プライベートアクセサ)を使用して親クラスから状態にアクセスすることにより、カプセル化を利用できます。

疑わしい場合は、プログラマーは自分に尋ねる必要があります:親クラスが変更されると、コードは壊れますか?およびXを変更すると、このクラスのサブクラスは壊れますか?

25
Caleb

他の回答に記載されているように、定義によりゲッター/セッターはオブジェクト内のフィールドのカプセル化を提供します。ただし、いくつかの落とし穴があります。たとえば、以下の擬似コードを使用すると、リスト「バー」は変更可能なオブジェクトであるため、クライアントコードはFoo型のオブジェクトの内部を混乱させることができます。そのため、クライアントはそれをクリアしたり、新しいものを追加したりするなど、何でもできます。私の見解では、これは明らかにカプセル化違反です。

class Foo
{
    private List bars = new List();

    public List GetBars() 
    {
        return bars;
    }
}

これを軽減するには、バーのゲッターをまったく提供しないか、リストの読み取り専用コピーを返すようにします。

class Foo
{
    private List bars = new List();

    public List GetBars() 
    {
        return bars.AsReadOnly();
    }
}

この問題はリストに固有のものではありませんが、ゲッターから返された変更可能な複合型で発生する可能性があります。

12

私は通常、パブリックセッターを回避しようとします。オブジェクトにデータを提供する必要がある場合は、メソッドパラメーターまたはコンストラクターを介してデータを渡す必要があります。プロパティを外部で設定すると、そのオブジェクトの整合性が損なわれる可能性があります。

カプセル化は、オブジェクトが公開するパブリックAPIのセマンティクスに関するものです...

Nameプロパティを持つ単純なクラスを取ります:

public class Foo
{
    public String Name { get; set; }
}

私はこれを次のように記述したいと思います(これが非常に単純な例である場合):

public class Foo
{
    public String Name { get; private set; }

    public void Rename(String name)
    {
        this.Name = name;
    }
}

ここでの目標は、オブジェクトの状態を外部から直接変更するのではなく、動作の一部を呼び出すことです。これは、behaviourobservable効果オブジェクトの内部状態。

このパターンは、不変性でさらに拡張できます。

public class Foo
{
    // Clone constructor...
    private Foo(foo prototype) { ... }

    public String Name { get; private set; }

    public Foo Rename(String name)
    {
        return new Foo(this)
        {
            Name = name
        };
    }
}
9
MattDavey

彼らはすることができます!あなたが提供する例は、彼らがいる良い例のようです。このようなメソッドは、非常に低レベルの抽象化でのアクセスを提供し、詳細からクラスのユーザーをカプセル化しません。これは、このようなクラスが間違っている、または悪いことを意味するのではなく、使用目的によって異なります。

あなたの例のサーバーは、ユーザーが気にしたり、バッファ管理の詳細を知らなくても、読み取りまたは送信リクエストに応答できるように、(動的バッファのような合理的なデフォルトで構築されている)IMOがバッファポリシーを採用する方がはるかに優れています。彼は望んでいない。

3
Fabio Fracassi

'Complex'クラスがあるとします。実数、虚数、大きさ、角度の4つのゲッターがあります。明らかに、合理的な表現では、これらの関数のうちの2つは単純にプライベートメンバー変数の値を返します。カプセル化が意味することは、それが問題ではないということですwhichこれらのメソッドのうち2つは「シンプル」で、どちらが計算を行うかです。

(1つは、これらのセッターも持つ可変の複雑なクラスを想像することができます。繰り返しますが、そのうちの2つは、単純に直接プライベートメンバー変数を設定します。)

1
Random832

私の意見では、クラスコード内からクラスプロパティを変更することを許可する限り、カプセル化に違反しているわけではありません。

変更を防止したい場合は、設計時(アクセス修飾子、読み取り専用仕様を使用)または実行時(ビジネスルールを使用)に変更を防止する方法があります。

Set/getペアを使用する利点の1つは、データ値の読み取り/設定の後/前に何が起こるかを制御するコード内のポイントを提供することです。

もともと厳密に禁止されていた内部変数を変更するために本質的に正式なウィンドウを開く大量のget/setメソッドを目にします。

内部変数(C#のようにフィールドについて理解していると思います)については、プロパティがパブリック、読み取り専用、またはその他であるかどうかにかかわらず、それらを常にプライベートとして宣言することをお勧めします。

このリンクはさらにヘルプを追加する可能性があります: プロパティへの誤った変更を防ぐためのSetPropertyメソッドの使用

Edit-1

元の質問への編集を読んだ後

あなたの例に基づいて、問題は取得/設定自体にはありません。データの外部ソースに依存するプロパティの実装は、説明している依存関係の問題につながると思います。より「純粋な」プロパティ定義を使用するには、このデータを所有するオブジェクトにメッセージを送信し、メソッドが目的の値を返すようにしてから、Set/Getを使用して処理を続行します。

カプセル化の定義( Encpc.Def-About.Com のように)は、クラスのデータをすべてクラス内で作成する必要があること、またはクラスだけで作成する必要があることを意味しません。問題はありません(実際には望ましい)。これにより、クラスの複雑さを制限し、既存のコードの共有を促進できます。

編集-2

これはメッセージの最初のコメントへの応答です。この記事のMartin Fowlerを読んでいます Getters Eradicator はこう言っています:

「私はこの動機に多くの共感を持っていますが、ゲッターを回避するように人々に伝えるだけではかなり率直なツールであることを恐れています。データを交換することによってオブジェクトが協調する必要があり、それがゲッターの純粋なニーズにつながる場合が多すぎます。 」

記事は主題と非常に関連があり、あなたの質問に答えるかもしれません。

0
NoChance