根据下一个例子:
class InvoiceGenerator
{
function create(Invoice $invoice)
{
$invoice->create();
}
}
class InvoiceGenerator
{
function create($invoiceData)
{
$invoice = new Invoice();
$invoice->create($invoiceData);
}
}
第一个示例在InvoiceGenerator和Invoice类之间具有 less coupling ,因为InvoiceGenerator不需要Invoice类。此外,它不仅可以处理一个类,而且可以处理几乎没有修改的整个界面。我已经多次读过这样的原则:接口代码,而不是实现代码。这种情况的缺点是我被迫在客户端代码中实例化Invoice类。
第二个有更多封装。实例化和创建发票的所有过程都委托给InvoiceGenerator类。尽管两个类都是耦合的,但这是有道理的,因为发票生成器"没有发票就不会做任何事。
您认为哪种方式最合适?或者两者之间的平衡设计的关键点是什么?
答案 0 :(得分:3)
首先,我认为你混淆了一些事情。看起来您的InvoiceGenerator类是 factory class 。它的职责是创建并返回一个Invoice对象。 Invoice对象不是InvoiceGenerator的依赖(在这种情况下,将Invoice对象作为参数传递确实更好,称为 Dependency Injection )
然而,正如我所说,InvoiceGenerator类似乎是一个工厂类,工厂类的整个想法是实例化对象的逻辑被封装在该类中(如第二个例子中所示)。如果您将Invoice对象作为参数传递(如第一个示例中所示),则必须在其他地方将其实例化,如果您的代码中有多个位置发生这种情况,则最终会复制实例化逻辑。工厂模式的整个想法是为了防止这种情况。
通过使用工厂类,您可以封装对象实例化逻辑并避免重复。您还可以将一些更高级的逻辑放入InvoiceGenerator的create()
方法中,以确定要返回的对象类型。例如,如果总价格为Invoice
,您可能需要返回> 0
对象;如果价格为CreditNote
,则可能需要返回< 0
个对象。当然,它们都应该扩展相同的接口(例如InvoiceInterface
)。
此外,看起来您正在将Invoice对象的实际初始化委托给对象本身,因为InvoiceGenerator的create()
方法只是调用Invoice对象的create()
方法,实际逻辑似乎在哪里发生。这违反了single responsibility principle,因为Invoice类现在都负责保存有关发票的数据和设置数组中的所有数据。后者应该是工厂阶级的责任。
所以,我会创建这样的类:
class InvoiceFactory
{
public function create(array $data)
{
if ($data['total'] >= 0) {
$invoice = new Invoice();
} else {
$invoice = new CreditNote();
}
$invoice->setTotal($data['total']);
// set other data using setters as well
return $invoice;
}
}
如您所见,create()
对象上没有调用$invoice
方法。
答案 1 :(得分:1)
如果您想关注SOLID ,那么第一个选项是最接近满足要求的。
第一个选项,InvoiceGenerator类,负责创建发票并且不实例化发票对象。 发票对象可以用子类或实现发票接口的类替换(扩展名)。
第二个选项,发票对象是硬编码的,不能用于扩展。它可以修改 如果发票类的实施将来发生变化。此外,无法使用发票类的模拟实例测试(PHPUnit)InvoiceGenerator。
在我的观点中,选择适合您的应用程序需求的内容,同时考虑谁将使用和维护您的代码。
使InvoiceGenerator类更少耦合的选项,您不必在客户端代码中实例化发票对象,但您可以选择客户端代码来设置自己的发票对象实例。
class InvoiceGenerator
{
function create(InvoiceInterface $invoice = null)
{
if (null === $invoice) {
$invoice = new Invoice();
}
$invoice->create();
}
}
答案 2 :(得分:1)
考虑到php作为语言,这实际上是一个困难的设计问题。
我确实更喜欢耦合而不是封装(DRY不要重复自己)。
对于我自己,我已经建立了发票软件,这通常是我的结构化方式。
输入: 工作小时数(小时) 已售物品(物品) 进入InvoiceGenerator,输出发票。
class Hourly {
}
class Items {
}
Class InvoiceGenerator {
//Returns Invoice
//A list of hourly objects and items
function createInvoice($state, $hourly = array(), $items = array() {
$invoice = new Invoice($hourly, $items);
$invoice->applyTaxes($state):
//Do Other Things
return $invoice;
}
}
我所有项目的关键点是#1 Readabilioty和#2 Not Repeating yourself 通过封装您的数据,您可以使测试更加困难,而且灵活性也会降低。
数组数据可以像PHP一样在PHP中抛出,但是你错过了在数据上拥有其他函数的能力。例如,如果1个对象是TAX豁免。您现在必须为您的海量阵列结构添加检查功能。
或者使用较少耦合的方法,您只需要一个返回税值(0或其他)的方法。
我希望这个例子说明为什么较少耦合的方法通常是更好的方法。
答案 3 :(得分:0)
您应该考虑使用Factory Method Design Pattern来解决问题:
class InvoiceFactory
{
static function create($invoiceData)
{
$invoice = new Invoice();
$invoice->create($invoiceData);
return $invoice;
}
}