0

小切手を管理するアプリケーションを構築する必要があるとしましょう。各小切手には、金額、日付、受取人、および存在する場合と存在しない場合がある追加の支払日に関するデータが含まれています。さらに、各小切手は、特定の銀行に属する当座預金口座に関連付けられている必要があります。これで、アプリケーションは次の条件下で小切手の印刷を許可する必要があります。

  • アプリによって管理される各銀行には、異なるチェック レイアウトがあります (つまり、各フィールドの x、y 位置は異なります)。

  • 関連する銀行オブジェクトが同じであっても、支払日が存在する場合、小切手のレイアウトはわずかに変化します。ただし、銀行ごとにこれらの変更は同じではない場合があります (たとえば、銀行 A では日付フィールドの位置が異なり、銀行 B では受取人フィールドの位置が異なる場合があります)。

これらの制限が適用されると、さまざまな種類のチェックを考慮に入れるための一貫した動作がないため、単純な継承スキーマを考え出すことは困難です。考えられる解決策の 1 つは、継承を回避し、小切手と銀行の組み合わせごとにクラスを作成することです。

  • クラス ChequeNoPaymentDateForBankA
  • クラス ChequeWithPaymentDateForBankA
  • クラス ChequeNoPaymentDateForBankB
  • クラス ChequeWithPaymentDateForBankB など

これらの各クラスは、Bank オブジェクトからフィールドの位置を取得し、小切手のレイアウトを作成する print() メソッドを実装します。ここまでは順調ですが、コードを再利用する余地がないため、このアプローチには奇妙な感覚が残ります。私は問題を誤解しているのか、おそらくもっと良い方法があるのだろうか。これはまったく新しい問題領域ではないので、これは車輪の再発明の取り組みであると確信しています。どんな洞察も親切に感謝されます。

4

2 に答える 2

1

通常、このような状況では、継承から委任に移行します。つまり、共通コードをスーパークラスに配置する代わりに (これは、2 つの次元があるために問題が発生します)、共通コードをフィールド (次元ごとに 1 つのフィールド) に配置し、そのフィールドにデリゲートします。

Java について話していると仮定すると、次のようになります。

public interface Bank {
   public void print();
}

public class BankA implements Bank {
   public void print() { ... }
}

public class BankB implements Bank {
   public void print() { ... }
}


public interface PaymentSchedule {
   public void print();
}

public class WithPaymentDate implements PaymentSchedule {
   public void print() { ... }    
}

public class NoPaymentDate implements PaymentSchedule {
   public void print() { ... }    
}

public class Cheque {
  private final Bank bank;
  private final PaymentSchedule schedule;


  public Cheque(Bank b, PaymentSchedule s) {
     bank = b;
     schedule = s;
  }


  public void print() {
     bank.print();
     schedule.print();
  }
}

それがソリューションの一般的な構造です。

print() アルゴリズムの正確な詳細によっては、さらにデータを print メソッドに渡したり、このデータを (Bank または PaymentSchedule サブクラスの) クラスのコンストラクターに渡してフィールドに格納したりする必要がある場合があります。

于 2013-02-19T05:47:27.357 に答える
0

まず、ドメインモデル(小切手、銀行など)をビュー(小切手の印刷方法)から分離します。これはMVCパターンの背後にある基本的な考え方であり、その目的の1つは、同じドメインモデルをさまざまな方法で表示できるようにすることです(これはあなたの場合のようです)。したがって、最初に次のようなドメインクラスを作成します。

class Cheque 
{
protected $bank;
protected $money;
...
}

class Bank {...}

これらのクラスはMVCトライアドの「M」であり、レンダリングプロセスに関連する動作ではなく、ドメインモデルのロジックを実装することに注意してください。次のステップは、小切手をレンダリングするために使用されるViewクラスを実装することです。どのアプローチを採用するかは、レンダリングの複雑さによって大きく異なりますが、ChequeViewまず、共通部分をレンダリングし、変更可能な特定の部分(この場合は日付)を他のサブビューに委任するクラスを作成します。

abstract class ChequeView
{
protected $cheque;
protected $dateView;

public function __construct($cheque)
{
  $this->cheque = $cheque;
  $this->dateView = //Whatever process you use to decide if the payment date is shown or not
}

public function render()
{
  $this->coreRender();
  $this->dateView->render();
}

abstract protected function coreRender();
}

class BankACheckView extends ChequeView
{
protected function coreRender() {...}
}

class BankBCheckView extends ChequeView
{
protected function coreRender() {...}
}

abstract class DateView
{
abstract function render()
}

class ShowDateView extends DateView
{
function render() {...}
}

class NullDateView extends DateView
{
function render() {...}
}

また、サブクラス間で再利用するコードがある場合は、もちろんそれらを考慮に入れChequeViewcoreRender()呼び出すことができます。

レンダリングが複雑になりすぎる場合、このデザインは拡大縮小されない可能性があります。その場合、ビューを意味のあるサブパート(たとえばHeaderViewAmountViewなど)に分割して、チェックのレンダリングが基本的に異なるサブパートのレンダリングになるようにします。この場合、ChequeViewは基本的にコンポジットとして機能しなくなる可能性があります。最後に、このケースに到達し、セットアップChequeViewが複雑なタスクであることが判明した場合は、Builderを使用することをお勧めします。

OPコメントに基づいて編集

Builderは主に、最終オブジェクトのインスタンス化が複雑なプロセスである場合に使用されます(たとえば、一貫性のある全体を取得するためにサブパーツ間で同期するものがたくさんあります)。通常、1つのビルダークラスとさまざまなクライアントがあり、メッセージを送信して(場合によってはさまざまな順序でさまざまな引数を使用して)、さまざまな最終オブジェクトを作成します。したがって、禁止されていませんが、ビルドするオブジェクトのタイプごとに1つのビルダーを使用することは通常ありません。

特定のインスタンスの作成を表すクラスを探している場合は、ファクトリファミリーのパターンを確認することをお勧めします(おそらく、アブストラクトファクトリはあなたが考えていたものに非常に似ています)。

HTH

于 2013-02-19T12:00:57.370 に答える