2013-02-19 92 views
0

假设您需要构建一个管理支票的应用程序。每张支票都包含有关金额,日期,收款人以及可能存在或不存在的额外支付日期的数据。此外,每张支票必须与属于某个银行的往来账户相关。 现在,我们的应用程序应该允许检查在这些条件下进行打印:复杂继承场景

  • 每个存储由该应用管理具有不同的检查的布局(即,每个场具有不同的x,y位置)。

  • 如果支付日期存在,检查布局稍有变化,即使使用相同的相关银行对象。不过,因银行这些变化可能是不一样的(如银行A可能会发生变化之日起场上的位置,而隔壁B为收款人场改变位置)

有了这些限制,因此很难想出一个简单的继承模式,因为没有一致的行为来分解不同类型的检查。一个可能的解决办法是,以避免继承和每一个检查创建一个类 - 银行组合:

  • 类ChequeNoPaymentDateForBankA
  • 类ChequeWithPaymentDateForBankA
  • 类ChequeNoPaymentDateForBankB
  • 类ChequeWithPaymentDateForBankB等

这些类中的每一个都实现了print()方法,该方法从Bank对象中获取字段位置并构建支票簿吨。目前为止这么好,但是这种方法给我留下了一种奇怪的感觉,因为没有足够的代码重用空间。我想知道我是否会误解这个问题,也许还有更好的办法。由于这根本不是一个新问题,我相信这是一个重头戏的努力。任何见解将受到赞赏。

+0

如果它们都具有相同的坐标不同的组件,可以考虑在同一个对象中使用不同的构造函数。 – Dukeling 2013-02-19 05:31:46

+1

可能是部分使用http://en.wikipedia.org/wiki/Visitor_pattern进行打印? – 2013-02-19 06:19:35

回答

0

我会从视图(检查打印的方式)分离领域模型(支票,银行等)开始。这是MVC模式背后的基本思想,其目标之一是允许以不同方式显示相同的域模型(这似乎是您的情况)。所以,我会首先创建域类,类似:

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() {...} 
} 

而且,如果有代码跨子类重用,你可以将它们在ChequeView,使coreRender()当然因素给他们打电话。

如果您的渲染过于复杂,这种设计可能无法扩展。在这种情况下,我会把你的视图分解成有意义的子部分(例如HeaderViewAmountView等),以便渲染一张支票变得基本上呈现其不同的子部分。在这种情况下,ChequeView可能会基本上以Composite的形式工作。最后,如果您遇到这种情况,并且设置ChequeView事实证明是一项复杂的任务,您可能需要使用Builder。基于在OP

编辑点评

构建器主要用于当最终对象的实例化是一个复杂的过程(例如,有很多事情,为了得到一个子部分之间同步一致的整体)。通常有一个构建器类和不同的客户端,它们发送消息(可能以不同的顺序和不同的参数)来创建各种最终对象。所以,虽然没有被禁止,但每个要构建的对象类型都有一个构建器并不常见。

如果您正在寻找表示创建特定实例的类,则可能需要检查Factory模式族(可能Abstract Factory与您的想法非常相似)。

HTH

+0

我想你明白了,将检查字段拆分成分离的渲染组件是我的选择之一,除了我从未想过Builder模式。正如你所说,在这种情况下,扩展是至关重要的,所以我正在将Builder作为一种去路。 – JCharly 2013-02-19 15:45:42

+0

我想要应用Builder模式,我必须为每个支票银行组合创建一个建造者类,类似于我的原始建议。在这种情况下,'ChequeWithPaymentDateForBankA'将变为'ChequeWithPaymentDateForBankABuilder'来创建一个渲染对象。另外,我需要一个'PrintingItem'实例的集合,它将携带x,y位置和一个文本字段。最后,渲染对象将有一个'print()'方法,它简单地遍历每个'PrintingItem'并打印它们。 – JCharly 2013-02-19 16:06:12

+0

@JCharly:请根据您的意见查看编辑。 – 2013-02-19 18:27:14

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()算法的确切细节,您可能需要将更多数据传递给打印方法和/或将这些数据传递给类(Bank或​​PaymentSchedule子类)的构造函数,并存储它在田野里。

+0

这是一个非常好的起点,但问题在于很难将常见代码分解并放入Bank类中,因为银行打印的内容(我的意思是位置和字段本身)会更改依赖于存在的付款日期。 – JCharly 2013-02-19 15:55:01