カプセル化はまだOOPの象の1つですか?


97

カプセル化により、すべてまたはほとんどすべてのフィールドをプライベートにして、ゲッター/セッターによってこれらを公開するように指示されます。しかし、Lombokなどのライブラリが登場し、1つの短い注釈ですべてのプライベートフィールドを公開できるようになりました@Data。すべてのプライベートフィールドのゲッター、セッター、および設定コンストラクターを作成します。

誰かが私にすべてのフィールドをプライベートとして非表示にし、その後いくつかの余分な技術でそれらのすべてを公開するという意味を説明できますか?なぜパブリックフィールドだけを使用しないのですか?私たちは出発点に戻るためだけに長く困難な道を歩んだと感じています。

はい、ゲッターとセッターを介して機能する他のテクノロジーがあります。そして、単純なパブリックフィールドを介してそれらを使用することはできません。しかし、これらの技術が登場したのは、私たちそれらの多くの特性を持っているからです-公共のゲッター/セッターの背後にあるプライベートフィールド プロパティがなければ、これらの技術は別の方法で開発され、パブリックフィールドをサポートします。そして、すべてがシンプルになり、ロンボクは今は必要ありません。

このサイクル全体の意味は何ですか?そして、実際のプログラミングではカプセル化は本当に意味がありますか?


19
"It will create getters, setters and setting constructors for all private fields."-このツールを説明する方法、カプセル化を維持しているようです。(少なくとも、ゆるく、自動化された、多少貧弱なモデルの意味で。)では、正確に何が問題なのでしょうか?
デビッド

78
カプセル化は、オブジェクトの実装内部をそのパブリックコントラクト(通常はインターフェイス)の背後に隠します。ゲッターとセッターはまったく逆のことを行います-それらはオブジェクトの内部を公開するため、問題はカプセル化ではなくゲッター/セッターにあります。

22
@VinceEmighデータクラスにはカプセル化がありません。それらのインスタンスは、プリミティブとまったく同じ意味での値です
カレス

10
JavaBeansを使用する@VinceEmigh はオブジェクト指向ではなく手続き型です。文献がそれらを「オブジェクト」と呼ぶことは、歴史の誤りです。
カレス

28
私は長年にわたってこれについて多くのことを考えてきました。これは、OOPの意図が実装とは異なる場合だと思います。SmallTalkを研究した後、OOPのカプセル化が意図されていることは明らかです(つまり、各クラスは、共有プロトコルとしてのメソッドを持つ独立したコンピューターのようになります)。概念的なカプセル化を提供しないゲッター/セッターオブジェクト(何も隠さない、何も管理せず、データ以外の責任を持たない)、それでもプロパティを使用します。
jrh

回答:


82

すべての属性をゲッター/セッターで公開すると、Cまたは他の手続き言語でまだ使用されているデータ構造のみを取得できます。これはカプセル化ではなく、Lombokは手続き型コードを簡単に使用できるようにしています。単純なパブリックフィールドと同じくらい悪いゲッター/セッター。本当に違いはありません。

また、データ構造はオブジェクトではありません。インターフェイスの作成からオブジェクトの作成を開始する場合、インターフェイスにゲッター/セッターを追加することはありません。属性を公開すると、データの操作がオブジェクトの外部にあり、コードベース全体に広がるスパゲッティプロシージャコードにつながります。これで、オブジェクトと通信するのではなく、データを処理し、データを操作します。ゲッター/セッターを使用すると、データ駆動型の手続き型プログラミングが可能になり、操作はまっすぐに命令的に行われます。データを取得する-何かをする-データを設定する。

OOPでは、正しい方法でカプセル化することは象です。オブジェクトが完全に制御できるように、状態と実装の詳細をカプセル化する必要があります。ロジックはオブジェクト内でフォーカスされ、コードベース全体に広がることはありません。そして、はい-コードのメンテナンス性が向上するため、プログラミングではカプセル化が依然として不可欠です。

編集

議論が進行しているのを見て、いくつかのことを追加したいと思います。

  • ゲッター/セッターを介して公開する属性の数と、それをどの程度慎重に行うかは関係ありません。より選択的であっても、カプセル化でコードをOOPにすることはありません。公開するすべての属性は、その裸のデータを命令的な手順で処理する何らかの手順につながります。より選択的にすることで、コードの拡散が遅くなります。それはコアを変更しません。
  • はい、システム内の境界では、他のシステムまたはデータベースから裸のデータを取得します。しかし、このデータは別のカプセル化ポイントです。
  • オブジェクトは信頼できるものでなければなりません。あなたがする必要がないように、オブジェクトの全体的なアイデアは、責任ある中である受注与えるまっすぐで不可欠です。代わりに、オブジェクトがコントラクトを通じて適切に機能することを行うように依頼しています。演技部分オブジェクトに安全に委任します。オブジェクト状態と実装の詳細をカプセル化します。

したがって、なぜこれを行う必要があるのか​​という質問に戻ります。この簡単な例を考えてみましょう。

public class Document {
    private String title;

    public String getTitle() {
        return title;
    }
}

public class SomeDocumentServiceOrHandler {

    public void printDocument(Document document) {
        System.out.println("Title is " + document.getTitle());
    }
}

ここには、ゲッターによって内部の詳細を公開するドキュメントと、オブジェクトの外部で機能する外部プロシージャコードがあります。なぜこれが悪いのですか?なぜなら今はCスタイルのコードしか持っていないからです。はい、構造化されていますが、実際の違いは何ですか?C関数を異なるファイルに名前付きで構造化できます。そして、いわゆるレイヤーはまさにそれを行っています。サービスクラスは、データを操作する一連のプロシージャです。そのコードは保守性が低く、多くの欠点があります。printDocument

public interface Printable {
    void print();
}

public final class PrintableDocument implements Printable {
    private final String title;

    public PrintableDocument(String title) {
        this.title = title;
    }

    @Override
    public void print() {
        System.out.println("Title is " + title);
    }
}

これと比較してください。これでコントラクトが作成され、このコントラクトの実装の詳細がオブジェクトに隠されました。これで、そのクラスを本当にテストでき、そのクラスはいくつかのデータをカプセル化しています。そのデータでどのように機能するかは、オブジェクトの問題です。今オブジェクトと話すためには彼に自分自身を印刷するように頼む必要があります。それがカプセル化であり、それがオブジェクトです。OOPを使用すると、依存関係の注入、モック、テスト、単一の責任、および多くの利点を最大限に活用できます。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
maple_shaft

1
「単なる公開フィールドと同じくらい悪いゲッター/セッター」-それは少なくとも多くの言語では真実ではありません。通常、プレーンフィールドアクセスはオーバーライドできませんが、ゲッター/セッターはオーバーライドできます。これにより、子クラスに汎用性が与えられます。また、クラスの動作を簡単に変更できます。たとえば、プライベートフィールドに裏打ちされたゲッター/セッターから始めて、後で別のフィールドに移動したり、他の値から値を計算したりできます。プレーンフィールドではどちらもできません。一部の言語では、フィールドに本質的に自動のゲッターおよびセッターを設定できますが、Javaはそのような言語ではありません。
キャット

えー、まだ納得できません。あなたの例は大丈夫ですが、存在しない「真の正しいもの」ではなく、異なるスタイルのコーディングを示しているだけです。現在、ほとんどの言語はマルチパラダイムであり、まれに純粋なオブジェクト指向または純粋な手続き型ではないことに注意してください。「純粋なオブジェクト指向の概念」に固執する。例として-印刷ロジックをサブクラスに結合したため、例でDIを使用することはできません。印刷はドキュメントの一部であってはなりません。printableDocumentは、印刷可能な部分を(ゲッター経由で)PrinterServiceに公開します。
T. Sar

これに対する適切なオブジェクト指向アプローチは、「純粋なオブジェクト指向」アプローチを行う場合、メッセージを介して、地獄の印刷対象を要求する抽象PrinterServiceクラスを実装するものを使用します。PrinterServiceは、プリンタの任意の並べ替え可能性が実装事は-あなたのソリューションは、それは不可能、より結合され、終わる、何か他のもののために印刷ロジックをスワップアウトすることができます... TXTに、画面に、PDFに印刷少ない maintanableあなたの「間違った」例より。
T. Sar

結論:ゲッターとセッターは邪悪でも、OOPを破壊するものでもありません。あなたの例は、DIがどのように機能するかという点を完全に理解していない人々の教科書の例です。あなたが「修正」した例は、すでにDI対応であり、分離されており、簡単にモックすることができます。オブジェクト指向の柱の1つですか?コードをリファクタリングする方法で、ウィンドウのそばに投げただけです。これは、深刻なコードレビューでは飛ぶことはありません。
T. Sar

76

誰かが私に説明することができますか?すべてのフィールドをプライベートとして非表示にし、その後いくつかの余分な技術ですべてを公開する意味は何ですか?なぜパブリックフィールドだけを使用しないのですか?

感覚は、あなたがそうすることになっていないということです。

カプセル化とは、アクセスするために実際に他のクラスが必要なフィールドのみを公開し、非常に選択的かつ慎重であることを意味します。

デフォルトですべてのフィールドにゲッターとセッターを与えるだけではありませ

これは、JavaBeans仕様の精神に完全に反しています。JavaBeans仕様は、皮肉なことに、パブリックgetterおよびsetterの概念の由来です。

しかし、仕様を見ると、ゲッターとセッターの作成が非常に選択的であり、「読み取り専用」プロパティー(セッターなし)および「書き込み専用」プロパティー(なしゲッター)。

別の要因は、ゲッターとセッターが必ずしもプライベートフィールドへの単純なアクセスではないことです。ゲッターは、任意の複雑な方法で返す値を計算できます。キャッシュすることもできます。セッターは値を検証したり、リスナーに通知したりできます。

だから、あなたは以下のとおりです。カプセル化は、あなたが実際に公開する必要があるだけでその機能を公開意味します。しかし、何を公開する必要があるかを考えず、構文変換に従ってすべてを自由に公開する場合、もちろんそれは実際にはカプセル化ではありません。


20
「[G]セッターとセッターは必ずしも単純なアクセスではありません」-それが重要なポイントだと思います。コードが名前でフィールドにアクセスしている場合、後で動作を変更することはできません。obj.get()を使用している場合は、できます。
ダンアンブロージョ

4
@jrh:Javaの悪い習慣と呼ばれることは聞いたことがなく、かなり一般的です。
マイケルボルグワード

2
@MichaelBorgwardt 興味深い; ちょっとしたトピックですが、なぜMicrosoftがC#に対してこれを行うことを推奨しないのかといつも思っていました。これらのガイドラインは、C#でセッターを使用できる唯一のことは、失敗したり無効な値を持たない方法で内部的に使用される他の値に与えられた値を変換することを意味すると思います(たとえば、PositionInchesプロパティとPositionCentimetersプロパティを持つ内部でmmに自動的に変換される場所)良い例ではありませんが、現時点で思いつく最高のものです。
jrh

2
そのソースは、セッターとは対照的に、ゲッターに対してそれをしないと言っている@jrh。
キャプテンマン

3
@Gangnusとあなたは本当に誰かがゲッター/セッターの中にいくつかのロジックを隠しているのを見ますか?私たちはそれに慣れているのでgetFieldName()、私たちにとっては自動契約になります。その背後にある複雑な動作は期待しません。ほとんどの場合、単にカプセル化が中断されます。
イズバサールトレゲン

17

問題の核心はあなたのコメントで説明されていると思います:

私はあなたの考えに完全に同意します。しかし、どこかでオブジェクトにデータをロードする必要があります。たとえば、XMLから。そして、それをサポートする現在のプラットフォームは、ゲッター/セッターを介してそれを行うため、コードの品質と考え方が低下します。ロンボクは、それ自体が悪いわけではありませんが、その存在自体が私たちに何か悪いことがあることを示しています。

あなたが持っている問題は、永続データモデルとアクティブデータモデルを混合しているということです。

通常、アプリケーションには複数のデータモデルがあります。

  • データベースと通信するためのデータモデル、
  • 構成ファイルを読み取るためのデータモデル、
  • 別のアプリケーションと通信するためのデータモデル、
  • ...

実際に計算を実行するために使用するデータモデルの上。

一般に、外部との通信に使用されるデータモデルは、計算が実行される内部データモデル(ビジネスオブジェクトモデル、BOM)から分離され独立している必要があります。

  • 独立:すべてのクライアント/サーバーを変更することなく、要件に合わせてBOMのプロパティを追加/削除できるように...
  • 分離:すべての計算が不変条件が存在するBOMで実行され、あるサービスから別のサービスへの変更、または1つのサービスのアップグレードがコードベース全体に波及しないようにします。

このシナリオでは、通信レイヤーで使用されるオブジェクトがすべてのアイテムを公開するか、ゲッター/セッターによって公開されることはまったく問題ありません。これらは、不変のない単純なオブジェクトです。

一方、BOMには不変式が必要です。これにより、一般に多くのセッターが使用できなくなります(ゲッターは不変式に影響しませんが、カプセル化はある程度は低下します)。


3
通信オブジェクトのゲッターとセッターを作成しません。フィールドを公開するだけです。なぜ役に立つよりも多くの仕事を作成するのですか?
イミビス

3
@immibis:一般的には同意しますが、OPで指摘されているように、一部のフレームワークはゲッター/セッターを必要とします。OPは、単一の属性を適用して自動的に作成するライブラリを使用していることに注意してください。
マチューM.17年

1
@Gangnus:ここでの考え方は、ゲッター/セッターが境界(I / O)レイヤーに分離されていることです。ここでは通常、汚れますが、アプリケーションの残り(純粋なコード)は汚染されず、エレガントなままです。
マシューM.

2
@kubanczyk:私の知る限り、公式の定義はビジネスオブジェクトモデルです。ここでは、アプリケーションの「純粋な」コアでロジックを実行するデータモデルである「内部データモデル」と呼んでいます。
マチューM.17年

1
これは実用的なアプローチです。私たちはハイブリッドの世界に住んでいます。外部ライブラリーがBOMコンストラクターに渡すデータを知っている場合、BOMしかありません。
エイドリアンイフトデ

12

以下を考慮してください。

Userプロパティを持つクラスがありますint age

class User {
    int age;
}

User年齢だけではなく、生年月日を設定するために、これを拡大します。ゲッターの使用:

class User {
    private int age;

    public int getAge() {
        return age;
    }
}

int ageより複雑なフィールドを交換できますLocalDate dateOfBirth

class User {
    private LocalDate dateOfBirth;

    public int getAge() {
        LocalDate now = LocalDate.now();
        int year = ...; // calculate using dateOfBirth and now
        return year;
    }

    // other behaviors can now make use of dateOfBirth
}

契約違反、コード破損はありません。より複雑な動作に備えて、内部表現をスケールアップするだけです。

フィールド自体はカプセル化されています。


今、懸念をクリアするために..

Lombokの@Data注釈は、Kotlinのデータクラスに似ています

すべてのクラスが動作オブジェクトを表すわけではありません。カプセル化の解除に関しては、機能の使用方法によって異なります。すべてのフィールドをゲッターで公開するべきではありません。

カプセル化は、クラス内の構造化データオブジェクトの値または状態を隠すために使用されます

より一般的な意味では、カプセル化は情報を隠す行為です。を悪用した場合@Data、おそらくカプセル化が破られていると推測しやすいでしょう。しかし、それはそれが目的を持っていないということではありません。たとえば、JavaBeansは一部の人に嫌われています。それでも、エンタープライズ開発では広く使用されています。

Beanを使用しているため、エンタープライズ開発は悪いと結論付けますか?もちろん違います!要件は、標準開発の要件とは異なります。豆を乱用することはできますか?もちろん!彼らはいつも虐待されています!

Lombokはまた@Getter@Setter独立してサポートします-要件が要求するものを使用します。


2
これは@Data、型の注釈を平手打ちすることとは無関係であり、設計上、すべてのフィールドのカプセル化を解除します
-Caleth

1
いいえ、フィールドは非表示ではありません。何でも一緒に来ることができるからですsetAge(xyz)
カレス

9
@Calethフィールド非表示です。セッターに事前条件と事後条件を設定できないかのように振る舞います。これは、セッターを使用する非常に一般的な使用例です。フィールド==プロパティのように振る舞います。これは、C#のような言語でも、両方ともサポートされる傾向があるため(C#のように)真実ではありません。フィールドが別のクラスに移動することができ、それは、より複雑な表現...と交換することができます
ヴィンスEmigh

1
そして、私が言っていることは重要
カレス

2
@Calethどうして重要じゃないの?それは意味がありません。カプセル化は定義上適用され、ユースケースがありますが、この状況では重要ではない感じるため、カプセル化は適用されないと言いますか?
ビンスエミー

11

カプセル化により、すべてまたはほとんどすべてのフィールドをプライベートにし、ゲッター/セッターによってこれらを公開するように指示されます。

これは、オブジェクト指向プログラミングでカプセル化がどのように定義されるかではありません。カプセル化とは、各オブジェクトがカプセルのようなものであり、その外部シェル(パブリックAPI)がその内部(プライベートメソッドとフィールド)へのアクセスを保護および規制し、ビューから隠すことを意味します。内部を非表示にすることにより、呼び出し元は内部に依存せず、呼び出し元を変更(または再コンパイル)せずに内部を変更できます。また、カプセル化により、呼び出し元が安全な操作のみを使用できるようにすることで、各オブジェクトが独自の不変条件を適用できます。

したがって、カプセル化は情報隠蔽の特殊なケースであり、各オブジェクトは内部を隠し、不変条件を強制します。

すべてのフィールドのゲッターとセッターの生成は、内部データの構造が隠されておらず、不変条件を強制できないため、カプセル化の非常に弱い形式です。ただし、呼び出し元を変更(または再コンパイル)しなくても、データを内部に格納する方法を変更できるという利点があります(古い構造との間で変換できる限り)。

誰かが私にすべてのフィールドをプライベートとして非表示にし、その後いくつかの余分な技術でそれらのすべてを公開するという意味を説明できますか?なぜパブリックフィールドだけを使用しないのですか?私たちは出発点に戻るためだけに長く困難な道を歩んだと感じています。

部分的に、これは歴史的な事故によるものです。1つ目は、Javaではメソッド呼び出し式とフィールドアクセス式が呼び出しサイトで構文的に異なることです。つまり、フィールドアクセスをゲッターまたはセッター呼び出しで置き換えるとクラスのAPIが壊れます。したがって、アクセサーが必要な場合は、今すぐアクセサーを作成するか、APIを解除する必要があります。この言語レベルのプロパティサポートの欠如は、他の最新の言語、特にC#EcmaScriptとは対照的です。

Strike 2は、JavaBeans 仕様がプロパティをゲッター/セッターとして定義したことであり、フィールドはプロパティではありませんでした。その結果、初期のエンタープライズフレームワークのほとんどはゲッター/セッターをサポートしていましたが、フィールドはサポートしていませんでした。それは今までに長いです(Java Persistence API(JPA)Bean ValidationJavaアーキテクチャーfor XMLバインディング(JAXB)Jacksonすべてがフィールドをサポートするようになりました)、しかし、古いチュートリアルと本は長続きし、誰もが物事が変わったことを認識しているわけではありません。言語レベルのプロパティのサポートがないことは、場合によっては苦痛になりますが(たとえば、パブリックフィールドが読み取られたときに単一エンティティのJPA遅延読み込みがトリガーされないため)、ほとんどのパブリックフィールドは正常に機能します。要するに、私の会社は、REST APIのすべてのDTOをパブリックフィールドで作成しています(結局、インターネット経由で送信されるパブリックはこれ以上ありません:-)。

とは言っても、Lombok's @Dataはゲッター/セッターを生成するだけtoString()ではhashCode()ありequals(Object)ません。

そして、実際のプログラミングではカプセル化は本当に意味がありますか?

カプセル化は非常に貴重またはまったく役に立たない可能性があり、カプセル化されるオブジェクトによって異なります。一般に、クラス内のロジックが複雑になるほど、カプセル化の利点が大きくなります。

すべてのフィールドに対して自動的に生成されるゲッターとセッターは一般的に使いすぎますが、レガシーフレームワークを使用したり、フィールドでサポートされていないフレームワーク機能を使用したりするのに役立ちます。

カプセル化は、ゲッターとコマンドメソッドで実現できます。セッターは単一のフィールドのみを変更することが予想されるため、通常は適切ではありませんが、不変式を維持するには複数のフィールドを一度に変更する必要がある場合があります。

概要

ゲッター/セッターは、カプセル化がかなり不十分です。

Javaでのゲッター/セッターの普及は、プロパティに対する言語レベルのサポートの欠如、および多くの教材やそれらによって教えられているプログラマーにまつわる歴史的なコンポーネントモデルでの疑わしい設計選択に起因しています。

EcmaScriptなどの他のオブジェクト指向言語は、言語レベルでプロパティをサポートしているため、APIを壊すことなくゲッターを導入できます。このような言語では、ゲッターを実際に必要なときに導入することができます。前日ではなく、1日で必要になる場合があります。これにより、はるかに快適なプログラミングが可能になります。


1
不変条件を強制しますか?英語ですか?英語以外の人に説明してもらえますか?複雑になりすぎないでください。ここの一部の人々は英語を十分に理解していません。
ガンヌス

私はあなたの基礎が好きです、あなたの考えは好きです(+1)が、実際の結果は好きではありません。リフレクションによってデータをロードするために、私はプライベートフィールドと特別なライブラリを使用します。私があなたの答えを私に対する議論として設定している理由がわからないだけですか?
ガンヌス

@Gangnus 不変式は、変化しない論理条件です(意味は変化せず、-variantの意味は変化/変化します)。多くの関数には、実行前と実行後(前提条件および事後条件と呼ばれる)に真でなければならないルールがあります。そうでない場合、コードにエラーがあります。たとえば、関数は引数がnullでないこと(前提条件)を必要とし、引数がnullの場合は例外がスローされる可能性があります。不変)。
ファラプ

@Gangus:この議論のどこに反省が入るかはよくわかりません。Lombokは注釈プロセッサ、つまりコンパイル中に追加のコードを生成するJavaコンパイラのプラグインです。ただし、ロンボクは使用できます。多くの場合、パブリックフィールドは同様に機能し、セットアップが簡単であると言っているだけです(すべてのコンパイラが注釈プロセッサを自動的に検出するわけではありません...)。
メリトン

1
@Gangnus:不変式は数学とCSで同じですが、CSには追加の観点があります。オブジェクトの観点からは、不変式を適用して確立する必要があります。そのオブジェクトの呼び出し元から見ると、不変式は常に真です。
メリトン

7

私は確かにその質問を自問しました。

しかし、それは完全に真実ではありません。ゲッター/セッターIMOの普及は、それを必要とするJava Bean仕様が原因です。そのため、オブジェクト指向プログラミングではなく、Bean指向プログラミングの機能です。2つの違いは、それらが存在する抽象化層の違いです。Beanは、システムインターフェースに近い、つまり上位層にあります。それらはオブジェクト指向の基盤から抽象化されるか、少なくとも意図されています-いつものように、物事は十分に頻繁に追いやられています。

Javaプログラミングに遍在しているこのBeanには、対応するJava言語機能が追加されていないのは少し不幸だと思います。C#のPropertiesコンセプトのようなものを考えています。それを知らない人にとっては、次のような言語構成体です。

class MyClass {
    string MyProperty { get; set; }
}

とにかく、実際の実装の要点は、カプセル化の恩恵を依然として受けています。


Pythonには、プロパティに透明なゲッター/セッターを設定できる同様の機能があります。同様の機能を備えた言語がさらに多くあると確信しています。
JAB

1
「ゲッター/セッターIMOの普及は、それを必要とするJava Bean仕様が原因です」はい、あなたは正しいです。そして誰がそれが必要だと言ったのですか?理由は?
ガンヌス

2
C#の方法は、Lombokとまったく同じです。本当の違いは見当たりません。より実用的な利便性ですが、明らかに構想が悪いです。
ガンヌス

1
@Gangnis仕様ではそれが必要です。どうして?ゲッターがフィールドを公開することと同じではない理由の具体例については、私の答えをご覧ください。ゲッターなしで同じことを達成してみてください。
ビンスエミー

@VinceEmigh gangnUs、どうぞ:-)。(ギャングウォーキング、ヌー-ナット)。そして、特に必要な場所でのみget / setを使用し、他の場合にはリフレクションによってプライベートフィールドに大量にロードするのはどうでしょうか。
ガンヌス

6

誰かが私に説明することができますか?すべてのフィールドをプライベートとして非表示にし、その後いくつかの余分な技術ですべてを公開する意味は何ですか?なぜパブリックフィールドだけを使用しないのですか?スタート地点に戻るためだけに長く困難な道を歩んだと感じています。

ここでの簡単な答えは、あなたは絶対に正しいということです。ゲッターとセッターは、カプセル化の値のほとんど(すべてではない)を排除します。これは、カプセル化を解除するgetおよび/またはsetメソッドがあるときはいつでも、クラスのすべてのプライベートメンバーにアクセッサを盲目的に追加する場合、間違っていると言うことではありません。

はい、ゲッターとセッターを介して機能する他のテクノロジーがあります。また、単純なパブリックフィールドでは使用できません。しかし、これらの技術が登場したのは、私たちがそれらの多くの特性を持っているからです-公共のゲッター/セッターの背後にあるプライベートフィールド。プロパティがなければ、これらの技術は別の方法で開発され、パブリックフィールドをサポートします。そして、すべてがシンプルになり、ロンボクは今は必要ありません。このサイクル全体の意味は何ですか?そして、実際のプログラミングではカプセル化は本当に意味がありますか?

ゲッターはセッターであり、Javaプログラミングではユビキタスです。JavaBeanの概念は、機能を事前に構築されたコードに動的にバインドする方法としてプッシュされたためです。たとえば、アプレットにフォームを作成して(誰でも覚えていますか?)、オブジェクトを検査し、すべてのプロパティを見つけてフィールドとして表示することができます。その後、UIはユーザー入力に基づいてこれらのプロパティを変更できます。開発者としてのあなたは、クラスを書くことを心配するだけで、そこに検証やビジネスロジックを配置します。

ここに画像の説明を入力してください

サンプルBeanの使用

これ自体はひどい考えではありませんが、私はJavaのアプローチの大ファンではありませんでした。それはちょうど穀物に反している。Python、Groovyなどを使用します。この種のアプローチをより自然にサポートするもの。

JavaBeanがJOBOLを作成したために制御不能になりました。つまり、OOを理解しないJavaで書かれた開発者です。基本的に、オブジェクトはデータの袋に過ぎず、すべてのロジックは長いメソッドで外部に記述されていました。それは普通と見られていたので、これに疑問を呈するあなたや私のような人々は、変人と見なされました。最近シフトを見ましたが、これはアウトサイダーのポジションではありません

XMLバインディングは非常に困難です。これは、おそらくJavaBeansに対抗するための良い戦場ではありません。これらのJavaBeansをビルドする必要がある場合は、実際のコードに近づけないようにしてください。それらをシリアル化レイヤーの一部として扱います。


2
最も具体的で賢明な考え。+1。しかし、クラスのグループを作成しないのはなぜですか?クラスのすべてが、外部フィールドとの間でプライベートフィールドを大量にロード/保存しますか?JSON、XML、その他のオブジェクト。POJOで動作する場合もあれば、そのための注釈でマークされたフィールドのみで動作する場合もあります。今、私はその代替案について考えています。
ガンヌス

@Gangnus推測で、それは多くの余分なコード記述を意味するからです。リフレクションが使用される場合、シリアル化コードの1つの塊のみを記述する必要があり、特定のパターンに従う任意のクラスをシリアル化できます。C#は、[Serializable]属性とその親類を通じてこれをサポートします。リフレクションを活用して1つだけを記述できるのに、なぜ101個の特定のシリアル化メソッド/クラスを記述するのですか?
ファラプ

@Pharapすみませんが、あなたのコメントは短すぎます。「反射の活用」?そして、非常に異なるものを1つのクラスに隠す意味は何ですか?どういう意味ですか?
ガンヌス

@Gangnus私はリフレクション、つまりコードが独自の構造を検査する能力を意味ます。Java(および他の言語)では、クラスが公開するすべての関数のリストを取得し、それらをXML / JSONなどにシリアル化するために必要なクラスからデータを抽出する方法として使用できます。クラスごとに構造を保存するための新しいメソッドを絶えず作成する代わりに、1回限りの作業になるリフレクションを使用します。ここだ、単純なJavaの例
ファラプ

@Pharapそれはまさに私が話していたものです。しかし、なぜそれを「レバレッジ」と呼ぶのでしょうか?そして、私がここで最初のコメントで言及した代替案は残ります-(非)パックされたフィールドは特別な注釈を持つべきですか?
ガンヌス

5

ゲッターなしでどれくらいできるでしょうか?それらを完全に削除することは可能ですか?これはどのような問題を引き起こしますか?returnキーワードを禁止することさえできますか?

あなたが仕事をする気があるなら、あなたはたくさんできることがわかります。それでは、この完全にカプセル化されたオブジェクトから情報がどのように取得されるのでしょうか?協力者を通して。

コードに質問させるのではなく、物事を行うように指示します。それらも返されない場合、返されるものについて何もする必要はありません。そのため、return代わりに手を差し伸べることを考えている場合は、出力ポートのコラボレーターに手を差し伸べてください。

このように物事を行うことには、利点と結果があります。あなたはただあなたが戻ってきたであろうこと以上のものについて考えなければなりません。それを要求していないオブジェクトにメッセージとして送信する方法を考える必要があります。返されたのと同じオブジェクトを渡すか、単にメソッドを呼び出すだけで十分かもしれません。その思考を行うにはコストがかかります。

利点は、インターフェイスに向かって話をしていることです。つまり、抽象化のメリットを最大限に活用できます。

また、ポリモーフィックなディスパッチも提供します。これは、あなたが言っていることを知っている間は、正確に何を言っているのかを知る必要がないからです。

これは、レイヤーのスタックを一方向に進むだけでよいと考えるかもしれませんが、多態性を使用して、狂った循環依存関係を作成せずに後方に移動できることがわかります。

次のようになります。

ここに画像の説明を入力してください

class Interactor implements InputPort {
    OutputPort out;
    int y = 0;

    Interactor(OutputPort out){
        this.out = out;
    }

    void accumulate(int x) {
        y = y + x;
        out.showAsImage(y);
    }
}

そのようなコードを作成できる場合は、ゲッターを使用することを選択します。必要な悪ではありません。


はい、そのようなゲッター/セッターには大きな意味があります。しかし、あなたはそれが何か他のものであると理解していますか?:-)とにかく+1。
ガンヌス

1
@Gangnusありがとう。あなたが話していることができるものがたくさんあります。少なくともそれらに名前を付けたいと思うなら、私はそれらに入ることができます。
candied_orange

5

これは議論の余地のある問題です(ご覧の通り)。多数のドグマと誤解がゲッターとセッターの問題に関する合理的な懸念と混同されるためです。しかし、要するに、何も問題はなく、@Dataカプセル化を壊しません。

パブリックフィールドではなくゲッターとセッターを使用する理由

ゲッター/セッターがカプセル化を提供するためです。値をパブリックフィールドとして公開し、後でその場で値の計算に変更する場合、フィールドにアクセスするすべてのクライアントを変更する必要があります。これは明らかに悪いことです。オブジェクトの一部のプロパティがフィールドに格納されるか、その場で生成されるか、他の場所からフェッチされるかは実装の詳細であるため、その違いはクライアントに公開されません。ゲッター/セッターセッターは、実装を隠すため、これを解決します。

しかし、ゲッター/セッターが基礎となるプライベートフィールドを単に反映している場合、それは同じくらい悪いことではありませんか?

番号!ポイントは、カプセル化により、クライアントに影響を与えずに実装を変更できることです。クライアントが知る必要も気にする必要もない限り、フィールドは依然として価値を保存するための完全に素晴らしい方法かもしれません。

しかし、カプセル化を破るフィールドからゲッター/セッターを自動生成していませんか?

カプセル化はまだありません!@Data注釈は、基礎となるフィールドを使用するゲッター/セッターペアを記述する便利な方法です。クライアントの観点からは、これは通常のゲッター/セッターのペアのようです。実装を書き直すことにした場合でも、クライアントに影響を与えずにそれを実行できます。したがって、カプセル化簡潔な構文の両方の長所を活用できます。

しかし、ゲッター/セッターは常に悪いと言う人もいます!

根本的な実装に関係なく、ゲッター/セッターパターンは常に悪いと考える人もいます。オブジェクトから値を設定したり取得したりするのではなく、オブジェクト間の相互作用を、あるオブジェクト別のオブジェクトに何かを要求するメッセージとしてモデル化する必要があるという考え方です。これは主に、オブジェクト指向思考の初期のドグマです。現在の考え方では、特定のパターン(値オブジェクト、データ転送オブジェクトなど)については、ゲッター/セッターが完全に適切である可能性があります。


カプセル化により、ポリモーフィズムと安全性が可能になります。取得/設定は、もみを許可しますが、2番目に強く反対します。
ガンヌス

私がどこかでドグマを使っていると言ったら、私のテキストはドグマです。そして、私が使用しているいくつかの考えが好きではない、または同意しないことを忘れないでください。私はそれらを矛盾の実証に使用しています。
ガンヌス

1
「GangNus」:-)。1週間前、私は文字通り複数の層にまたがるゲッターとセッターを呼び出すプロジェクトに取り組んできました。それは絶対に安全ではなく、さらに悪いことに、安全でないコーディングの人々をサポートします。人々がその方法に慣れるので、私は十分に速く仕事を変えたことがうれしいです。だから、私はそれを考えていない、私はそれ見る
ガンヌス

2
@jrh:正式な説明があるかどうかはわかりませんが、C#には、プロパティ(より良い構文のゲッター/セッター)およびプロパティのみで動作のないタイプである匿名タイプの組み込みサポートがあります。そのため、C#の設計者は、メッセージングのメタファーと「教えてはいけない」という原則から意図的に逸脱しています。バージョン2.0以降のC#の開発は、従来のオブジェクト指向の純度よりも関数型言語に触発されているようです。
ジャックB

1
@jrh:私は今推測していますが、C#はデータと操作がより分離されている関数型言語に非常に影響を受けていると思います。分散アーキテクチャでは、視点もメッセージ指向(RPC、SOAP)からデータ指向(REST)に移行しました。そして、データ(「ブラックボックス」オブジェクトではない)を公開および操作するデータベースが普及しています。要するに、私はフォーカスが公開されたデータモデルにブラックボックス間のメッセージからずれていると思います
JacquesB

3

カプセル化には目的がありますが、誤用または悪用される可能性もあります。

数十(数百ではないにしても)のフィールドを持つクラスを持つAndroid APIのようなものを考えてください。これらのフィールドをAPIのコンシューマーに公開すると、ナビゲートおよび使用が難しくなります。また、ユーザーは、フィールドの使用方法と競合する可能性のあるフィールドを使用して、好きなことを実行できるという誤った概念をユーザーに与えます。その意味で、カプセル化は、保守性、使いやすさ、読みやすさ、およびクレイジーなバグを回避するという意味で優れています。

一方、PODまたはすべてのフィールドがパブリックであるC / C ++の構造体のような単純な古いデータ型も有用です。Lombokの@dataアノテーションによって生成されるような、役に立たないゲッター/セッターを持つことは、「カプセル化パターン」を維持するための単なる方法です。Javaで「役に立たない」ゲッター/セッターを行う数少ない理由の1つは、メソッドがコントラクトを提供することです。

Javaでは、インターフェイスにフィールドを含めることはできません。したがって、ゲッターとセッターを使用して、そのインターフェイスのすべての実装者が持つ共通のプロパティを指定します。KotlinやC#などの最近の言語では、プロパティの概念は、セッターとゲッターを宣言できるフィールドとして認識されています。結局のところ、役に立たないゲッター/セッターは、Oracleがプロパティを追加しない限り、Javaと共に生きなければならないレガシーです。たとえば、Ketlinは、JetBrainsによって開発された別のJVM言語であり、Lombokで@dataアノテーションが行うことを基本的に行うデータクラスを持っています。

また、いくつかの例を示します。

class DataClass 
{
    private int data;

    public int getData() { return data; }
    public void setData(int data) { this.data = data; } 
}

これはカプセル化の悪いケースです。ゲッターとセッターは事実上役に立ちません。これはJavaなどの言語の標準であるため、カプセル化が主に使用されます。コードベース全体の一貫性を維持することに加えて、実際には役立ちません。

class DataClass implements IDataInterface
{
    private int data;

    @Override public int getData() { return data; }
    @Override public void setData(int data) { this.data = data; }
}

これはカプセル化の良い例です。カプセル化は、契約(この場合はIDataInterface)を実施するために使用されます。この例のカプセル化の目的は、このクラスのコンシューマーに、インターフェースによって提供されるメソッドを使用させることです。ゲッターとセッターは派手なことは何もしませんが、DataClassとIDataInterfaceの他の実装者の間で共通の特徴を定義しました。したがって、私はこのようなメソッドを持つことができます:

void doSomethingWithData(IDataInterface data) { data.setData(...); }

さて、カプセル化について話すとき、構文の問題も解決することが重要だと思います。カプセル化自体ではなく、カプセル化を実施するために必要な構文について人々が不平を言うのをよく見ます。頭に浮かぶ1つの例は、ケーシームラトリからです(彼の暴言はこちらで確認できます)。

カプセル化を使用するプレイヤークラスがあり、自分の位置を1ユニットずつ移動したいとします。コードは次のようになります。

player.setPosX(player.getPosX() + 1);

カプセル化を行わないと、次のようになります。

player.posX++;

ここで彼は、カプセル化は利点を追加せずにさらに多くのタイピングにつながると主張し、これは多くの場合真実であるが、何かに気付く。引数は、カプセル化自体ではなく構文に反します。カプセル化の概念を欠くCのような言語でさえ、 '_'または 'my'またはそれらがAPIのコンシューマーによって使用されるべきではないことを示すもので接頭辞または固定された構造体の変数をしばしば見るでしょう民間。

問題の事実は、カプセル化により、コードのメンテナンス性と使いやすさが向上することです。このクラスを検討してください:

class VerticalList implements ...
{
    private int posX;
    private int posY;
    ... //other members

    public void setPosition(int posX, int posY)
    {
        //change position and move all the objects in the list as well
    }
}

この例で変数がパブリックである場合、このAPIのコンシューマーは、posXおよびposYを使用するタイミングとsetPosition()を使用するタイミングについて混乱します。これらの詳細を非表示にすることで、消費者が直感的にAPIをよりよく使用できるようになります。

ただし、構文は多くの言語の制限事項です。ただし、新しい言語には、publiceメンバーの素晴らしい構文とカプセル化の利点を提供するプロパティがあります。MSVCを使用している場合、C ++、さらにはC ++でも、C#、Kotlinにプロパティがあります。Kotlinの例を次に示します。

クラスVerticalList:... {var posX:Int set(x){field = x; ...} var posY:Int set(y){field = y; ...}}

ここでは、Javaの例と同じことを達成しましたが、posXとposYをパブリック変数であるかのように使用できます。ただし、それらの値を変更しようとすると、セッターの本体set()が実行されます。

たとえば、Kotlinでは、これはgetter、setter、hashcode、equals、toStringが実装されたJava Beanと同等です。

data class DataClass(var data: Int)

この構文により、Java Beanを1行で実行できることに注意してください。Javaのような言語がカプセル化の実装に問題があることに気づきましたが、それはカプセル化自体ではなくJavaの欠点です。

Lombokの@Dataを使用してゲッターとセッターを生成すると言いました。@Dataという名前に注意してください。これは主に、データのみを保存するデータクラスで使用されることを意図しており、シリアル化および逆シリアル化されることを意図しています。ゲームの保存ファイルのようなものを考えてください。しかし、UI要素のような他のシナリオでは、変数の値を変更するだけでは期待される動作を得るのに十分でない可能性があるため、最も明確にセッターが必要です。


カプセル化の誤用について例を挙げてください。それは面白いかもしれません。あなたの例は、カプセル化の欠如に反対です。
ガンヌス

1
@Gangnusいくつかの例を追加しました。役に立たないカプセル化は一般に誤用であり、私の経験から、API開発者はAPIを特定の方法で使用するように無理やり努力しています。簡単なプレゼンテーションがなかったので、そのための例を挙げませんでした。見つかったら、追加します。カプセル化に対するコメントのほとんどは、実際にはカプセル化自体ではなく、カプセル化の構文に反していると思います。
BananyaDev

編集してくれてありがとう。ただし、「public」ではなく「publice」という小さなタイプミスを見つけました。
jrh

2

カプセル化により柔軟性が得られます。構造とインターフェースを分離することにより、インターフェースを変更せずに構造を変更できます。

たとえば、構築時に基礎となるフィールドを初期化するのではなく、他のフィールドに基づいてプロパティを計算する必要があることがわかった場合は、ゲッターを単に変更できます。フィールドを直接公開していた場合、代わりにインターフェースを変更し、すべての使用サイトで変更を行う必要があります。


理論上は良い考えですが、APIのバージョン2にバージョン1の例外をスローさせても、ライブラリまたはクラスのユーザーがひどく(そしておそらく静かに!)私は、これらのデータクラスのほとんどで「舞台裏」で大きな変更を加えることができるのではないかと少し疑っています。
jrh

申し訳ありませんが、説明ではありません。インターフェイスでフィールドの使用が禁止されているという事実は、カプセル化のアイデアに由来しています。そして今、私たちはそれについて話している。あなたは議論されている事実に基づいています。(注意、私はあなたの考えを賛否両論ではありません。ここでの議論としては使用できません)
ガンヌス

@Gangnus「インターフェイス」という言葉は、Javaキーワード以外の意味を持っています:dictionary.com/browse/interface-glennsl
14:17

@jrhそれはまったく別の問題です。特定のコンテキストでのカプセル化反対するためにこれを使用できますが、その引数を無効にすることはありません。
-glennsl

1
一括取得/設定なしの古いカプセル化は、ポリモーフィズムだけでなく、安全性ももたらしました。そして、大量のセット/取得は、悪い習慣に向かって教えてくれます。
ガンヌス

2

カプセル化とクラス設計の問題空間を説明し、最後に質問に答えようとします。

他の回答で述べたように、カプセル化の目的は、コントラクトとして機能するパブリックAPIの背後にオブジェクトの内部詳細を隠すことです。オブジェクトは、パブリックAPIを介してのみ呼び出されることがわかっているため、内部を変更しても安全です。

パブリックフィールド、ゲッター/セッター、またはより高レベルのトランザクションメソッドまたはメッセージパッシングを使用することが理にかなっているかどうかは、モデル化されているドメインの性質に依存します。Akka Concurrencyという本(これは多少古くなっていてもお勧めできます)には、これを説明する例がありますが、ここでは省略します。

Userクラスを考えてみましょう:

public class User {
  private String first = "";
  private String last = "";

  public String getFirstName() {
    return this.first;
  }
  public void setFirstName(String s) {
    this.first = s;
  }

  public String getLastName() {
    return this.last;
  }
  public void setLastName(String s) {
    this.last = s;
  }
}

これは、シングルスレッドのコンテキストでうまく機能します。モデル化されるドメインは人の名前であり、その名前の格納方法の仕組みはセッターによって完全にカプセル化できます。

ただし、これはマルチスレッドコンテキストで提供する必要があることを想像してください。1つのスレッドが定期的に名前を読み取っていると仮定します。

System.out.println(user.getFirstName() + " " + user.getLastName());

そして、他の2つのスレッドが綱引きと戦っており、ヒラリークリントンドナルドトランプを順番に設定します。それぞれ2つのメソッドを呼び出す必要があります。ほとんどはこれで問題ありませんが、時々ヒラリートランプドナルドクリントンが通り過ぎるのを見ることがあります。

ロックは名または姓のいずれかを設定している間だけ保持されるため、セッター内にロックを追加してもこの問題を解決することはできません。ロックによる唯一の解決策は、オブジェクト全体にロックを追加することですが、呼び出し側のコードがロックを管理する必要があるため(そしてデッドロックを引き起こす可能性があるため)、カプセル化が解除されます。

結局のところ、ロックによるクリーンなソリューションはありません。クリーンな解決策は、内部をより粗くすることで内部を再度カプセル化することです。

public class UserName {
   public final String first;
   public final String last;
   public UserName(String first, String last) { ... }
}

public class User
   private UserName name;
   public UserName getName() { return this.name; }
   public setName(UserName n) { this.name = n; }
}

名前自体は不変になり、そのメンバーはパブリックになることができます。これは、作成後は変更できない純粋なデータオブジェクトだからです。同様に、UserクラスのパブリックAPIはより粗くなり、セッターが1つだけ残っているため、名前は全体としてのみ変更できます。APIの背後にある内部状態の多くをカプセル化します。

このサイクル全体の意味は何ですか?そして、実際のプログラミングではカプセル化は本当に意味がありますか?

このサイクルで見られるのは、特定の状況に適したソリューションを広範に適用しようとする試みです。適切なレベルのカプセル化には、モデル化されるドメインを理解し、適切なレベルのカプセル化を適用する必要があります。これは、すべてのフィールドがパブリックであることを意味する場合があります(Akkaアプリケーションのように)場合によっては、メッセージを受信する単一のメソッドを除いて、パブリックAPIがまったくないことを意味します。ただし、カプセル化の概念自体は、安定したAPIの背後に内部を隠すことを意味し、特にマルチスレッドシステムでは、大規模なプログラミングソフトウェアの鍵となります。


素晴らしい。あなたは議論をより高いレベルに上げたと思います。たぶん2。本当に、私はカプセル化を理解し、時には標準的な本よりも優れていると思っていました。そして、受け入れられたカスタムと技術のために実際にそれを失うことを本当に恐れていました。それが私の質問の本当の主題でした(おそらく、良い方法)、しかし、あなたは私にカプセル化の本当に新しい側面を示しました。私は間違いなくマルチタスクが得意ではありませんが、他の基本原則を理解するのにそれがどれほど有害であるかを示してくれました。
ガンヌス

しかし、私はあなたのポストマークすることはできません、それはオブジェクトへの/からの大量のデータのロード、豆、ロンボク島などのプラットフォームが出現により、先の問題に関するものではありませんため、答えを。もしかしたら、時間があるなら、その方向で考えを練ることができますか?私自身は、あなたの考えがその問題にもたらす結果を再考していません。そして、私はそれで十分かどうか確信が持てません(覚えておいてください、悪いマルチスレッドの背景:-[)
Gangnus

ロンボクは使用していませんが、理解しているように、特定のレベルのカプセル化(すべてのフィールドのゲッター/セッター)を少ない入力で実装する方法です。問題のあるドメインのAPIの理想的な形を変えるものではなく、より迅速に記述するためのツールにすぎません。
ジョーリセブレヒト

1

これが理にかなっているユースケースを考えることができます。元々は単純なgetter / setter APIを介してアクセスするクラスがあります。後で同じフィールドを使用しないように拡張または変更しますが、同じAPIを引き続きサポートします

幾分不自然な例:デカルトと対として開始ポイントp.x()p.y()。後で極座標を使用して新しい実装やサブクラスを作るので、あなたも呼び出すことができますp.r()p.theta()、しかし呼び出すクライアントコードp.x()p.y()有効です。クラス自体は、内部の極形式から透過的に変換されy()ますreturn r * sin(theta);。つまり、now になります。(この例では、設定するだけであるx()y()、あまり意味がありませんが、それでも可能です。)

この場合、「フィールドを公開するのではなく、ゲッターとセッターを自動的に宣言するのが面倒でしたか、そこでAPIを壊さなければならなかった」と言うかもしれません。


2
見た目ほど良くありません。内部の物理表現を変更すると、オブジェクトが実際に異なります。また、品質が異なるため、同じように見えるのは悪いことです。デカルトシステムには特別なポイントはありませんが、極座標システムには特別なポイントがあります。
ガンヌス

1
その特定の例が気に入らなければ、確かに、複数の同等の表現を実際に持っている他のもの、または古い表現との間で変換できる新しい表現を考えることができます。
デイビスラー

はい、非常に生産的にプレゼンテーションに使用できます。UIでは、広く使用できます。でも、自分の質問に答えさせてくれないの?:
ガンヌス

その方が効率的ですね。:)
ダビスラー

0

誰かが私に説明することができますか?すべてのフィールドをプライベートとして非表示にし、その後いくつかの余分な技術ですべてを公開する意味は何ですか?

まったく意味がありません。ただし、その質問をするという事実は、Lombokが何をするのか理解していないこと、およびカプセル化を使用してオブジェクト指向コードを記述する方法を理解していないことを示しています。少し巻き戻しましょう...

クラスインスタンスの一部のデータは常に内部であり、決して公開されるべきではありません。クラスインスタンスの一部のデータは外部で設定する必要があり、一部のデータはクラスインスタンスから戻す必要があります。ただし、クラスの表面下での処理方法を変更したい場合があるため、関数を使用してデータを取得および設定できます。

一部のプログラムは、クラスインスタンスの状態を保存する必要があるため、これらには何らかのシリアル化インターフェイスが含まれている場合があります。クラスインスタンスがその状態をストレージに保存し、その状態をストレージから取得できるようにする関数を追加します。クラスインスタンスはそれ自体のデータを制御しているため、これによりカプセル化が維持されます。プライベートデータをシリアル化している可能性がありますが、プログラムの残りの部分はアクセスできません(より正確には、プライベートデータを故意に破損しないように選択することでチャイニーズウォールを維持します)。逆シリアル化の整合性チェックを実行して、データが正常に戻ったことを確認します。

データには、範囲チェック、整合性チェックなどが必要な場合があります。これらの関数を自分で記述することで、すべてを行うことができます。この場合、私たちはすべて自分でやっているので、ロンボクは必要ありません。

しかし、多くの場合、外部で設定されたパラメーターは単一の変数に保存されます。この場合、その変数の内容を取得/設定/シリアライズ/デシリアライズするために4つの関数が必要になります。これらの4つの関数を自分で記述するたびに速度が低下し、エラーが発生しやすくなります。Lombokでプロセスを自動化すると、開発が高速化され、エラーの可能性がなくなります。

はい、その変数を公開することは可能です。この特定のバージョンのコードでは、機能的に同一です。しかし、関数を使用している理由に戻ります。「クラスの表面下での処理方法を変更したい場合があります...」変数をパブリックにすると、このパブリック変数がインターフェース。ただし、関数を使用する場合、またはLombokを使用してこれらの関数を自動的に生成する場合は、将来いつでも基になるデータと基になる実装を自由に変更できます。

これにより明確になりますか?


あなたは1年目のSW学生の質問について話している。すでに他の場所にいます。あなたがあなたの答えの形でそれほど失礼ではなかったなら、私はこれを決して言いません、そしてあなたに賢明な答えにプラスさえ与えます。
ガンヌス

0

私は実際にはJava開発者ではありません。しかし、以下はほとんどプラットフォームに依存しません。

私たちが書いたほぼすべてのものは、プライベート変数にアクセスするパブリックゲッターとセッターを使用します。ゲッターとセッターのほとんどは簡単です。しかし、セッターが何かを再計算する必要がある、またはセッターが何らかの検証を行う、またはプロパティをこのクラスのメンバー変数のプロパティに転送する必要があると判断した場合、これはコード全体に対して完全に非破壊的であり、バイナリ互換性があるため、その1つのモジュールを交換できます。

このプロパティを実際にオンザフライで計算する必要があると判断した場合、それを見るすべてのコードを変更する必要はなく、それに書き込むコードだけを変更する必要があり、IDEがそれを見つけることができます。書き込み可能な計算フィールドであると判断した場合(これを数回行う必要がありました)、それも実行できます。良い点は、これらの変更の多くがバイナリ互換であることです(読み取り専用の計算フィールドへの変更は理論上ではありませんが、とにかく実際に行われる可能性があります)。

最終的には、複雑なセッターを備えた多くの些細なゲッターになりました。また、かなりの数のキャッシングゲッターができました。最終結果として、ゲッターは合理的に安価であるが、セッターはそうでない可能性があると仮定することができます。一方で、セッターがディスクに保持されないことを判断するのに十分な賢明です。

しかし、すべてのメンバー変数をプロパティに盲目的に変更していた男を追跡する必要がありました。彼はアトミックアドが何であるかを知らなかったため、実際にはパブリック変数である必要があるものをプロパティに変更し、コードを微妙に壊しました。


Javaの世界へようこそ。(C#も)。*はぁ*。ただし、内部表現を非表示にするためにget / setを使用する場合は注意してください。それは外部形式のみに関するものであり、問​​題ありません。しかし、それが数学、またはさらに悪いことに物理学またはその他の現実のものである場合、異なる内部表現は異なる品質を持ちます。viz softwareengineering.stackexchange.com/a/358662/44104
Gangnus

@Gangnus:そのポイントの例は最も残念です。これらの多くはありましたが、計算は常に正確でした。
ジョシュア

-1

ゲッターとセッターは、開発プロセス中に内部構造またはアクセス要件が変更された場合に将来のリファクタリングを回避するために追加された「念のため」の手段です。

リリースの数か月後に、クライアントは、クラスのフィールドの1つが負の値に設定される場合があることを通知します。パブリックフィールドを使用すると、コードベース全体でこのフィールドのすべての割り当てを検索して、設定する値にクランプ機能を適用する必要があります。このフィールドを変更するときは、常にそれを行う必要があることに注意してください。しかし、それは最悪です。代わりに、ゲッターとセッターをすでに使用している場合は、setField()メソッドを変更するだけで、このクランプが常に適用されるようにすることができます。

現在、Javaのような「時代遅れの」言語の問題は、この目的のためにメソッドの使用を奨励していることです。これにより、コードが無限に冗長になります。書くのは苦痛で読みにくいので、この問題を何らかの形で軽減するIDEを使用しているのです。ほとんどのIDEは、ゲッターとセッターを自動的に生成し、特に指示がない限り非表示にします。Lombokはさらに一歩進んで、コンパイル時に手続き的に生成するだけで、コードをより洗練された状態に保ちます。ただし、他の最新の言語では、この問題を何らかの方法で単純に解決しています。たとえば、Scalaまたは.NET言語

たとえば、VB .NETまたはC#では、単純な-副作用なし-ゲッターとセッターを単にパブリックフィールドにするためのすべてのフィールドを作成し、後でプライベートにし、名前を変更し、以前の名前のプロパティを公開することができます必要に応じて、フィールドのアクセス動作を微調整できるフィールド。Lombokを使用すると、getterまたはsetterの動作を微調整する必要がある場合、必要に応じてこれらのタグを削除し、新しい要件で独自のコードを作成できます。他のファイルでは何もリファクタリングする必要はありません。

基本的に、メソッドがフィールドにアクセスする方法は、透明で統一されている必要があります。現代の言語では、フィールドの同じアクセス/呼び出し構文で「メソッド」を定義できるため、これらの変更は開発の初期段階ではあまり考慮せずにオンデマンドで実行できますが、Javaでは事前にこの作業を行う必要がありますこの機能はありません。使用している言語では「念のために」メソッドの時間を節約したくなかったため、Lombokでできることは時間を節約することだけです。


それらの「現代」の言語では、APIは、のように見えるフィールドアクセスfoo.barが、それは可能メソッドの呼び出しによって処理されます。これは、API メソッド呼び出しのように見せかける「時代遅れの」方法よりも優れていると主張していますfoo.getBar()。パブリックフィールドには問題があることに同意するようですが、API全体が対称的である(すべてのメソッド呼び出しである)ため、「時代遅れの」代替は「現代の」代替よりも優れていると主張します。「現代の」アプローチでは、どのものをプロパティにし、どのものをメソッドにする必要があるかを決定する必要があります。これはすべてを複雑にします(特にリフレクションを使用する場合)。
ウォーボ

1
1.物理を隠すことは、必ずしもそれほど良いとは限りません。softwareengineering.stackexchange.com/a/358662/44104への私のコメントを見てください。2.ゲッター/セッターの作成がどれほど難しいか簡単かについては話していない。C#には、私たちが話している問題とまったく同じ問題があります。大量の情報の読み込みの悪い解決策によるカプセル化の欠如。3.はい、解決策は構文に基づくことができますが、あなたが言及しているのとは異なる方法でお願いします。それらは悪いです、ここには説明でいっぱいの大きなページがあります。4.ソリューションは構文に基づく必要はありません。Javaの制限で行うことができます。
ガンヌス

-1

誰かが私にすべてのフィールドをプライベートとして非表示にし、その後いくつかの余分な技術でそれらのすべてを公開するという意味を説明できますか?なぜパブリックフィールドのみを使用しないのですか?出発点に戻るためだけに長く困難な道を歩んだと思います。

ええ、それは矛盾しています。最初にVisual Basicのプロパティに遭遇しました。それまで、私の経験では、フィールドの周りにプロパティラッパーはありませんでした。パブリック、プライベート、および保護フィールドのみ。

プロパティは一種のカプセル化です。Visual Basicのプロパティは、1つ以上のフィールドの出力を制御および操作する方法であり、明示的なフィールドとそのデータ型さえ隠し、たとえば日付を特定の形式の文字列として出力する方法であると理解しました。しかし、それでも大きなオブジェクトの観点から「状態を隠して機能を公開する」ことはありません。

しかし、プロパティは、プロパティゲッターとセッターが分離されているため、正当化されます。プロパティなしでフィールドを公開することは、すべてかゼロでした。それを読むことができれば、それを変更できます。したがって、保護されたセッターを使用して、弱いクラスの設計を正当化できます。

では、なぜ実際のメソッドを使用しなかったのでしょうか?それはVisual Basic(およびVBScript)(oooh!aaah!)であり、大衆向けのコーディング(!)であり、それは大流行でした。したがって、最終的にはばかげた政治が支配した。


はい、UIのプロパティには特別な意味があり、パブリックでフィールドのきれいな表現です。いい視点ね。
ガンヌス

「では、なぜ実際のメソッドを使用しなかったのですか?」技術的には、.Netバージョンで行いました。プロパティは、get関数およびset関数の構文糖衣です。
ファラプ

"idiotocracy"?「特異性」という意味ではないですか?
ピーターモーテンセン

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