データアクセスを超えるオブジェクト行動の解放
♪TL;DR:内部状態を暴露する代わりに操作を実行する行動豊かな方法でゲッターを削除または置き換える。
♪
TL;DR:内部状態を暴露する代わりに操作を実行する行動豊かな方法でゲッターを削除または置き換える。
問題解決 ↓↓
- ♪
- Anemic オブジェクト ♪
- 過剰なコネクタ ♪
- 失われたカプセル ♪
- 基本変異 ♪
- デメーター法則違反 ♪
- 情報流出 ♪
- 内部展示 ♪
- 原始的な執着 ♪
関連コード 臭い
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxv
ステップ↓
- ♪
- 内部オブジェクト状態を露出するゲッターを識別する ♪
- コードベースのすべての getter usages を見つける ♪
- Move behavior that uses the getter into the object itself (ゲッターをオブジェクトそのものに使用する動作) ♪
- オペレーションを実行する意図を明らかにする方法を作成する (get prefix remove) ♪
- 新しい方法を使用するためにコードを更新する ♪
サンプルコード
前の↓↓
public class Invoice {
private List<LineItem> items;
private Customer customer;
private LocalDate dueDate;
public Invoice(Customer customer, LocalDate dueDate) {
this.customer = customer;
this.dueDate = dueDate;
this.items = new ArrayList<>();
}
public void addItem(LineItem item) {
// This is the right way
// to manipulate the internal consistency
// adding assertions and access control if necessary
items.add(item);
}
public List<LineItem> getItems() {
// You are exposing your internal implementation
// In some languages, you also open a backdoor to
// manipulate your own collection unless you return
// a copy
return items;
}
public Customer getCustomer() {
// You expose your accidental implementation
return customer;
}
public LocalDate getDueDate() {
// You expose your accidental implementation
return dueDate;
}
}
Invoice invoice = new Invoice(customer, dueDate);
// Calculate the total violating encapsulation principle
double total = 0;
for (LineItem item : invoice.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// Check if the invoice is overdue
boolean isOverdue = LocalDate.now().isAfter(invoice.getDueDate());
// Print the customer information
System.out.println("Customer: " + invoice.getCustomer().getName());
その後↓
public class Invoice {
private List<LineItem> items;
private Customer customer;
private LocalDate dueDate;
public Invoice(Customer customer, LocalDate dueDate) {
this.customer = customer;
this.dueDate = dueDate;
this.items = new ArrayList<>();
}
public void addItem(LineItem item) {
items.add(item);
}
// Step 3: Move behavior that uses the getter into the object
public double calculateTotal() {
// Step 4: Create intention-revealing methods
double total = 0;
for (LineItem item : items) {
total += item.price() * item.quantity();
}
return total;
}
public boolean isOverdue(date) {
// Step 4: Create intention-revealing methods
// Notice you inject the time control source
// Removing the getter and breaking the coupling
return date.isAfter(dueDate);
}
public String customerInformation() {
// Step 4: Create intention-revealing methods
// You no longer print with side effects
// And coupling to a global console
return "Customer: " + customer.name();
}
// For collections, return an unmodifiable view if needed
// Only expose internal collaborators if the name
// is an actual behavior
public List<LineItem> items() {
return Collections.unmodifiableList(items);
}
// Only if required by frameworks
// or telling the customer is an actual responsibility
// The caller should not assume the Invoice is actually
// holding it
public String customerName() {
return customer.name();
}
// You might not need to return the dueDate
// Challenge yourself if you essentially need to expose it
// public LocalDate dueDate() {
// return dueDate;
// }
}
// Client code (Step 5: Update client code)
Invoice invoice = new Invoice(customer, dueDate);
double total = invoice.calculateTotal();
boolean isOverdue = invoice.isOverdue(date);
System.out.println(invoice.customerInformation());
タイプ↓
- ♪
- (x)半自動 ♪
セキュリティ ️
このリファクタリングは一般的に安全ですが、慎重な実行が必要です。
ゲッターのすべての用途が識別され、新しい行動方法で置き換えられるようにする必要があります。
最大のリスクは、ゲッターが変換可能なオブジェクトまたはコレクションを返す場合に発生するため、クライアントコードがこれらのオブジェクトを変更した可能性があります。
リファクタリングの前と後の包括的なテストを通じて、行動が変わっていないことを確認する必要があります。
コレクションの場合、変更できないコピーまたはビューを返し、移行中にセキュリティを維持します。 プロパティアクセスを必要とするフレームワークの場合、行動豊富な方法と共に「Get」プレフィックスなしでシンプルなアクセサリーを保存する必要があります。
いつものように、リファクタリングを行う前にコードに行動カバー(構造的ではない)を追加する必要があります。
なぜコードが良いのか?
The refactored code is better because it adheres to theTELL-DON'T-ASK原則として、あなたのオブジェクトは単に無血性のデータ保持者ではなく、スマートなものになります。
このソリューションは、オブジェクト自体内のオブジェクトのデータに関連する論理を集中させ、複製を減らし、実装の詳細を隠し、クライアントコードに影響を与えずに内部表示を変更することができます。
このアプローチは減らすカプセルクライアントは、オブジェクトの内部構造を知る必要はありません。
それはまた、ゲッターの鎖を除去することによって、デメーターの法則の違反を防ぐ。
以来のTheEssence is not muted. Essence is not muted.このソリューションは、オブジェクト内でより良い検証とビジネスルールの執行を可能にします。
では、どのようにしてバリエーションを改善するのか?️
ゲーターの除去は、改善するバイエクションコードと現実の間で、オブジェクトがより現実世界の同僚のように振る舞うようにすることです。
現実世界では、オブジェクトは、他人が操作するために内部状態を暴露しない - 彼らは要求に基づいて操作を実行します。
たとえば、あなたは銀行口座にその残高を尋ねず、それから引き出しが可能かどうかを自分で計算します。代わりに、あなたは口座に「私は100ドルを引き出すことができますか?」アカウントは内部ルールを適用し、あなたに答えを与えます。
データを露出するのではなく、オブジェクトをモデリングして操作を実行することにより、ドメインコンセプトのより忠実な表現を作成します。
これにより、現実世界とコンピュータモデル間の一対一の相応性を強化し、コードをより直感的で、人々が問題ドメインについてどのように考えているかと一致させることができます。
このアプローチは、Theマップコンピュータオブジェクトが構造と行動で現実世界の実体を反映することを確保することによって。
️制限
フレームワークや図書館は、シリアリング/deserialization のためのゲッターの方法をしばしば期待します。
伝統的なコードベースには、一度にすべてを再現するのが困難な広範なゲッターの使用があるかもしれません。
ユニットテストは、内部状態がアクセスしにくいため、より困難になる可能性があります。覚えておいて、プライベートな方法をテストするべきではありません。
リハーサル AI
♪Suggested Prompt: 1. Identify getters that expose internal object state 2. Find all getter uses in the codebase 3. Move behavior that uses the getter into the object itself 4. Create intention-revealing methods that perform operations (remove the get prefix) 5. Update your code to use the new methods
♪
Suggested Prompt: 1. Identify getters that expose internal object state 2. Find all getter uses in the codebase 3. Move behavior that uses the getter into the object itself 4. Create intention-revealing methods that perform operations (remove the get prefix) 5. Update your code to use the new methods
適切な指示なし
具体的な指示により
具体的な指示により
チャット
チャット
戸惑い
戸惑い
パイロット
パイロット
ディープ検索
ディープ検索
グロック
グロック
グロック
QWEN
QWEN
タグ ️
- ♪
- カプセル化 ♪
レベル
- ♪
- (x) 中間 ♪
関連記事↓↓
こちら↓↓↓
クレジット↓
この記事は、Refactoringシリーズの一部です。