根据下面的例子:
class InvoiceGenerator
{
function create(Invoice $invoice)
{
$invoice->create();
}
}
class InvoiceGenerator
{
function create($invoiceData)
{
$invoice = new Invoice();
$invoice->create($invoiceData);
}
}
第一个示例在 InvoiceGenerator 和 Invoice 类之间的耦合较少,因为 InvoiceGenerator 不需要 Invoice 类。 此外,它不仅可以处理一个类,还可以处理整个界面,只需很少的修改。我已经读过很多次这个原则:代码到接口,而不是实现。这种情况的缺点是我被迫在客户端代码中实例化 Invoice 类。
第二个有更多的封装。实例化和创建发票的所有过程都委托给 InvoiceGenerator 类。即使这两个类是耦合的,这也是有道理的,因为"发票生成器"在没有发票的情况下不会做任何事情。
您认为哪个最合适?或者两者之间平衡设计的关键点是什么?
首先,我认为你混淆了一些东西。看起来您的发票生成器类是工厂类。它的职责是创建并返回发票对象。发票对象不是发票生成器的依赖项(在这种情况下,最好将发票对象作为参数传递,称为依赖项注入(。
但是,正如我所说,InvoiceGenerator 类似乎是一个工厂类,工厂类的整个思想是将实例化对象的逻辑封装在该类中(如第二个示例所示(。如果将 Invoice 对象作为参数传递(如第一个示例所示(,则必须在其他地方实例化它,如果代码中有多个发生这种情况的位置,则最终会复制实例化逻辑。工厂模式的整个想法就是为了防止这种情况。
通过使用工厂类,可以封装对象实例化逻辑并避免重复。您还可以将一些更高级的逻辑放入 InvoiceGenerator 的 create()
方法中,以确定要返回的对象类型。例如,如果总价格为 > 0
,您可能希望返回一个Invoice
对象,但如果价格为 < 0
,则返回一个CreditNote
对象。当然,它们都应该扩展相同的接口(例如InvoiceInterface
(。
此外,看起来您正在将发票对象的实际初始化委托给对象本身,因为 InvoiceGenerator 的create()
方法只不过是调用发票对象的create()
方法,其中实际逻辑似乎发生了。这违反了单一责任原则,因为 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;
}
}
如您所见,没有在$invoice
对象上调用create()
方法。
如果你想遵循SOLID的,则第一个选项最接近满足要求。
第一个选项是 InvoiceGenerator 类,该类负责创建发票,并且不实例化发票对象。发票对象可以替换为子类或实现发票接口的类。
第二个选项是发票对象硬编码的,并且不开放扩展。它开放修改如果将来发票类别的实现发生变化。此外,无法使用发票类的模拟实例测试 (PHPUnit( 发票生成器。
在我看来,选择适合您的应用程序需要的内容,同时考虑谁将使用和维护您的代码。
一个选项,用于降低 InvoiceGenerator 类的耦合度,您不必在客户端代码中实例化发票对象,但您为客户端代码提供了设置其自己的发票对象实例的选项。
class InvoiceGenerator
{
function create(InvoiceInterface $invoice = null)
{
if (null === $invoice) {
$invoice = new Invoice();
}
$invoice->create();
}
}
考虑到 php 作为语言,这实际上是一个困难的设计问题。
我确实更喜欢较少的耦合而不是封装(DRY 不要重复自己(。
就我自己而言,我已经构建了发票软件,这通常是我构建它的方式。
输入:工作小时数(每小时(已售商品(商品(进入输出发票的发票生成器。
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 不要重复自己通过封装数据,您可以使其更难测试,并且灵活性降低。
数组数据可以在PHP中像对象一样被抛出,但你错过了对数据有其他函数的能力。例如,如果 1 个对象是免税的。您现在必须将检查该功能添加到大型数组结构中。
或者,使用不太耦合的方法,您只有一个返回税值(0 或其他(的方法。
我希望这个例子能说明为什么不太耦合的方法往往是更好的方法。
您应该考虑使用工厂方法设计模式来解决问题,如下所示:
class InvoiceFactory
{
static function create($invoiceData)
{
$invoice = new Invoice();
$invoice->create($invoiceData);
return $invoice;
}
}