メソッドの名前を変更してもカプセル化を維持できますか?


9

ゲッター/セッターが正当化される時期についてこのページを読んでいましたが、OPは次のコードサンプルを提供しました。

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

受け入れ答えの状態:

ちなみに、あなたの例ではFridgeputCheese()andのtakeCheese()代わりに、クラスに and メソッドを指定get_cheese()set_cheese()ます。その後、まだカプセル化されています。

カプセル化は、名前をget / setからputCheese()/に変更することによってどのように保持さtakeCheese()れますか?

同じ答えでそれはまた述べています:

ゲッターとセッターを使用しても、カプセル化が壊れることはありません。カプセル化を解除するのは、何も考えずに、すべてのデータメンバー(Java lingoのすべてのフィールド)にゲッターとセッターを自動的に追加することです。

この場合、変数は1つだけなのでcheese、チーズを取り出して冷蔵庫に戻したい場合があるため、この場合の取得/設定ペアは正当化されます。


7
putCheese冷蔵庫にチーズを追加してtakeCheeseそれを削除します-これらはオブジェクトフィールドのゲッターとセッター(低レベルのコンピュータープログラミングの抽象化)ではなく、(高レベルの)ドメイン指向の抽象化です。
Erik Eidt

回答:


17

ポイントが足りないと思います。これは、setterとgetterの名前を変更する必要があると言っているのではなく、冷蔵庫からアイテムを追加および削除するメソッドを用意するためのものです。すなわち

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

これでプライベート変数がカプセル化されました。必要なだけに設定することはできません。メソッドを使用して冷蔵庫にチーズのスライスを追加および削除することしかできません。これにより、必要なチーズビジネスロジックルールが適用されることが保証されます。


2
そうですがsetCheese()、セッターに同じロジックがあるのでそのままにしておくことができます。とにかく、メソッドの名前を変更してセッターを使用しているという事実を隠そうとしているが、明らかに何かをゲッター/設定している。
QueenSvetlana

21
@QueenSvetlanaセッターではありません。操作です。あなたは冷蔵庫に「ここに別のチーズがあります」と言っていて、それはそれを内部のカプセル化されたチーズの数に追加しています。セッターは「あなたは今、5つのチーズを持っている」と言うでしょう。これらは、単に異なる名前ではなく、機能的に異なる操作です。
Ant P

1
setCheeseと呼ぶこともできますが、チーズの値を設定しないので混乱を招きます。
Ewan、

6
ここに整数オーバーフローのバグがあることに注意してください。すでに0を超えるチーズAddCheese(Integer.MAX_VALUE)がある場合、失敗するはずですが、失敗しません。
user253751

3
それは完全に..etcセクションでカバーされます
Ewan

5

定義により、ゲッターとセッターは毎回カプセル化を解除します。どのような可能性があると主張することは、時には我々はそれを行う必要があるということです。それが邪魔にならないうちに、ここに私の答えがあります:

カプセル化の名前をget / setからputCheese()/ takeCheese()に変更することで、カプセル化はどのように保存されますか?

違いは意味論、つまりあなたが書くものの意味にあります。カプセル化は、保護するだけでなく、内部状態を隠すことも目的としています。内部状態は外部でさえ知られているべきではなく、オブジェクトはその状態を操作/使用するためのビジネス関連のメソッド(「動作」と呼ばれることもあります)を提供することが期待されています。

だから、getおよびset技術的な用語でありながら、「冷蔵庫」ドメインとは何の関係もないように見えるputし、take実際にいくつかの意味をなすかもしれません。

しかし、かどうputtake作る実際の感覚はまだ要件に依存し、客観的に判断することはできません。次のポイントを参照してください:

この場合、変数はチーズ1つだけなので、チーズを取り出して冷蔵庫に戻したい場合があるため、この場合の取得/設定ペアは正当化されます。

これは、より多くのコンテキストなしでは客観的に決定することはできません。あなたの例はgo_shopping()どこかにメソッドを含んでいます。それFridgeが目的である場合、それが必要としないgetset、または必要とするものですFridge.go_shopping()。このように、要件から派生したメソッドがあり、必要なすべての「データ」はローカルであり、薄いベールに覆われたデータ構造ではなく実際の動作があります。

覚えておいてください、あなたは一般的で再利用可能なを構築していませんFridge。あなたはFridgeあなたの要件のためだけに構築しています。それを必要以上のものにするために費やしたどんな努力も、実際には無駄です。


10
Getters and setters break encapsulation every single time-それは私の趣味には少し強すぎます。メソッド(ゲッターとセッターを含む)は、コンサートでのゲートと同じ目的で使用されます。これらのメソッドは、規則的に会場に出入りできます。
ロバートハーベイ

8
「ゲッターとセッターは、カプセル化を毎回、定義により中断します」 -どの定義によるのか 他のパブリックメンバーと同様に、ゲッターとセッターはパブリックインターフェイスの一部にすぎないため、私もこれに問題があります。設計によって(つまり、プログラマー(またはチーム)の決定によって)内部であると見なされる実装の詳細を公開する場合にのみ、カプセル化を解除します。ゲッターとセッターが偶然に偶然に追加されることが多く、結合制御が後から考えられるという事実は、まったく別の問題です。
FilipMilovanović19年

2
@FilipMilovanovićフェアポイント。回答で述べたように、「カプセル化」はオブジェクトの内部(==オブジェクトの状態==インスタンス変数)を隠すために使用されます。ゲッターとセッターオブジェクトの内部を公開します。したがって、これらの定義では、それらは永続的な対立にあります。ここで、カプセル化を解除する必要があるかどうかは別問題であり、個別に処理する必要があります。明確にするために、セッター/ゲッターは常にカプセル化を壊すと言っても、それが役立つ場合は常に「間違っている」とは言っていません。
RobertBräutigam19年

5
ゲッターとセッターは「文字通り常にカプセル化を解除する」わけではありません。ゲッターとセッターは多くの場合、直接下のメンバーを参照することも、なんらかの操作を実行することも、排他的に定義されることもあり、ゲッターのみまたはセッターのみを持つ場合があります。ゲッターとセッターがメンバーアクセス以外に何もせず、両方とも同じフィールドに対して定義されている場合、カプセル化が解除されます。これは、内部の変更でABI / APIを壊したり、クライアントの再コンパイルを回避したりしたくないライブラリメンテナーにとって必要な場合もあります。
WHN

4
OK、ゲッターとセッターはカプセル化を99%の時間だけ壊します。ハッピー?そして、カプセル化されたオブジェクトのタイプを公開することで、カプセル化を確実に壊します。いったん取得したらpublic int getFoo()、実際には別のタイプに変更することはほぼ不可能です。
user949300

3

これのほとんどすべては、カプセル化の基本的な誤解と、それがどのように適用されるかを示しています。

カプセル化を解除していた最初の応答は、間違っています。アプリケーションでは、増加/減少または追加/削除するのではなく、単純に冷蔵庫でチーズの値を設定する必要がある場合があります。また、属性にアクセスしたり属性を変更したりする必要がある場合でも、セマンティクスではなく、属性を提供することでカプセル化を壊すことはありません。最後に、カプセル化は実際には「非表示」ではなく、クラスの外部で公開または操作する必要のない状態と値へのアクセスを制御すると同時に、内部で利用できるようにするタスクにタスクを許可して実行することです。

値を取得または設定する正当な必要性がある場合、ゲッターまたはセッターはカプセル化を解除しません。これが、メソッドを公開できる理由です。

カプセル化とは、データとそのデータを直接変更するメソッドを1つの論理的な場所であるクラスに保持することです。

この特定のケースでは、アプリケーションでチーズの値を変更する必要があることは明らかです。これがどのように行われるかに関係なく、メソッドがクラスにカプセル化されている限り、get / setまたはadd / removeを使用して、オブジェクト指向スタイルに従っています。

明確にするために、メソッド名や論理的な実行に関係なく、アクセスを提供することによってカプセル化がどのように壊れるかの例を示します。

冷蔵庫に「寿命」があるとしましょう。冷蔵庫が機能しなくなるまでの数のティックだけです(議論のために、冷蔵庫は修理できません)。論理的には、ユーザー(またはアプリケーションの残りの部分)がこの値を変更できるようにする方法はありません。プライベートにする必要があります。「isWorking」と呼ばれる別のパブリック属性を介してのみ表示されます。有効期限が切れると、冷蔵庫は内部でisWorkingをfalseに設定します。

ライフタイムカウントダウンの実行とisWorkingスイッチの切り替えはすべて冷蔵庫の内部で行われ、外部でプロセスに影響を与えることはできません。isWorkingは表示のみにする必要があるため、ゲッターはカプセル化を中断しません。ただし、ライフタイムプロセスの要素にアクセサを追加すると、カプセル化が壊れます。

ほとんどの場合と同様に、カプセル化の定義はリテラルではなく、相対的です。クラス外でXを見ることができますか?Yを変更できますか?このクラスのオブジェクトに適用されるすべてのものですか、それとも複数のクラスに機能が分散していますか?


実際に見てみましょう。コードがそのように意図されている場合、または「正当な」理由がある場合、カプセル化は壊れていないと言っています。それは基本的に、開発者がそのように「意図した」と言う必要があるか、または「正当な」理由があったため、カプセル化が壊れていると決して言えないことを意味します。だから、この定義は基本的に役に立たないですね。
RobertBräutigam19年

いいえ、アクセスを提供するだけでカプセル化が破られるわけではないと私は言います。そして、それはプログラマーの言うこととは何の関係もありませんが、アプリケーションのニーズは何ですか。アプリケーションは、オブジェクトを操作するためのアクセス権を持っている必要があります。カプセル化とは、アクセスを制御することです。アプリケーションはオブジェクトの属性を「設定」する必要がある場合がありますが、その「設定」操作を実行するために必要なロジックや計算は、オブジェクトクラスにカプセル化する必要があります。
user343330

これは私たちが根本的に異なるところだと思います。「アプリケーションはオブジェクトの属性を「設定」する必要があるかもしれません...」。私はそうは思いません。属性の設定または取得を伴うデザインの選択は、開発者次第です。それは選択です!何かをコード化する方法はたくさんありますが、インスタンス変数の値を取得または設定する方法がすべてではありません。
RobertBräutigam19年

可能性があります...通常、設計はニーズを満たすことに基づいています。どちらがプログラマによって選択されるかは、ビジネス環境に基づいています。ただしどちらの場合でも、アプリケーションがオブジェクトの一部である値を操作する根本的な必要性を持っている場合は、その機能に何らかの方法でアクセスできるようにする必要があります。作業を行うためのエントリポイントを提供しても、カプセル化が壊れることはありません。セールスアプリを取ります。ある時点で、あなたの取引は、取引対象で、それがカプセル化され続けるだろうことをやって、税を計算する必要がありますが、このアプリはtransaction.CalcTaxを述べることができるはず
user343330

セッターはその単純さのために悪い例です。オブジェクトの属性が更新され、クラスの外で何かが行われ、object.attribute = xとなるような、はるかに多くの作業を行う関数の観点から考えてください。1つ目は適切なアクセスを提供しながら適切にカプセル化することで、2つ目はそうではありません。ゲッターについては、オブジェクトクラスに含めることができない他のことを行うために、アプリケーションはオブジェクトのその部分だけを知る必要がありますか?はいの場合、それを公開してもカプセル化は解除されません。何場合は、そのオブジェクトのクラスでそれをカプセル化していない
user343330

1

メソッドの名前を変更するだけではありません。2つの方法は機能が異なります。

(あなたの心の中にこれを描きなさい)

get_cheeseとset_cheeseはチーズを公開します。putCheese()とtakeCheese()は、チーズを非表示にして管理し、ユーザーがチーズを処理する方法を提供します。観察者はチーズを見ません。彼/彼女はそれを操作する2つの方法だけを見ます。

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