松散耦合与封装。平衡设计的最佳方法

时间:2014-12-04 18:43:34

标签: php encapsulation loose-coupling coupling

根据下一个例子:

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类。尽管两个类都是耦合的,但这是有道理的,因为发票生成器"没有发票就不会做任何事。

您认为哪种方式最合适?或者两者之间的平衡设计的关键点是什么?

4 个答案:

答案 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;
   }
}