2012-04-30 35 views
5

Sau "Tell, don't Ask"-principle, bạn không nên sử dụng getters trong OOP.OOP - không cần GET/SET

Nhưng làm thế nào để giải quyết vấn đề mà (ít nhất tôi nghĩ như vậy) một trong những thực sự cần một số "thông tin bên trong" từ một đối tượng? Vậy làm thế nào để thay đổi ví dụ sau để hàm create_bill() không cần ASK cho giá cho mỗi mục?

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

Cách sử dụng:

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

nếu giỏ hàng có chứa các mặt hàng, tại sao việc mua giỏ hàng lại có thể tìm hiểu giá trị của từng mặt hàng là điều xấu? – user12345613

+1

theo thông báo "Nói không hỏi" -principle nó có vẻ là ác. – Tom

+0

Bạn nghe nguyên tắc "nói không hỏi" ở đâu? Tôi chưa bao giờ nghe nói về nó. – xxpor

Trả lời

7

Nếu bạn không sử dụng các dữ liệu yêu cầu/trạng thái thay đổi đối tượng mà bạn đang đề cập đến, tôi không nghĩ rằng có điều gì sai trái với việc sử dụng thu khí ở tất cả .

Các cuộc đàm phán nguyên tắc về một kịch bản như thế này:

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

Điều đó sẽ được biến thành một cái gì đó giống như $item->set_minimum_price(5).

4

Có hai mục từ "Tell, Don't Ask" bài báo mà bạn đang đề cập đến rằng gấu kiểm tra chặt chẽ:

  1. [...] đừng hỏi [đối tượng] câu hỏi về tình trạng của họ, đưa ra quyết định và sau đó nói cho họ biết phải làm gì.
    Với ví dụ cụ thể của bạn, chopping_cart chỉ truy vấn các đối tượng & đưa ra quyết định. Quan trọng, nó không tiếp tục nói cho họ biết phải làm gì.
  2. Theo Thiết kế theo hợp đồng, miễn là các phương pháp (truy vấn và lệnh) của bạn có thể được trộn lẫn tự do và không có cách nào vi phạm sự bất biến của lớp bằng cách làm như vậy, thì bạn ổn. Nhưng trong khi bạn đang duy trì sự bất biến của lớp, bạn cũng có thể tăng đáng kể sự ghép nối giữa người gọi và callee tùy thuộc vào trạng thái bạn đã tiếp xúc.
    Các cuộc gọi Getter thường có thể được tự do trộn lẫn, do đó không có sự ghép nối nào do nhu cầu duy trì các bất biến lớp.

Vì vậy, getters không gây ra các vấn đề mà nguyên tắc "nói, đừng hỏi" được cho là ngăn chặn.

2

Bạn có thể sử dụng Visitor design pattern trong trường hợp này. Trong lớp Product của bạn, hãy triển khai phương thức addToBill và làm đối số chuyển một cá thể triển khai giao diện hóa đơn của bạn, IBill. IBill hỗ trợ phương thức addToTotal sẽ chấp nhận tất cả thông tin cần thiết có sẵn trong mục; trong trường hợp của bạn, đây là một mức giá. Ví dụ:

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); 
    } 
    ... 
} 

Tuy nhiên, bạn sẽ luôn luôn gió trên mặt đất run rẩy. Ở trên đòi hỏi một phương pháp như addToTotal, trong đó giới thiệu một bất biến (tổng số phải phù hợp với tổng của các sản phẩm của giá mục và số lượng), chỉ là loại điều "Tell, Don't Ask" là nghĩa vụ phải tránh. Bạn có thể thử làm điều đó mà không cần addToTotal: * Làm việc với Bill; ShoppingCart theo dõi tổng số. Chuyển giá thành addItem ngoài sản phẩm & số lượng; addItem cập nhật tổng số. Điều này phần nào đánh bại mục đích của việc có các lớp học, vì bạn không sử dụng LineItem hoặc Product cho nhiều.Điều này cũng cho biết thêm một bất biến rằng giá đã vượt qua và giá được đưa ra khi sản phẩm được tạo nên phù hợp, mặc dù nếu không, nó sẽ không gây ra vấn đề (nó sẽ chỉ là lạ). * Có addItem khởi tạo ProductLineItem; addItem cập nhật tổng số. Khi thêm các mục bổ sung đã được thêm trước đó, phải có thêm bất biến mà số được chuyển vào $price phải khớp với số tiền được chuyển trong các cuộc gọi trước hoặc addItem không thể được phép thêm các mục hiện có bổ sung. * Làm tất cả mọi thứ với nhau. ShoppingCart lưu trữ ID và số lượng sản phẩm. Mỗi cuộc gọi đến addItem sẽ cập nhật tổng số. createBill sử dụng tổng số đã tính. Thậm chí nhiều hơn những người khác, điều này kết hợp separate concerns.

Có các thiết kế tiềm năng khác, nhưng mỗi loại đều gặp một số vấn đề, thường liên quan đến việc tách mối quan tâm, giới thiệu các bất biến và thêm độ phức tạp. Tất cả cùng nhau, truy cập vào tổng giá của chi tiết đơn hàng trực tiếp trong phương pháp tính tổng số không chỉ là lỗi đơn giản nhất, nhưng sạch nhất và ít có khả năng sản xuất nhất.