1

对于我关于如何以有益的方式使用 OOP 的问题,我假设一个 BASKET 作为一个例子,它的所有者(Tom)具有一定的地址(NY)可以添加文章(自行车,汽车)。最后打印一张包含所有这些信息的账单。

我的问题是:如何处理从多个对象收集所需的信息(这里:所有者、城市、物品数量)?因为我认为按照下面的方式手动执行此操作是愚蠢的(见 4.),不是吗?(由于现实中信息量的增加,甚至更多)

那么创建账单/收集此示例所需信息的“干净方式”是什么?

<?php
$a = new basket('Tom','NY');
$a->add_item("Bike",1.99);
$a->add_item("Car",2.99);

$b = new bill( $a );
$b->do_print();

1.

class basket {

    private $owner = "";
    private $addr = "";
    private $articles = array();

    function basket( $name, $city ) {
        // Constructor
        $this->owner = $name;
        $this->addr = new addresse( $city );

    }

    function add_item( $name, $price ) {
        $this->articles[] = new article( $name, $price );
    }

    function item_count() {
        return count($this->articles);
    }

    function get_owner() {
        return $this->owner;
    }

    function get_addr() {
        return $this->addr;
    }

}

2.

class addresse {

    private $city;

    function addresse( $city ) {
        // Constructor
        $this->city = $city;
    }

    function get_city() {
        return $this->city;
    }

}

3.

class article {

    private $name = "";
    private $price = "";

    function article( $n, $p ) {
        // Constructor
        $this->name = $n;
        $this->price = $p;
    }   

}

4.

class bill {

    private $recipient = "";
    private $city = "";
    private $amount = "";

    function bill( $basket_object ) {

        $this->recipient = $basket_object->get_owner();
        $this->city = $basket_object->get_addr()->get_city();
        $this->amount = $basket_object->item_count();

    }

    function do_print () {
        echo "Bill for " . $this->recipient . " living in " . $this->city . " for a total of " . $this->amount . " Items.";
    }

}
4

3 回答 3

2

如果您执行Tell Dont Ask,您确实会向您传递 BillRenderer 实例的账单添加一个渲染方法。Bill 然后会告诉 BillRenderer 如何渲染 Bill。这符合InformationExpert 和高内聚原则,这些原则建议在具有最多信息的对象上执行任务的方法。

class Bill
{
    …
    public function renderAs(BillRenderer $billRenderer)
    {
        $billRenderer->setRecipient($this->owner);
        $billRenderer->setAddress($this->address);
        …
        return $billRenderer->render();
    }
}

BillRenderer(一个接口)然后会知道输出格式,例如,你会为 PlainText 或 HTML 或 PDF 编写具体的渲染器:

class TxtBillRenderer implements BillRenderer
{
    …
    public function render()
    {
        return sprintf('Invoice for %s, %s', $this->name, $this->address);
    }
}

echo $bill->renderAs(new TxtBillRenderer);

如果您的 Bill 包含其他对象,那么这些对象也将实现 renderAs 方法。然后,Bill 会将渲染器向下传递给这些对象。

于 2012-04-30T18:28:27.393 回答
1

篮子和账单都可能与仓位项目相关 - 一个表示零个或多个项目的有序列表的对象,其中包含计数和价格。

由于这样的列表是它自己的对象,因此很容易传递:

$bill = new Bill($buyer, $address, $basket->getPositions());

但是账单的打印应该由 来完成BillPrinter,因为打印本身不是账单的工作:

$billPrinter = new BillPrinter($bill, $printerDevice);
$billPrinter->print();
于 2012-04-30T17:38:17.883 回答
1

首先,在 PHP5 中的构造函数吧public function __construct()。你在那里使用的是 PHP4 方式。然后你的代码还有其他问题:

  • 而不是将城市名称Basket传递给(您的意思是Cart?),您应该创建地址对象实例并传递它。
  • 不要根据名称和金额添加项目到篮子,而是添加项目的整个实例,否则在切换站点语言或货币时会遇到很多问题。
  • Articles(你的意思是Items?)应该基于 ID 创建,而不是基于名称。原因与上述相同+您将遇到唯一性问题。然后一些项目可能有更低的价格,当组合购买时。您需要一种安全识别它们的方法。

至于清理那里的代码:

  • 您应该停止从构造函数中的给定参数创建实例。虽然这并不总是一件坏事,但在你的情况下,你在那里弄得一团糟。
  • Bill不应对印刷本身负责。

就像是 :

class Basket
{
    // -- other code 

    public function handleInvoice( Bill $invoice )
    {
        $invoice->chargeFor( $this->items );
        $invoice->chargeTo( $this->account );
        return $invoice->process();
    }
}

..然后将其用作

$cart = new Basket(..);
// some operation with it

$invoice = new Bill;
$cart->handleInvoice($invoice);

$printer = new PDFPrinter;
// OR new JpegPrinter; OR new FakePrinter OR anything else
$printer->print( $invoice );

这会给你一个Bill类外的实例,然后你可以打印或发送给某人。

此外,您可能会从观看柳树讲座中受益:

于 2012-05-01T05:02:24.390 回答