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 Product
và LineItem
; 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.
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
theo thông báo "Nói không hỏi" -principle nó có vẻ là ác. – Tom
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