5

"Tell, don't ask" の原則に従って、OOP で getter を使用すべきではありません。

しかし、オブジェクトからの「内部情報」が実際に必要な場合 (少なくとも私はそう思います) の問題を解決するにはどうすればよいでしょうか? では、create_bill() 関数が各アイテムの価格を尋ねる必要がないように、次の例を変更するにはどうすればよいでしょうか?

class chopping_cart {

    private $itemlist = array();

    function item_add( $name, $price ) {
        $his->itemlist[]=new item( $name, $price );
    }
    private create_bill() {

        foreach $this->itemlist AS $element;
        $sum += $element->get_price();

    }
}


class item {
    private $name;
    private $price;
    function __construcor($name,$price) {...}
    function get_price() {
        return $price;
    }
}

使用法:

$sc = new shopping_cart()
$sc->item_add( "Bike", 1.00 );
$sc->item_add( "Ship", 2.00 );
$sc->create_bill();
4

3 に答える 3

7

参照しているオブジェクトを変更するために要求されたデータ/状態を使用していない場合は、ゲッターを使用しても問題はないと思います。

原則は、次のようなシナリオについて語っています。

if ($item->get_price() < 5) {
    $item->set_price(5);
}

それはのようなものに変わるはずです$item->set_minimum_price(5)

于 2012-04-30T22:31:06.377 に答える
4

あなたが言及している「教えて、聞かないでください」の記事から、そのクマの綿密な調査の2つの項目があります。

  1. [...] [オブジェクト]に状態について質問したり、決定を下したりしてから、何をすべきかを伝えないでください。
    あなたの特定の例でchopping_cartは、オブジェクトにクエリを実行して決定を下すだけです。重要なのは、彼らに何をすべきかを伝えることではありません。
  2. 契約による設計によれば、メソッド(クエリとコマンド)を自由に組み合わせることができ、そうすることでクラス不変条件に違反する方法がない限り、問題はありません。ただし、クラスを不変に維持している間、公開した状態の量によっては、呼び出し元と呼び出し先の間の結合が劇的に増加した可能性もあります。
    ゲッター呼び出しは一般に自由に混合できるため、クラスの不変条件を維持する必要性によって引き起こされる結合はありません。

したがって、ゲッターは、「教えて、聞かない」という原則が防ぐはずの問題を引き起こしません。

于 2012-04-30T22:49:58.217 に答える
2

この場合、 Visitor デザイン パターンを使用できます。Productクラスでメソッドを実装し、引数addToBillとして請求書インターフェイスを実装するインスタンスを渡しますIBill。アイテムで利用可能なすべての必要な情報を受け入れるIBillメソッドをサポートします。addToTotalあなたの場合、これは価格です。例えば:

interface IBill {
    /* needs to be public because PHP doesn't understand the concept of 
       friendship
     */
    function addToTotal($price);
}

class Bill implements IBill {
    private $total = 0;

    function addToTotal($price) {
        $this->total += $price;
    }
    ...
}

class ShoppingCart {
    private $items = array();

    function addItem($id, $product, $quantity) {
        if (isset($this->items[$id])) {
            $this->items[$id]->addQuantity($quantity);
        } else {
            $this->items[$id] = new LineItem($product, $quantity);
        }
    }

    private createBill() {
        $bill = new Bill;
        foreach ($this->items AS $lineItem) {
            $lineItem->addToBill($bill);
        }
        return ...;
    }
}

class LineItem {
    private $product, $quantity;
    function __constructor($product, $quantity) {...}
    function addToBill(IBill $bill) {
        $this->product->addToBill($bill, $quantity);
    }
    function addQuantity($quantity) {
        $this->quantity += $quantity;
    }
    ...
}

class Product {
    private $name, $description, $price;
    function __constructor(...) {...}
    function addToBill(IBill $bill, $quantity) {
        $bill->addToTotal($this->price * $quantity);
    }
    ...
}

ただし、常に不安定な地面に巻き込まれます。上の例では のようなメソッドが必要ですaddToTotal。これは不変条件 (合計は、項目の価格と数量の積の合計と一致する必要があります) を導入するものであり、まさに「教えて、聞かないで」というようなことを回避することになっています。あなたはなしでそれを試みることができますaddToTotal: 合計を追跡します。製品と数量に加えて価格を渡します。合計を更新します。クラスを使用していないか、あまり使用していないため、これはクラスを持つ目的をやや無効にします。これにより、渡された価格と製品が作成されたときに指定された価格が一致する必要があるという不変条件も追加されますが、一致しなくても問題は発生しません (単に奇妙になります)。*インスタンス化しているBillShoppingCartaddItemaddItemLineItemProductaddItemProductそしてLineItem; addItem合計を更新します。$price以前に追加された追加のアイテムを追加する場合、渡された値が以前の呼び出しで渡された量と一致する必要があるという追加の不変条件が存在するか、addItem単に追加の既存のアイテムを追加できないようにする必要があります。※アイテムはまとめて廃棄してください。ShoppingCart製品 ID と数量を格納します。を呼び出すたびaddItemに合計が更新されます。createBill計算済みの合計を使用します。他のものよりも、これは別々の懸念を結びつけます。

他にも考えられる設計はありますが、いずれも何らかの問題に悩まされています。通常は、関心の分離、不変条件の導入、複雑さの追加に関連しています。全体として、合計を計算するメソッド内で品目の合計価格に直接アクセスすることは、最も簡単であるだけでなく、最もクリーンで、エラーが発生する可能性が最も低くなります。

于 2012-04-30T22:51:54.977 に答える