私はooの方法でクラスを設計するのに苦労しています。オブジェクトはデータではなく動作を公開することを読みました。したがって、ゲッター/セッターを使用してデータを変更するのではなく、特定のクラスのメソッドは「動詞」またはオブジェクトで動作するアクションである必要があります。たとえば、「Account」オブジェクトでは、Withdraw()
などではなく、メソッドDeposit()
およびsetAmount()
を使用します。以下を参照してください。 Why getter andセッターメソッドは悪です 。
したがって、たとえば、顧客に関する多くの情報を保持するCustomerクラスがあるとします。名前、DOB、Tel、アドレスなど、これらすべての属性を取得および設定するためのゲッター/セッターをどのように回避しますか?すべてのデータを入力するために、どのような「動作」タイプのメソッドを書き込むことができますか?
かなりの数の回答とコメントで述べられているように、DTO areは、特に境界を越えてデータを転送する場合(たとえば、JSONにシリアル化してWebサービスを介して送信する場合など)に適切かつ有用です。この回答の残りの部分では、多かれ少なかれそれを無視して、ドメインクラス、およびそれらをどのように設計してゲッターとセッターを最小化(排除しないとしても)しても、大規模プロジェクトで役立つ方法について説明します。 whyゲッターやセッターを削除したり、-whenを削除したりすることについても触れません。
例として、プロジェクトがチェスや戦艦のようなボードゲームであると想像してください。これをプレゼンテーションレイヤー(コンソールアプリ、Webサービス、GUIなど)で表現する方法はさまざまですが、コアドメインもあります。あるクラスはCoordinate
で、ボード上の位置を表します。それを書く「邪悪な」方法は次のようになります:
_public class Coordinate
{
public int X {get; set;}
public int Y {get; set;}
}
_
(簡潔にするため、およびJavaに慣れているため、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を変更するクラスの他のメソッドからは保護されないことに注意してください。より厳密に不変であるようにするには、readonly
(Javaではfinal
)を使用できます。しかし、どちらの方法でも-プロパティを真に不変にするか、セッターによる直接のパブリックミューテーションを防ぐだけかに関わらず、パブリックセッターを削除するというトリックを行います。ほとんどの場合、これは問題なく機能します。
上記はすべて適切でセッターに適していますが、ゲッターに関しては、開始する前に実際に足で撃ちました。私たちのプロセスは、座標とは何かデータそれが表す-を考え、その周りにクラスを作成することでした。代わりに、座標から必要なbehaviorから始める必要があります。ちなみに、このプロセスは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サイトなどに顧客情報を表示している可能性があります。その場合、何らかのCustomer
DTOがおそらく意味があります。ただし、システムに顧客が含まれていて、それらがデータモデルで表されているからといって、ドメインにCustomer
クラスが存在する必要があるとは限りません。多分あなたは振る舞いを設計するときにそれが現れるでしょうが、ゲッターを避けたいなら、先制的にそれを作成しないでください。
したがって、上記は良いスタートですが、遅かれ早かれ、クラスに関連付けられている振る舞いがあり、何らかの形でクラスの状態に依存しますが、属していない状況に遭遇するでしょうonクラス。この種の動作は、通常、アプリケーションのサービスレイヤーに存在します。
Coordinate
の例をとると、最終的にはユーザーにゲームを表現する必要があり、画面に描画することになる可能性があります。たとえば、_Vector2
_を使用して画面上のポイントを表すUIプロジェクトがあるとします。しかし、Coordinate
クラスが座標から画面上の点に変換することを担当することは不適切です。これは、あらゆる種類のプレゼンテーションの懸念をコアドメインにもたらすことになります。残念ながら、このタイプの状況はOO設計に固有です。
最初のオプションは、非常に一般的に選択されていますが、いまいましいゲッターを公開して地獄にそれを言うだけです。これには単純さの利点があります。しかし、ゲッターを回避することについて話しているので、議論のためにこれを拒否し、他にどのようなオプションがあるかを見てみましょう。
2番目のオプションは、クラスに何らかの.ToDTO()
メソッドを追加することです。とにかく、これまたは同様のものが必要になる場合があります。たとえば、ゲームを保存する場合は、ほとんどすべての状態をキャプチャする必要があります。ただし、サービスに対してこれを行うことと、ゲッターに直接アクセスすることの違いは、多かれ少なかれ美的です。それはまだそれと同じくらい多くの「悪」を持っています。
3番目のオプション-私がいくつかのPluralsightビデオで Zoran Horvat によって提唱されているのを見たのですが、修正したビジターパターンのバージョン。これはかなり珍しいパターンの使用法とバリエーションであり、人々のマイレージは、それが実際の利益のために複雑さを追加するのか、それとも状況に対するニースの妥協であるのかによって大きく異なります。基本的には標準のビジターパターンを使用するという考え方ですが、Visit
メソッドに、訪問しているクラスではなく、必要な状態をパラメーターとして取得させるようにします。例は here にあります。
私たちの問題の場合、このパターンを使用した解決策は次のようになります。
_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
_はreallyカプセル化されていません。それらを直接返すだけの_IPositionTransformer<Tuple<int,int>>
_を作成することで、それらを抽出できます。好みによっては、これが全体の練習を無意味にしていると感じるかもしれません。
ただし、パブリックゲッターを使用すると、データを直接取り出してそれを Tell、Do n't Ask に違反して使用するだけで、間違った方法で実行するのが非常に簡単になります。このパターンを使用するのは、実際にはsimplerが正しい方法です。動作を作成する場合は、それに関連付けられたタイプを自動的に作成します。 TDAの違反は非常に悪臭があり、おそらくより単純で優れたソリューションを回避する必要があります。実際には、これらのポイントにより、ゲッターが奨励する「邪悪な」方法よりも、OOを正しく実行することがはるかに容易になります。
最後に、最初は明白ではない場合でも、実際に必要なものを十分公開する方法があるかもしれません状態を公開する必要がないようにする動作。たとえば、唯一のパブリックメンバーがEquals()
である以前のバージョンのCoordinate
を使用すると(実際には、完全な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つに戻すことができます。
セッターを回避する最も簡単な方法は、オブジェクトをnew
アップするときにコンストラクターメソッドに値を渡すことです。これも通常の方法ですオブジェクトを作成したいときのパターンimmutable.とはいえ、現実の世界では物事は必ずしもそれほど明確ではありません。
メソッドが振る舞いに関するものであることは事実です。ただし、Customerなどの一部のオブジェクトは、主にhold information.に存在します。これらは、ゲッターとセッターから最も恩恵を受ける種類のオブジェクトです。そのような方法がまったく必要ない場合は、それらを完全に排除します。
さらに読む
ゲッターとセッターはいつ正当化されるか
動作ではなくデータを公開するオブジェクトがあっても問題ありません。これを「データオブジェクト」と呼びます。パターンは、データ転送オブジェクトや値オブジェクトなどの名前で存在します。オブジェクトの目的がデータを保持することである場合、ゲッターとセッターはデータへのアクセスに有効です。
それで、なぜ誰かが「ゲッターとセッターのメソッドは悪だ」と言うでしょうか?あなたはこれをよく目にします-誰かが特定のコンテキストで完全に有効なガイドラインを取り、それからそのコンテキストを削除して、より印象的な見出しを取得します。たとえば、「 継承よりも構成を好む 」は良い原則ですが、すぐに誰かがコンテキストを削除して「 Why extends is evil "(ちょっと、同じ作者) 、なんという偶然でしょう!)または " 継承は悪であり、破壊する必要があります "。
記事の内容を見ると、実際には有効なポイントがいくつかありますが、ポイントを引き伸ばしてクリックベイティーの見出しを作成するだけです。たとえば、記事では、実装の詳細は公開しないでください。これは、OOの基本であるカプセル化とデータ非表示の原則です。ただし、getterメソッドは、定義上、実装の詳細を公開しません。 Customerデータオブジェクトの場合、Name、Addressなどは実装の詳細ではなく、オブジェクトの目的全体であり、パブリックインターフェイスの一部である必要があります。
リンク先の記事の continuation を読んで、邪悪なセッターを使用せずに「Employee」オブジェクトに「name」や「salary」などのプロパティを実際に設定することを彼がどのように提案するかを確認してください。 addName、addSalaryと呼ばれるメソッドが設定された 'Exporter'でパターンを使用することが判明同じ名前のフィールド...つまり、結局彼は、異なる命名規則を使用して、正確にセッターパターンを使用することになります。
これは、同じ実装を維持しながら、シングルトンの名前を1つにしか変更できないようにすることで、シングルトンの落とし穴を回避すると考えるのと同じです。
Customer
- classをデータオブジェクトから変換するには、データフィールドについて次の質問に答えます。
{データフィールド}をどのように使用しますか? {データフィールド}はどこで使用されますか? {データフィールド}の使用をクラスに移動できますか?
例えば。:
Customer.Name
の目的は何ですか?
考えられる答えは、ログインWebページに名前を表示し、顧客への郵送でその名前を使用することです。
メソッドにつながる:
Customer.DOB
の目的は何ですか?
顧客の年齢を検証します。顧客の誕生日の割引。メーリング。
コメントを考えると、サンプルオブジェクトCustomer
–データオブジェクトとしても、独自の責任を持つ「実際の」オブジェクトとしても–は広すぎます。つまり、プロパティ/責任が多すぎる。これは、(プロパティを読み取ることにより)Customer
に依存する多くのコンポーネント、または多くのコンポーネントに依存するCustomer
につながります。おそらく、顧客の異なるビューが存在します。おそらく、それぞれに独自のクラスがあります。1:
Account
および金銭取引のコンテキストでの顧客は、おそらく次の目的にのみ使用されます。
Account
s。この顧客は、DOB
、FavouriteColour
、Tel
などのフィールドを必要とせず、Address
も必要ない場合があります。
ユーザーが銀行のWebサイトにログインしている状況での顧客。
関連するフィールドは次のとおりです。
FavouriteColour
。これは、パーソナライズされたテーマの形で提供される場合があります。LanguagePreferences
、およびGreetingName
ゲッターとセッターを持つプロパティの代わりに、これらは単一のメソッドでキャプチャされる場合があります。
マーケティングおよびパーソナライズされたメール配信のコンテキストでの顧客。
ここでは、データオブジェクトのプロパティに依存せず、代わりにオブジェクトの責任から始めます。例えば。:
この顧客オブジェクトにFavouriteColour
プロパティ、および/またはAddress
プロパティがあるという事実は無関係になります。おそらく実装はこれらのプロパティを使用します。ただし、一部の機械学習手法を使用し、顧客との以前のやり取りを使用して、顧客が興味を持っている可能性のある製品を発見することもあります。
1.もちろん、Customer
クラスとAccount
クラスは例であり、簡単な例や宿題の練習では、この顧客を分割するのはやり過ぎかもしれませんが、分割の例では、データオブジェクトを責任のあるオブジェクトに変換する方法が機能することを確認します。
TL; DR
行動のモデリングは優れています。
良い(!)抽象化のためのモデリングはより良いです。
データオブジェクトが必要になる場合があります。
動作と抽象化
ゲッターとセッターを回避する理由はいくつかあります。 1つは、あなたが指摘したように、データのモデル化を回避することです。これは実際にはマイナーな理由です。より大きな理由は、抽象化を提供することです。
明確な銀行口座の例では、残高の設定は口座の使用目的ではないので、setBalance()
メソッドは本当に悪いでしょう。アカウントの動作は、現在の残高から可能な限り抽象化する必要があります。引き出しに失敗するかどうかを判断するときに残高が考慮され、現在の残高にアクセスできる場合がありますが、銀行口座とのやり取りを変更しても、ユーザーは新しい残高を計算する必要はありません。それが、アカウント自体が行うべきことです。
deposit()
メソッドとwithdraw()
メソッドのペアでさえ、銀行口座をモデル化するには理想的ではありません。より良い方法は、引数として別のアカウントと金額を受け取るtransfer()
メソッドを1つだけ提供することです。これにより、アカウントクラスは、システムで誤ってお金を作成/破壊しないことを簡単に確認でき、非常に使いやすい抽象化を提供し、特別なアカウントの使用を強制するため、実際により多くの洞察をユーザーに提供します。稼いだ/投資した/失われたお金( double-accounting を参照)。もちろん、アカウントのすべての使用がこのレベルの抽象化を必要とするわけではありませんが、クラスが提供できる抽象化の量を考慮することは間違いなく価値があります。
抽象化を提供することとデータ内部を非表示にすることは必ずしも同じではないことに注意してください。ほとんどすべてのアプリケーションには、事実上単なるデータであるクラスが含まれています。タプル、辞書、配列はよくある例です。ポイントのx座標をユーザーから隠したくない。ポイントで実行できる/すべきである抽象化はほとんどありません。
顧客クラス
顧客は確かに、有用な抽象化を提供しようとするシステム内のエンティティです。たとえば、ショッピングカートに関連付けられている可能性が高く、カートと顧客の組み合わせは購入のコミットを許可する必要があります。これにより、要求された製品を彼に送信し、(選択した支払いを考慮して)お金を請求するなどのアクションを開始できます。メソッド)など.
問題は、あなたが言及したすべてのデータは顧客に関連付けられているだけでなく、そのすべてのデータも変更可能であることです。お客様が移動する場合があります。彼らは彼らのクレジットカード会社を変えるかもしれません。メールアドレスと電話番号は変更される場合があります。一体、彼らは彼らの名前やセックスを変えるかもしれません!したがって、完全な機能を備えた顧客クラスは、実際にこれらすべてのデータ項目への完全な変更アクセスを提供する必要があります。
それでも、セッターは重要なサービスを提供できます/そうする必要があります。彼らは、電子メールアドレスの正しい形式、郵便アドレスの検証などを保証できます。同様に、「ゲッター」は、Name <[email protected]>
名前フィールドと送信されたメールアドレスを使用してフォーマットするか、正しくフォーマットされた郵便アドレスなどを提供します。もちろん、この高レベルの機能の意味は、ユースケースに大きく依存します。それは完全にやり過ぎかもしれませんし、別のクラスがそれを正しく行うように要求するかもしれません。抽象化レベルの選択は簡単ではありません。
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へのすべての複雑な接続、トリッキーな最適化の詳細、テーブルを知っています。クエリ、結合など(またはHibernateのようなORMを使用)して、ResultSetを生成します。オブジェクトはResultSet
と通信します。これは抽象的な標準であり、変更される可能性はほとんどありません。基になるデータベースを変更するか、スキーマを変更すると、Customerは変更されませんになりますが、CustomerDBHelperは変更されます。運が良ければ、Customer、Merchant、Shippingなどの変更を自動的に行うのはAbstractMySQLHelperだけです...
このようにして、ゲッターとセッターの必要性を(おそらく)回避または軽減できます。
そして、Holub記事の要点は、上記のものをすべてに対してゲッターとセッターを使用し、データベースを変更した場合の状況と比較および対比してください。
同様に、多くのXMLを使用するとします。 IMO、お客様をPython xml.etree.ElementTreeまたはJava org.w3c.dom.Element。顧客はそれから自分自身を取得および設定します。ここでも、(おそらく)ゲッターとセッターの必要性を減らすことができます。
ゲッターとセッターの問題は、クラスがビジネスロジックで1つの方法で使用される場合があるが、データベース、ファイル、または他の永続的なストレージからのデータをシリアル化/逆シリアル化するヘルパークラスがある場合があるという事実の問題である可能性があります。
データを格納/取得するには多くの方法があり、データオブジェクトの格納方法からデータオブジェクトを分離したいという事実があるため、これらのメンバーをパブリックにするか、getterおよびそれらを公開するのとほとんど同じくらい悪いセッター。
これにはさまざまな方法があります。 1つの方法は、「友達」がデータを利用できるようにすることです。友情は継承されませんが、友だちに情報を要求するシリアライザ、つまり基本シリアライザが情報を「転送」することによって、これを克服できます。
クラスには、一般的な「fromMetadata」または「toMetadata」メソッドを含めることができます。 from-metadataはオブジェクトを構築するため、コンストラクターである可能性があります。それが動的に型付けされた言語である場合、メタデータはそのような言語のかなり標準であり、おそらくそのようなオブジェクトを構築する主要な方法です。
言語が特にC++の場合、これを回避する1つの方法は、データのパブリック「構造体」を作成し、クラスにこの「構造体」のインスタンスをメンバーとして、実際にはすべてのデータを格納することです/そこに格納するために取得します。その後、「ラッパー」を簡単に記述して、複数の形式でデータを読み書きできます。
言語がC#またはJavaで「構造体」を持たない場合)同様に実行できますが、構造体は2次クラスになります。データの「所有権」という実際の概念はありません。またはconst-nessなので、データを含むクラスのインスタンスを提供し、それがすべてパブリックである場合、保持されるものはそれを変更できます。これは高価になる可能性がありますが、「クローン」することもできます。または、このクラスにプライベートデータを含めることもできますこれはアクセサを使用します。これは、クラスのユーザーにデータにアクセスするための迂回的な方法を提供しますが、これはクラスとの直接的なインターフェースではなく、実際にクラスのデータを格納する詳細であり、ユースケースでもあります。
SQL-speaking objects アプローチに言及して、ここに2セントを追加します。
このアプローチは、自己完結型オブジェクトの概念に基づいています。動作を実装するために必要なすべてのリソースがあります。 方法にその仕事をするように指示する必要はありません-宣言的なリクエストで十分です。また、オブジェクトはすべてのデータをクラスのプロパティとして保持する必要はありません。どこから取得したかは問題ではありません。
aggregate について言えば、不変性も問題ではありません。たとえば、集計で保持できる一連の状態があるとします。 各状態をスタンドアロンオブジェクトとして実装するのはまったく問題ありません。おそらく、さらに先に進むことができます。ドメインの専門家とチャットしてください。おそらく、彼または彼女はこの集合体をいくつかの統一された実体として見ていません。おそらく、各状態には独自の意味があり、独自のオブジェクトに値します。
最後に、オブジェクト検索プロセスは システムのサブシステムへの分解 と非常に似ていることに注意したい。どちらも動作に基づいており、それ以外のものはありません。
OOPは、オブジェクト内の動作をカプセル化および非表示にすることです。オブジェクトはブラックボックスです。これは物事をデザインする方法です。資産は多くの場合、別のコンポーネントの内部状態を知る必要がないので、知る必要がない方が良いです。主にインターフェースを使用して、または可視性を備えたオブジェクトの内部でそのアイデアを実施でき、呼び出し側は許可された動詞/アクションのみを使用できます。
これは、ある種の問題に適しています。たとえば、個々のUIコンポーネントをモデル化するユーザーインターフェイス。テキストボックスを操作するときは、テキストの設定、取得、またはテキスト変更イベントのリスニングにのみ関心があります。通常、カーソルの位置、テキストの描画に使用するフォント、またはキーボードの使用方法については気にしません。カプセル化はここで多くを提供します。
逆に、ネットワークサービスを呼び出す場合は、明示的な入力を提供します。通常、文法(JSONやXMLなど)があり、サービスを呼び出すすべてのオプションを非表示にする必要はありません。アイデアは、サービスを好きな方法で呼び出すことができ、データ形式は公開されて公開されるということです。
この場合、または他の多く(データベースへのアクセスなど)で、実際に共有データを操作します。そのため、非表示にする理由はありません。逆に、使用できるようにする必要があります。読み取り/書き込みアクセスまたはデータチェックの一貫性に懸念がある可能性がありますが、このコアでは、これが公開されている場合のコアコンセプトです。
カプセル化を避けて物事を公開し、平文でオブジェクトを避けたいような設計要件の場合。実際に必要なのは、タプル、C構造体、またはそれらに相当するものであり、オブジェクトではありません。
ただし、Javaなどの言語でも発生します。モデル化できるのは、オブジェクトまたはオブジェクトの配列だけです。それら自体のオブジェクトはいくつかのネイティブタイプ(int、float ...)を保持できますが、それだけです。しかし、オブジェクトは、パブリックフィールドだけの単純な構造体のように動作することもできます。
したがって、データをモデル化する場合は、オブジェクト内のパブリックフィールドだけで実行できます。それ以上必要としないからです。カプセル化は必要ないので使用しません。これは、多くの言語でこの方法で行われます。 Javaでは、歴史的に、getter/setterで少なくとも読み取り/書き込みコントロール(たとえば、setterを追加しないこと)が可能な標準的なバラと、イントロスペクションAPIを使用したツールとフレームワークがgetter/setterメソッドを探し、それを使用してコンテンツを自動入力するか、自動生成されたユーザーインターフェイスで変更可能なフィールドとしてこれらを表示します。
また、setterメソッドにロジック/チェックを追加することもできます。
ゲッター/セッターは純粋なデータをモデル化するために最も頻繁に使用されるため、実際にはほとんど正当化されません。オブジェクトを使用するフレームワークと開発者は、とにかくフィールドを設定/取得する以外に、getter/setterが何もしないことを期待しています。あなたは効果的に、getter/setterを使用して、パブリックフィールドで実行できること以上のことはしません。
しかし、それは古い習慣であり、古い習慣を取り除くことは困難です...彼らが何であるか、そしてそれらが何であるかをよりよく理解するための背景がない場合、ゲッター/セッターを盲目的にどこにも置かないと、同僚や教師に脅かされる可能性さえありますない。
これらのゲッター/セッターのボイラープレートコードをすべて使用するには、言語を変更する必要があります。 (C#またはLISPのように)。私にとってゲッター/セッターは、もう1つの10億ドルの間違いです...
したがって、たとえば、顧客に関する情報(たとえば、名前、DOB、Tel、アドレスなど、これらすべての属性を取得および設定するためのゲッター/セッターをどのように回避しますか?すべてのデータを入力するために、どのような「動作」タイプのメソッドを書き込むことができますか?
データを取り込むための動作メソッドについて心配しているので、この質問は厄介だと思いますが、Customer
オブジェクトのクラスがカプセル化することを意図している動作の兆候はわかりません。
Customer
をオブジェクトのクラスと 'Customer'をuser /と混同しないでください 俳優あなたのソフトウェアを使用してさまざまなタスクを実行する人。
Customerクラスが顧客に関する情報の場合に多くを維持するCustomerクラスを指定すると、動作に関する限り、Customerクラスはそれを岩とほとんど区別しないように見えます。 Rock
には色を付けることができ、名前を付けたり、現在の住所を格納するためのフィールドを設定したりできますが、岩からのインテリジェントな動作は期待できません。
ゲッター/セッターが悪であることについてのリンクされた記事から:
OO設計プロセスはユースケースを中心としています。ユーザーはいくつかの有用な結果をもたらすスタンドアロンタスクを実行します(ログオンは問題領域で有用な結果を欠くため、ユースケースではありません。 paycheckはユースケースです。)OOシステムは、ユースケースを構成するさまざまなシナリオを実行するために必要なアクティビティを実装します。
動作が定義されていない場合、岩をCustomer
として参照しても、それが追跡したいいくつかのプロパティを持つオブジェクトであり、どのトリックをプレイするかは問題ではないという事実は変わりませんゲッターとセッターから逃れるために。岩はそれが有効な名前を持っているかどうかを気にせず、岩は住所が有効かどうかを知ることを期待されません。
注文システムはRock
を注文書に関連付けることができ、そのRock
に住所が定義されている限り、システムの一部でアイテムが確実に岩に配達されるようにすることができます。
これらすべての場合において、Rock
は単なるデータオブジェクトであり、仮説ではなく有用な結果を伴う特定の動作を定義するまで1のままです。
これを試してください:
「顧客」という言葉に2つの異なる意味を持つ可能性があることを避けるには、概念化を容易にする必要があります。
Rock
オブジェクトは注文を出しますか、それとも人間がUI要素をクリックしてシステムでアクションをトリガーすることによって行うことですか?