ゲッターとセッターをどのように回避しますか?


85

私は、オブジェクト指向の方法でクラスを設計するのに苦労しています。オブジェクトは、データではなく動作を公開することを読みました。したがって、ゲッター/セッターを使用してデータを変更するのではなく、特定のクラスのメソッドは「動詞」またはオブジェクトで動作するアクションでなければなりません。たとえば、「Account」オブジェクトには、etcなどWithdraw()Deposit()はなくメソッドなどがありますsetAmount()ゲッターメソッドとセッターメソッドが悪である理由を参照してください。

たとえば、Name、DOB、Tel、Addressなど、顧客に関する多くの情報を保持するCustomerクラスがある場合、これらすべての属性を取得および設定するゲッター/セッターを回避するにはどうすればよいでしょうか?すべてのデータを取り込むためにどのような「動作」タイプのメソッドを記述できますか?




8
Java Beans仕様に対処する必要ある場合、ゲッターとセッターを持つことになります。多くのことはJava Bean(jspの表現言語)を使用しており、そのようなことを避けようとすることは、おそらく困難です。

4
...そして、MichaelTとは反対の観点から:JavaBeans仕様を使用していない場合(および必要なコンテキストでオブジェクトを使用している場合を除き、個人的には避けるべきだと思います)、必要はありません特に対応するセッターを持たないプロパティの場合、ゲッターに対する「get」イボ。name()on Customerと呼ばれるメソッドは、と呼ばれるメソッドよりも明確または明確だと思いますgetName()
ダニエル・プライデン

2
@Daniel Pryden:名前は()を設定したり、取得するために、両方の意味することができます...?
IntelliData

回答:


55

かなりの数の回答とコメントで述べられているように、DTO 状況によって、特に境界を越えてデータを転送する場合(たとえば、JSONにシリアル化してWebサービス経由で送信する場合)に適切で便利です。この答えの残りの部分では、多かれ少なかれそれを無視して、ドメインクラス、およびゲッターとセッターを最小化(排除しないにしても)し、それでも大規模プロジェクトで役立つように設計する方法について説明します。また、ゲッターまたはセッターを削除する理由、または削除するタイミングについても説明しません。これらは独自の質問であるためです。

例として、プロジェクトがチェスや戦艦のようなボードゲームであると想像してください。これをプレゼンテーション層(コンソールアプリ、Webサービス、GUIなど)で表すさまざまな方法がありますが、コアドメインもあります。あなたが持つかもしれない1つのクラスはCoordinate、ボード上の位置を表します。「悪」の書き方は次のとおりです。

public class Coordinate
{
    public int X {get; set;}
    public int Y {get; set;}
}

(簡潔にするため、また、私はそれをよく知っているので、JavaではなくC#でコード例を作成します。それが問題ではないことを願っています。概念は同じで、翻訳は単純でなければなりません。)

セッターの削除:不変性

公開ゲッターとセッターはどちらも潜在的に問題がありますが、セッターはこの2つの「悪」です。通常、それらは除去するのが簡単です。このプロセスは、コンストラクター内から値を設定する単純なものです。以前にオブジェクトを変更したメソッドは、代わりに新しい結果を返す必要があります。そう:

public class Coordinate
{
    public int X {get; private set;}
    public int Y {get; private set;}

    public Coordinate(int x, int y)
    {
        X = x;
        Y = y;
    }
}

これは、XおよびYを変更するクラスの他のメソッドから保護されないことに注意してください。より厳密に不変にするには、readonlyfinalJavaで)を使用できます。ただし、プロパティを真に不変にするか、セッターを介して直接パブリックミューテーションを防止するだけでも、パブリックセッターを削除するトリックは実行されます。大多数の状況では、これはうまく機能します。

ゲッターの削除、パート1:動作の設計

上記はすべてセッターに適していますが、ゲッターに関しては、開始する前に実際に足を撃ちました。私たちのプロセスは、座標が何であるか(それが表すデータ)を考え、その周りにクラスを作成することでした。代わりに、座標から必要な動作から始める必要がありました。ちなみに、このプロセスはTDDによって支援されます。TDDでは、必要な場合にのみこのようなクラスを抽出するため、目的の動作から開始してそこから作業を行います。

だから、最初Coordinateに衝突を検出する必要があると思った場合、2つの部品がボード上の同じスペースを占有しているかどうかを確認したいとしましょう。これが「邪悪な」方法です(簡潔にするためにコンストラクタは省略されています)。

public class Piece
{
    public Coordinate Position {get; private set;}
}

public class Coordinate
{
    public int X {get; private set;}
    public int Y {get; private set;}
}

    //...And then, inside some class
    public bool DoPiecesCollide(Piece one, Piece two)
    {
        return one.X == two.X && one.Y == two.Y;
    }

そして、これが良い方法です:

public class Piece
{
    private Coordinate _position;
    public bool CollidesWith(Piece other)
    {
        return _position.Equals(other._position);
    }
}

public class Coordinate
{
    private readonly int _x;
    private readonly int _y;
    public bool Equals(Coordinate other)
    {
        return _x == other._x && _y == other._y;
    }
}

IEquatable実装は簡略化のために省略されています)。データをモデリングするのではなく、動作を設計することで、ゲッターを削除することができました。

これは例にも関連していることに注意してください。ORMを使用している場合や、Webサイトなどに顧客情報を表示している場合は、何らかのCustomerDTOが意味をなすでしょう。しかし、システムに顧客が含まれており、それらがデータモデルで表されているからといってCustomer、ドメインにクラスがある必要があることを自動的に意味するわけではありません。たぶん、振る舞いのために設計すると、1つが出現しますが、ゲッターを避けたい場合は、先制的に作成しないでください。

ゲッターの削除、パート2:外部の動作

したがって、上記のは良いスタートですが、遅かれ早かれ、あなたはおそらくあなたには、いくつかの方法で、クラスの状態に依存クラス、関連付けられている行動を持っているような状況に遭遇しますが、これは属していないクラス。このような動作は、通常、アプリケーションのサービス層に存在します。

このCoordinate例を使用すると、最終的にはユーザーにゲームを表現したいと思うでしょう。これは、画面に描画することを意味する場合があります。たとえば、Vector2画面上のポイントを表すために使用するUIプロジェクトがあるとします。ただし、Coordinateクラスが座標から画面上のポイントへの変換を担当することは不適切です。これにより、あらゆる種類のプレゼンテーションの問題がコアドメインに取り込まれます。残念ながら、このような状況はオブジェクト指向設計に固有のものです。

非常に一般的に選択される最初のオプションは、いまいましいゲッターを公開して、それで地獄に言うだけです。これには単純さという利点があります。しかし、ゲッターを避けることについて話しているので、議論のためにこれを拒否し、他にどんなオプションがあるか見てみましょう。

2番目のオプション.ToDTO()、クラスに何らかのメソッドを追加することです。とにかく、これまたは類似のものが必要になる可能性があります。たとえば、ゲームを保存する場合、ほとんどすべての状態をキャプチャする必要があります。しかし、サービスに対してこれを行うことと、ゲッターに直接アクセスすることの違いは、多かれ少なかれ見た目です。それにはまだ「悪」があります。

3つ目のオプション -Zoran Horvatが複数のPluralsightビデオで提唱しているのを見てきた- は、訪問者パターンの修正バージョンを使用することです。これはパターンの非常に珍しい使用とバリエーションであり、実際のゲインなしに複雑さを追加するのか、それとも状況の良い妥協であるのかによって、人々の走行距離は大きく変わると思います。基本的には、標準のビジターパターンを使用するという考え方ですが、Visitメソッドは、訪問するクラスではなく、必要な状態をパラメーターとして取得します。例はここにあります

私たちの問題では、このパターンを使用した解決策は次のとおりです。

public class Coordinate
{
    private readonly int _x;
    private readonly int _y;

    public T Transform<T>(IPositionTransformer<T> transformer)
    {
        return transformer.Transform(_x,_y);
    }
}

public interface IPositionTransformer<T>
{
    T Transform(int x, int y);
}

//This one lives in the presentation layer
public class CoordinateToVectorTransformer : IPositionTransformer<Vector2>
{
    private readonly float _tileWidth;
    private readonly float _tileHeight;
    private readonly Vector2 _topLeft;

    Vector2 Transform(int x, int y)
    {
        return _topLeft + new Vector2(_tileWidth*x + _tileHeight*y);
    }
}

あなたはおそらく言うことができる、通り_x_yされていない、本当にこれ以上をカプセル化。IPositionTransformer<Tuple<int,int>>それらを直接返すだけのを作成することで抽出できます。味によっては、これが運動全体を無意味にすると感じるかもしれません。

ただし、パブリックゲッターでは、データを直接引き出してTell、Do n't Askに違反して使用するだけで、間違った方法で物事を行うのは非常に簡単です。このパターンを使用すると、実際には正しい方法で実行する方が簡単です。動作を作成する場合は、それに関連付けられた型を作成することから自動的に開始されます。TDAの違反は非常に明らかに臭いであり、おそらくよりシンプルでより良いソリューションを回避する必要があります。実際には、これらのポイントは、ゲッターが奨励する「邪悪な」方法よりも、正しい方法でオブジェクト指向を行う方がはるかに簡単です。

最後に、最初は明らかではない場合でも、実際には、状態を公開する必要を回避するために、振る舞いとして必要ものを十分に公開する方法があるかもしれません。たとえば、Coordinateパブリックメンバーのみが存在する以前のバージョンを使用するとEquals()(実際には完全なIEquatable実装が必要になります)、プレゼンテーションレイヤーに次のクラスを記述できます。

public class CoordinateToVectorTransformer
{
    private Dictionary<Coordinate,Vector2> _coordinatePositions;

    public CoordinateToVectorTransformer(int boardWidth, int boardHeight)
    {
        for(int x=0; x<boardWidth; x++)
        {
            for(int y=0; y<boardWidth; y++)
            {
                _coordinatePositions[new Coordinate(x,y)] = GetPosition(x,y);
            }
        }
    }

    private static Vector2 GetPosition(int x, int y)
    {
        //Some implementation goes here...
    }

    public Vector2 Transform(Coordinate coordinate)
    {
        return _coordinatePositions[coordinate];
    }
}

おそらく驚くべきことに、目標を達成するために座標から実際に必要なすべての動作は、等価性チェックでした!もちろん、このソリューションはこの問題に合わせて調整されており、許容可能なメモリ使用量/パフォーマンスについて想定しています。これは、一般的なソリューションの青写真ではなく、この特定の問題領域に適合する単なる例です。

繰り返しになりますが、実際にはこれが不必要な複雑さであるかどうかについて意見は異なります。場合によっては、このような解決策が存在しないか、非常に奇妙で複雑な場合があり、その場合は上記の3つに戻すことができます。


美しく答えました!受け入れたいと思いますが、最初にいくつかのコメント:1。toDTO()が素晴らしいと思います。bcuzurはget / setにアクセスしないため、DTOに与えられたフィールドを既存のコードを壊さずに変更できます。2.セイカスタマーはuはそれらを変更する小道具にアクセスする方法を実体、例えば、アドレス/ TEL変更など行うことを正当化するのに十分な行動を持っている
IntelliData

@IntelliData 1.「フィールドを変更する」と言うとき、クラス定義を変更するか、データを変更するということですか?後者は、パブリックセッターを削除してゲッターを残すことで回避できます。そのため、dtoの側面は無関係です。前者は、実際にパブリックゲッターが「悪」である(全体の)理由ではありません。たとえば、programmers.stackexchange.com / questions / 157526 /…を参照してください。
ベンアーロンソン

@IntelliData 2.これは、動作を知らずに答えることは困難です。しかし、おそらく、答えはそうではないでしょう。Customer電話番号を変更できることを必要とするクラスの動作はどのようなものですか?おそらく、顧客の電話番号が変更され、その変更をデータベースに保持する必要がありますが、それは動作を提供するドメインオブジェクトの責任ではありません。これはデータアクセスの問題であり、おそらくDTOやリポジトリなどで処理されます。
ベンアーロンソン

@IntelliData Customerドメインオブジェクトのデータを(dbと同期して)比較的新鮮に保つことは、そのライフサイクルを管理する問題であり、それ自体の責任ではありません。Customersをインスタンス化するもの。
ベンアーロンソン

2
私が本当に好きな行動のためのデザインコンセプトを。これは、基礎となる「データ構造」に通知し、あまりにも一般的な貧弱で使いにくいクラスを回避するのに役立ちます。一を足す。
レーダーボブ

71

セッターを回避する最も簡単な方法は、オブジェクトを作成するときにコンストラクターメソッドに値を渡すことですnew これは、オブジェクトを不変にする場合の通常のパターンでもあります とはいえ、現実の世界では物事は必ずしもそれほど明確ではありません。

メソッドは振る舞いに関するものでなければならないのは事実です。ただし、Customerなどの一部のオブジェクトは、主に情報保持するために存在します。 これらは、ゲッターとセッターから最も恩恵を受ける種類のオブジェクトです。そのような方法がまったく必要ない場合は、単にそれらを完全に排除します。

さらに読む
GetterおよびSetterが正当化されるとき


3
では、なぜ「ゲッター/セッター」に関する誇大広告はすべて悪なのでしょうか?
IntelliData

46
よく知るべきソフトウェア開発者による通常の主張。あなたがリンクした記事について、著者はあなたの注意を引くために「ゲッターとセッターは悪です」というフレーズを使用していますが、それはステートメントが明確に真実であることを意味しません。
ロバートハーヴェイ

12
@IntelliDataの中には、Java自体が悪であると言う人もいます。
nullの

18
@MetaFightsetEvil(null);

4
確かにevalはEvil Incarnateです。または近くのいとこ。
ビショップ

58

振る舞いではなくデータを公開するオブジェクトを持つことはまったく問題ありません。単に「データオブジェクト」と呼びます。パターンは、データ転送オブジェクトや値オブジェクトなどの名前で存在します。オブジェクトの目的がデータの保持である場合、ゲッターとセッターはデータへのアクセスに有効です。

では、なぜ「getterメソッドとsetterメソッドは悪」と言うのでしょうか?あなたはこれをよく見るでしょう-誰かが特定のコンテキストで完全に有効なガイドラインを取り、より難しいヒットの見出しを得るためにコンテキストを削除します。例えば、「継承を超える賛成組成物は、」罰金原則ですが、十分にすぐに誰かがコンテキストを削除し、「書き込みしようとしているのはなぜ悪である拡張」(ねえ、同じ著者、どのような偶然!)や「継承は悪であるとでなければなりません破壊されました

記事の内容を見ると、実際にいくつかの有効なポイントがありますが、それはポイントを引き伸ばしてクリックベイティの見出しを作成するだけです。たとえば、この記事では、実装の詳細を公開すべきではないと述べています。これは、オブジェクト指向の基本であるカプセル化とデータ隠蔽の原則です。ただし、ゲッターメソッドは定義により実装の詳細を公開しません。以下の場合には顧客のデータオブジェクトのプロパティ住所等の実装の詳細ではなく、オブジェクトの全体の目的ではなく、パブリックインターフェイスの一部であるべきです。

リンク先の記事の続きを読んで、悪のセッターを使用せずに「従業員」オブジェクトに「名前」や「給与」などのプロパティを実際に設定することを彼がどのように提案するかを確認してください。彼は、名前の追加、同じ名前のフィールドを順番に設定する給与の追加というメソッドが入力された「エクスポーター」を使用したパターンを使用していることが判明しました...異なる命名規則。

これは、同じ実装を維持しつつ、シングルトンの名前を変更することでシングルトンの落とし穴を回避することを考えることに似ています。


おっと、いわゆる専門家はそうではないと言っているようです。
IntelliData

8
その後、再び、一部の人は「決してOOPを使用していない」と述べていますharmful.cat-v.org/software/OO_programming
JacquesB

7
FTR、私はまだも作成しないよりも、すべてのゲッター/セッターを無意味に作成することを教える「専門家」が多いと思います。IMO、後者は誤解を招くアドバイスではありません。
15年

4
@leftaroundabout:OK、しかし、私は「適切なときに使用する」「常に」と「決して」の間の中間点を提案しています。
ジャックB

3
問題は、多くのプログラマーがすべてのオブジェクト(またはオブジェクトが多すぎる)をDTOに変えることだと思います。必要な場合もありますが、データを動作から分離するため、可能な限り避けてください。(あなたが敬outなOOPerであると仮定)
-user949300

11

Customerデータオブジェクトから-class を変換するために、データフィールドについて次の質問をすることができます。

{data field}をどのように使用しますか?{データフィールド}はどこで使用されますか?{data field}の使用をクラスに移動できますか?

例えば:

  • の目的はCustomer.Name何ですか?

    可能な答えは、ログインWebページに名前を表示し、顧客への郵送で名前を使用します。

    メソッドにつながります:

    • Customer.FillInTemplate(…)
    • Customer.IsApplicableForMailing(…)
  • の目的はCustomer.DOB何ですか?

    顧客の年齢を検証します。顧客の誕生日の割引。郵送。

    • Customer.IsApplicableForProduct()
    • Customer.GetPersonalDiscount()
    • Customer.IsApplicableForMailing()

コメントを考えるCustomerと、データオブジェクトとしても、独自の責任を持つ「実際の」オブジェクトとしても、サンプルオブジェクトは広すぎます。つまり、プロパティ/責任が多すぎます。これは、多くのコンポーネントをCustomer(そのプロパティを読み取ることCustomerによって)依存するか、多くのコンポーネントに依存するかのいずれかになります。顧客のさまざまな見方が存在する可能性があり、おそらくそれぞれが独自の異なるクラス1を持つ必要があります。

  • Account金融取引のコンテキストでの顧客は、おそらく次の目的でのみ使用されます。

    • 送金が正しい人に送られていることを人間が識別できるようにします。そして
    • グループAccountの。

    この顧客は、のようなフィールドを必要としないDOBFavouriteColourTel、そしておそらくもありませんAddress

  • 銀行のWebサイトにログインするユーザーのコンテキストでの顧客。

    関連フィールドは次のとおりです。

    • FavouriteColour、パーソナライズされたテーマの形で提供される場合があります。
    • LanguagePreferences、そして
    • GreetingName

    ゲッターとセッターを持つプロパティの代わりに、これらは単一のメソッドでキャプチャされる場合があります。

    • PersonaliseWebPage(テンプレートページ);
  • マーケティングおよびパーソナライズされた郵送のコンテキストでの顧客。

    ここでは、データオブジェクトのプロパティに依存せず、代わりにオブジェクトの責任から始めます。例えば:

    • IsCustomerInterestedInAction(); そして
    • GetPersonalDiscounts()。

    この顧客オブジェクトがFavouriteColourプロパティを持っている、および/またはAddressプロパティが無関係になるという事実:おそらく実装はこれらのプロパティを使用します。しかし、いくつかの機械学習手法を使用し、顧客との以前の対話を使用して、顧客が興味を持っている可能性のある製品を発見することもできます。


もちろん1、CustomerおよびAccountクラスは、この顧客を分割することはやり過ぎかもしれない、と簡単な例や宿題の練習のため、例あったが、分割の一例で、私は持つオブジェクトへのデータオブジェクトを回す方法ことを実証したいと考えています責任が働きます。


4
実際に質問に答えているため、投票してください:)ただし、提案されたソリューションは、単にgettes / setterを持っているよりもはるかに悪いことも明らかです。たとえば、FillInTemplateは明らかに懸念の分離の原則を破ります。質問の前提に欠陥があることを示すだけです。
ジャックB

@Kasper van den Berg:そして、通常のように顧客に多くの属性がある場合、最初にどのように設定しますか?
IntelliData

2
@IntelliDataは、データベース、XMLなどから値を取得している可能性があります。顧客またはCustomerBuilderがそれらを読み取り、プライベート(Java)でパッケージ/内部クラスアクセス、または(ick)リフレクションを使用して設定します。完璧ではありませんが、通常はパブリックセッターを回避できます。(詳細については私の答えを参照してください)
-user949300

6
顧客クラスがこれらのことについて知っているべきではないと思います。
CodesInChaos

のようなものはCustomer.FavoriteColorどうですか?
ゲイブ

8

TL; DR

  • 動作のモデリングは優れています。

  • 優れた(!)抽象化のためのモデリングの方が優れています。

  • データオブジェクトが必要になる場合があります。


行動と抽象化

ゲッターとセッターを避ける理由はいくつかあります。1つは、既に述べたように、データのモデリングを回避することです。これは実際には小さな理由です。より大きな理由は、抽象化を提供することです。

明確な銀行口座の例:setBalance()残高の設定は口座の使用目的ではないため、方法は本当に悪いでしょう。アカウントの動作は、可能な限り現在の残高から抽象化する必要があります。引き出しに失敗するかどうかを決定する際に残高を考慮する場合があり、現在の残高へのアクセスを許可する場合がありますが、銀行口座とのやり取りを変更する場合、ユーザーは新しい残高を計算する必要はありません。それは、アカウント自体が行うべきことです。

メソッドdeposit()withdraw()メソッドのペアでさえ、銀行口座をモデル化するには理想的ではありません。より良い方法はtransfer()、引数として別のアカウントと金額を取る1つのメソッドのみを提供することです。これにより、アカウントクラスは、システムで誤ってお金を作成/破壊しないことを簡単に保証できるようになり、非常に有用な抽象化を提供します。稼いだ/投資した/失われたお金(二重会計を参照)。もちろん、アカウントのすべての使用がこのレベルの抽象化を必要とするわけではありませんが、クラスがどれだけの抽象化を提供できるかを考慮する価値は間違いありません。

抽象化の提供とデータ内部の非表示は常に同じではないことに注意してください。ほとんどすべてのアプリケーションには、事実上単なるデータであるクラスが含まれています。タプル、辞書、および配列はよくある例です。ユーザーからポイントのx座標を非表示にしたくありません。ポイントを使用して実行できる/するべき抽象化はほとんどありません。


顧客クラス

顧客は確かに、有用な抽象化を提供しようとするシステム内のエンティティです。たとえば、ショッピングカートに関連付けられている可能性が高く、カートと顧客の組み合わせで購入を許可する必要があります。これにより、要求された製品の送信、お金の請求(選択した支払いを考慮に入れる)などのアクションが開始されますメソッド)など

問題は、あなたが言及したすべてのデータが顧客に関連付けられているだけでなく、そのデータもすべて変更可能であるということです。顧客が移動する場合があります。彼らはクレジットカード会社を変えるかもしれません。メールアドレスと電話番号を変更する場合があります。ちなみに、彼らは名前や性別を変更するかもしれません!したがって、フル機能の顧客クラスでは、これらすべてのデータ項目への完全な変更アクセスを提供する必要があります。

それでも、セッターは非自明なサービスを提供できる/する必要があります。電子メールアドレスの正しい形式、郵便アドレスの検証などを保証できます。同様に、「ゲッター」はそのName <user@server.com>形式で電子メールアドレスを提供する名前フィールドとデポジットされた電子メールアドレスを使用するか、正しくフォーマットされた住所などを提供します。もちろん、この高レベルの機能が意味をなすのは、ユースケースに大きく依存します。完全にやり過ぎかもしれませんし、別のクラスがそれを正しく行うように要求するかもしれません。抽象化レベルの選択は簡単ではありません。


私はセックスの部分については意見が異なりますが...;)
IntelliData

6

Kasperの答えを拡大しようとすると、セッターを怒らせて排除するのが最も簡単です。かなり漠然とした、手を振る(そしてできればユーモラスな)議論では:

Customer.Nameはいつ変更されますか?

滅多。たぶん彼らは結婚した。または、目撃者の保護に入りました。ただし、その場合は、居住地、近親者、およびその他の情報も確認し、場合によっては変更する必要があります。

DOBはいつ変更されますか?

最初の作成時、またはデータ入力の失敗時のみ。または、彼らがドミンカの野球選手なら。:-)

これらのフィールドは、通常の通常のセッターではアクセスできません。たぶん、あなたはCustomer.initialEntry()メソッド、またはCustomer.screwedUpHaveToChange()特別な許可を必要とするメソッドを持っています。ただし、パブリックCustomer.setDOB()メソッドはありません。

通常、顧客はデータベース、REST API、XMLなどから読み取られます。メソッドCustomer.readFromDB()を使用するか、SRP /懸念事項の分離についてより厳格な場合は、メソッドを持つCustomerPersisterオブジェクトなど、別個のビルダーを使用しますread()。内部的には、何らかの方法でフィールドを設定します(パッケージアクセスまたは内部クラスYMMVを使用することを好みます)。しかし、再び、公共のセッターを避けてください。

(質問が多少変わったための補遺...)

アプリケーションでリレーショナルデータベースを多用するとします。メソッドCustomer.saveToMYSQL()Customer.readFromMYSQL()メソッドを持つのは愚かなことでしょう。これにより、具体的で非標準の、変更される可能性のあるエンティティへの望ましくない結合が作成さます。たとえば、スキーマを変更したとき、またはPostgressまたはOracleに変更したとき。

しかし、IMO、それはへのカップルのお客様に完全に受け入れだ抽象標準ResultSet。個別のヘルパーオブジェクト(CustomerDBHelperおそらくのサブクラスと呼ばAbstractMySQLHelperれます)は、DBへのすべての複雑な接続、トリッキーな最適化の詳細、テーブル、クエリ、結合などを知っています(または、 ResultSetを生成するためのHibernateのようなORM)。あなたのオブジェクトResultSetは、変更される可能性のない、抽象的な標準であると通信します。基礎となるデータベースを変更した場合、またはスキーマを変更した場合、Customerは変更されませんが、CustomerDBHelper変更されます。運がよければ、変更されるのはAbstractMySQLHelperだけです。これにより、顧客、販売者、配送などの変更が自動的に行われます。

このようにして、(おそらく)ゲッターとセッターの必要性を回避または軽減できます。

そして、Holubの記事の要点は、上記の内容を、すべてにゲッターとセッターを使用してデータベースを変更した場合の方法と比較対照することです。

同様に、多くのXMLを使用するとします。IMO、顧客をPython xml.etree.ElementTreeやJava org.w3c.dom.Elementなどの抽象的な標準に結合することは問題ありません。顧客はそこから取得して設定します。繰り返しますが、(おそらく)ゲッターとセッターの必要性を減らすことができます。


Builderパターンを使用することをお勧めしますか?
IntelliData

Builderは、オブジェクトの構築をより簡単かつ堅牢にし、必要に応じて、オブジェクトを不変にすることを可能にするのに役立ちます。ただし、基礎となるオブジェクトにDOBフィールドがあるという事実は(部分的に)まだ公開されているため、すべてがすべてではありません。
-user949300

1

ゲッターとセッターの問題は、クラスがビジネスロジックで1つの方法で使用される可能性があるという事実の問題である可能性がありますが、データベースまたはファイルまたはその他の永続ストレージからのデータをシリアル化/逆シリアル化するヘルパークラスもある場合があります。

データを保存/取得する方法がたくさんあり、データオブジェクトを保存方法から切り離したいという事実のため、これらのメンバーを公開するか、ゲッターとセッターは、公開するのとほとんど同じくらい悪いです。

これにはさまざまな方法があります。1つの方法は、「友人」がデータを利用できるようにすることです。友情は継承されませんが、これは友人に情報を要求するシリアライザ、つまり基本シリアライザが情報を「転送」することで克服できます。

クラスには、一般的な「fromMetadata」または「toMetadata」メソッドを含めることができます。From-metadataはオブジェクトを構築するため、コンストラクターになる可能性があります。それが動的に型付けされた言語である場合、メタデータはそのような言語のかなり標準であり、おそらくそのようなオブジェクトを構築する主な方法です。

言語が特にC ++である場合、これを回避する1つの方法は、データのパブリックな「構造」を持ち、クラスがこの「構造」のインスタンスをメンバーとして、実際に保存するすべてのデータを保持することです/そこに格納されるように取得します。その後、複数の形式でデータを読み書きするための「ラッパー」を簡単に作成できます。

言語が「構造体」を持たないC#またはJavaの場合、同様に実行できますが、構造体は2次クラスになります。データまたはconst-nessの「所有権」の概念はありません。したがって、データを含むクラスのインスタンスを公開し、それがすべてパブリックである場合、保持するものは何でも変更できます。これは高価ですが、「クローン」できます。または、このクラスにプライベートデータを持たせ、アクセサーを使用することもできます。これにより、クラスのユーザーはデータに到達するための回り道をすることができますが、それはクラスとの直接的なインターフェースではなく、ユースケースであるクラスのデータを保存することの詳細です。


0

OOPは、オブジェクト内の動作をカプセル化および非表示にすることです。オブジェクトはブラックボックスです。これは物をデザインする方法です。多くの場合、資産は別のコンポーネントの内部状態を知る必要がないので、知る必要はありません。主にインターフェースまたは可視性を備えたオブジェクト内でそのアイデアを実施でき、呼び出し側が許可された動詞/アクションのみを使用できるようにします。

これはある種の問題に対してうまく機能します。たとえば、個々のUIコンポーネントをモデル化するユーザーインターフェイス。テキストボックスを使用して作業を行う場合、テキストの設定、取得、またはテキスト変更イベントのリッスンに興味があるだけです。通常、カーソルの位置、テキストの描画に使用されるフォント、またはキーボードの使用方法については関心がありません。カプセル化はここで多くを提供します。

それどころか、ネットワークサービスを呼び出すときは、明示的な入力を提供します。通常、文法(JSONやXMLなど)と、サービスを呼び出すすべてのオプションに非表示の理由はありません。アイデアは、あなたが望む方法でサービスを呼び出すことができ、データ形式は公開され公開されているということです。

この場合、または他の多くの(データベースへのアクセスなど)共有データを実際に使用します。そのため、非表示にする理由はありませんが、逆に使用可能にする必要があります。読み取り/書き込みアクセスまたはデータチェックの一貫性が懸念される場合がありますが、このコアでは、これがパブリックである場合のコア概念です。

カプセル化を避けて物事を公開したい、そして明確にしたいような設計要件の場合、オブジェクトを避けたいのです。本当に必要なのは、オブジェクトではなく、タプル、C構造体、またはそれらに相当するものです。

しかし、Javaのような言語でも起こります。モデル化できるのは、オブジェクトまたはオブジェクトの配列だけです。オブジェクト自体がいくつかのネイティブ型(int、float ...)を保持できますが、それだけです。しかし、オブジェクトは、パブリックフィールドだけを持つ単純な構造体のように動作することもできます。

したがって、データをモデル化する場合、オブジェクト内のパブリックフィールドだけで行うことができます。これ以上必要ないからです。カプセル化は必要ないので使用しません。これは多くの言語でこの方法で行われます。歴史的に、javaでは、getter / setterを使用すると(たとえばsetterを追加しないことで)読み取り/書き込み制御を行うことができ、introspection APIを使用することでツールやフレームワークがgetter / setterメソッドを探して使用する標準が上昇しましたコンテンツを自動入力するか、自動生成されたユーザーインターフェイスで修正可能なフィールドとしてこれらを表示します。

また、セッターメソッドにロジック/チェックを追加できる引数もあります。

ゲッター/セッターは純粋なデータのモデル化に最もよく使用されるため、実際にはほとんど正当化されません。オブジェクトを使用するフレームワークと開発者は、ゲッター/セッターがフィールドを設定/取得するだけであると期待しています。パブリックフィールドでできることよりも、getter / setterで効果的に行うことはありません。

しかし、それは古い習慣であり、古い習慣を取り除くことは困難です...彼らが何であり、彼らが何であるかをよりよく理解するための背景が欠けている場合、盲目的にゲッター/セッターを置くと、同僚や教師によって脅かされることさえありますありません。

これらのゲッター/セッターの定型コードすべてに乗るには、おそらく言語を変更する必要があります。(C#やlispのように)。私にとって、ゲッター/セッターはもう10億ドルの間違いです...


6
C#のプロパティは、実際にゲッターとセッターを行うよりも、それ以上...カプセル化を実装していない
IntelliData

1
[gs] Setterの利点は、通常はしないこと(値の確認、オブザーバーへの通知)、存在しないフィールドのシミュレートなどができ、主に後で変更できることです。ではロンボク、定型がなくなっています:@Getter @Setter class MutablePoint3D {private int x, y, z;}
maaartinus

1
@maaartinus確かに、[gs] etterは他の方法と同じように何でもできます。つまり、呼び出し元のコードは、設定した値が例外をスローしたり、変更されたり、変更の通知を送信したりする可能性があることなどを認識している必要があります。多かれ少なかれ[gs] etterはフィールドへのアクセスを提供するのではなく、任意のコードを実行します。
ニコラスブーケ

@IntelliData C#プロパティを使用すると、ボイラープレートの1つの無駄な文字を1つも記述せず、このゲッター/セッターのすべてを気にせずに済みます。また、私にとっては、ゲッター/セッターだけのPOJOはカプセル化を提供するためではなく、サービスとの交換として自由に読み書きできるデータ形式を公開するためにここにあります。カプセル化は、設計要件の反対です。
ニコラスブスケ

プロパティが本当に良いとは思わない。確かに、プレフィックスと括弧、つまり呼び出しごとに5文字を保存しますが、1。それらはフィールドのように見え、紛らわしいです。2.これらは、リフレクションでサポートが必要な追加物です。大したことはありませんが、大きな利点もありません(Java + Lombokと比較した場合、純粋なJavaは明らかな損失にさらされています)。
maaartinus

0

たとえば、Name、DOB、Tel、Addressなど、顧客に関する情報を多数保持するCustomerクラスがある場合、これらすべての属性を取得および設定するゲッター/セッターを回避するにはどうすればよいでしょうか?すべてのデータを取り込むためにどのような「動作」タイプのメソッドを記述できますか?

データを取り込むための動作メソッドについて心配しているので、この質問は厄介だと思いますがCustomer、オブジェクトのクラスがカプセル化しようとしている動作の兆候は見られません。

混同しないでくださいCustomerオブジェクトのクラスとして「顧客とユーザー/ 俳優のソフトウェアを使用してさまざまなタスクを実行します。

顧客に関する情報がたくさんある場合は、Customerクラスを指定すると、振る舞いに関する限り、Customerクラスはそれを岩とほとんど区別しないように見えます。AにRockは色を付けることができ、名前を付けることができます。現在のアドレスを格納するフィールドを持たせることもできますが、岩からのインテリジェントな動作は期待できません。

ゲッター/セッターが悪であるというリンクされた記事から:

OOの設計プロセスは、ユースケースを中心としています。ユーザーは、いくつかの有用な結果をもたらすスタンドアロンタスクを実行します。(ログオンは問題領域で有用な結果が得られないため、ユースケースではありません。給与を描くことはユースケースです。)OOシステムは、ユースケースを構成するさまざまなシナリオを実行するために必要なアクティビティを実装します。

動作を定義せずに、岩をとして参照しても、Customerそれが追跡したいいくつかのプロパティを持つオブジェクトであるという事実は変わりません。セッター。岩は有効な名前を持っているかどうかを気にしません。また、岩は住所が有効かどうかを知ることを期待されません。

注文システムをRock購入注文に関連付けることができ、それRockが定義された住所を持っている限り、システムの一部はアイテムが岩に配達されることを確認できます。

これらのすべての場合において、これRockは単なるデータオブジェクトであり、仮説の代わりに有用な結果を伴う特定の動作を定義するまで1のままです。


これを試して:

「顧客」という言葉に2つの異なる意味を持たせすぎないようにすると、概念化が容易になります。

Rockオブジェクトは注文を出しますか、それとも人間がUI要素をクリックしてシステム内のアクションをトリガーすることによって行うものですか?


顧客は多くのことを行う俳優ですが、たまたま多くの情報が関連付けられています。それは2つの別々のクラスを作成することを正当化しますか?1つはアクターとして、1つはデータオブジェクトとして
IntelliData

レイヤー間でリッチオブジェクトを渡す@IntelliDataは、物事が難しい場所です。オブジェクトをコントローラーからビューに送信する場合、ビューはオブジェクトの一般的なコントラクト(JavaBeans標準など)を理解する必要があります。オブジェクトを有線で送信する場合、JaxBなどはそれをダムオブジェクトに再インスタンス化できる必要があります(完全なリッチオブジェクトを提供しなかったため)。リッチオブジェクトは、データを操作するのに適しています-状態を転送するのには適していません。逆に、ダムオブジェクトはデータを操作するには不十分であり、状態を転送するのには問題ありません。

0

ここでは、SQLを話すオブジェクトのアプローチに言及する2セントを追加します

このアプローチは、自己完結型オブジェクトの概念に基づいています。動作を実装するために必要なすべてのリソースがあります。仕事をする方法を伝える必要はありません-宣言的な要求で十分です。また、オブジェクトはすべてのデータをクラスプロパティとして保持する必要はありません。それは本当にどこから来ても関係ありませんし、そうすべきではありません。

集合体について言えば、不変性も問題ではありません。集約が保持できる一連の状態があるとしサガとしての集約ルート ます。各状態をスタンドアロンオブジェクトとして実装するのはまったく問題ありません 。おそらく、さらに先へ進むことができます。ドメインの専門家とチャットすることができます。おそらく、彼または彼女は、この集合体を統一されたエンティティとは見なさないでしょう。おそらく、各状態には独自の意味があり、独自のオブジェクトに値します。

最後に、オブジェクト検索プロセスは、サブシステムへのシステム分解と非常に似ていることに注意したいと思います。どちらも動作に基づいており、他には何もありません。

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