Có phải là một ý tưởng tốt để định nghĩa một hàm riêng lớn trong một lớp để duy trì trạng thái hợp lệ, nghĩa là, để cập nhật các thành viên dữ liệu của đối tượng?


18

Mặc dù trong mã bên dưới, một giao dịch mua một mặt hàng đơn giản trong một trang web thương mại điện tử được sử dụng, câu hỏi chung của tôi là về việc cập nhật tất cả các thành viên dữ liệu để giữ dữ liệu của đối tượng ở trạng thái hợp lệ mọi lúc.

Tôi thấy "tính nhất quán" và "trạng thái là xấu xa" là các cụm từ có liên quan, được thảo luận tại đây: https://en.wikibooks.org/wiki/Object_Orients_Programming#.22State.22_is_Evil.21

<?php

class CartItem {
  private $price = 0;
  private $shipping = 5; // default
  private $tax = 0;
  private $taxPC = 5; // fixed
  private $totalCost = 0;

  /* private function to update all relevant data members */
  private function updateAllDataMembers() {
    $this->tax =  $this->taxPC * 0.01 * $this->price;
    $this->totalCost = $this->price + $this->shipping + $this->tax;
  }

  public function setPrice($price) {
      $this->price = $price;
      $this->updateAllDataMembers(); /* data is now in valid state */
  }

  public function setShipping($shipping) {
    $this->shipping = $shipping;
    $this->updateAllDataMembers(); /* call this in every setter */
  }

  public function getPrice() {
    return $this->price;
  }
  public function getTaxAmt() {
    return $this->tax;
  }
  public function getShipping() {
    return $this->shipping;
  }
  public function getTotalCost() {
    return $this->totalCost;
  }
}
$i = new CartItem();
$i->setPrice(100);
$i->setShipping(20);
echo "Price = ".$i->getPrice(). 
  "<br>Shipping = ".$i->getShipping().
  "<br>Tax = ".$i->getTaxAmt().
  "<br>Total Cost = ".$i->getTotalCost();

Bất kỳ nhược điểm, hoặc có thể cách tốt hơn để làm điều này?

Đây là sự cố định kỳ trong các ứng dụng trong thế giới thực được hỗ trợ bởi cơ sở dữ liệu quan hệ và nếu bạn không sử dụng rộng rãi các quy trình được lưu trữ để đẩy tất cả xác thực vào cơ sở dữ liệu. Tôi nghĩ rằng kho lưu trữ dữ liệu chỉ nên lưu trữ dữ liệu, trong khi mã phải thực hiện tất cả trạng thái thời gian chạy duy trì công việc.

EDIT: đây là một câu hỏi liên quan nhưng không có khuyến nghị thực hành tốt nhất liên quan đến một chức năng lớn duy nhất để duy trì trạng thái hợp lệ: /programming/1122346/c-sharp-object-oriented-design-maintained- trạng thái đối tượng hợp lệ

EDIT2: Mặc dù câu trả lời của @ eignesheep là tốt nhất, nhưng câu trả lời này - /software//a/148109/208591 - là những gì lấp đầy các dòng giữa câu trả lời của @ eigensheep và những gì tôi muốn biết - mã chỉ nên xử lý, và trạng thái toàn cầu nên được thay thế bằng trạng thái chuyển qua trạng thái cho phép DI giữa các đối tượng.


Tôi tránh có các biến là tỷ lệ phần trăm. Bạn có thể chấp nhận tỷ lệ phần trăm từ người dùng hoặc hiển thị một tỷ lệ cho người dùng, nhưng cuộc sống sẽ tốt hơn nhiều nếu các biến chương trình là tỷ lệ.
kevin cline

Câu trả lời:


29

Tất cả những thứ khác đều bằng nhau, bạn nên thể hiện bất biến của mình bằng mã. Trong trường hợp này bạn có bất biến

$this->tax =  $this->taxPC * 0.01 * $this->price;

Để thể hiện điều này trong mã của bạn, hãy xóa biến thành viên thuế và thay thế getTaxAmt () bằng

public function getTaxAmt() {
  return $this->taxPC * 0.01 * $this->price;
}

Bạn nên làm một cái gì đó tương tự để thoát khỏi tổng số thành viên biến.

Thể hiện sự bất biến của bạn trong mã của bạn có thể giúp tránh các lỗi. Trong mã gốc, tổng chi phí không chính xác nếu được chọn trước khi setprice hoặc setShipping được gọi.


3
Nhiều ngôn ngữ có getters để các chức năng như vậy sẽ giả vờ chúng là tài sản. Tốt nhất của cả hai!
tò mò

Điểm tuyệt vời, nhưng trường hợp sử dụng chung của tôi là nơi mã tìm nạp và lưu trữ dữ liệu vào nhiều cột trong nhiều bảng trong cơ sở dữ liệu quan hệ (chủ yếu là MySQL) và tôi không muốn sử dụng các thủ tục được lưu trữ (tranh cãi và một chủ đề khác). Đưa ý tưởng bất biến trong mã của bạn đi xa hơn, điều này có nghĩa là tất cả các tính toán phải được "xâu chuỗi": getTotalCost()các cuộc gọi getTaxAmt(), v.v. Điều này có nghĩa là chúng tôi chỉ lưu trữ những thứ không tính toán . Có phải chúng ta đang di chuyển một chút về lập trình chức năng? Điều này cũng làm phức tạp việc lưu trữ các thực thể được tính toán trong các bảng để truy cập nhanh ... Cần thử nghiệm!
web80443

13

Bất kỳ nhược điểm nào [?]

Chắc chắn rồi. Phương pháp này dựa vào tất cả mọi người luôn nhớ để làm một cái gì đó. Bất kỳ phương pháp nào dựa vào tất cả mọi người & luôn bị ràng buộc để thất bại đôi khi.

có lẽ cách tốt hơn để làm điều này?

Một cách để tránh gánh nặng của buổi lễ ghi nhớ là tính toán các thuộc tính của đối tượng phụ thuộc vào các thuộc tính khác khi cần, như @eigensheep đề xuất.

Một cách khác là làm cho mục giỏ hàng không thay đổi và tính toán nó trong phương thức khởi tạo / phương thức xuất xưởng. Bạn thường sẽ đi với phương pháp "tính toán khi cần thiết", ngay cả khi bạn làm cho đối tượng không thay đổi. Nhưng nếu việc tính toán quá tốn thời gian và sẽ được đọc nhiều, nhiều lần; bạn có thể chọn tùy chọn "tính toán trong khi tạo".

$i = new CartItem();
$i->setPrice(100);
$i->setShipping(20);

Bạn nên tự hỏi mình; Liệu một mặt hàng giỏ hàng mà không có giá có ý nghĩa? Giá của một mặt hàng có thể thay đổi? Sau khi nó được tạo ra? Sau khi tính thuế? vv Có lẽ bạn nên thực hiện giá CartItembất biến và assig và vận chuyển trong nhà xây dựng:

$i = new CartItem(100, 20);

Liệu một mặt hàng giỏ hàng có ý nghĩa mà không có giỏ hàng nó thuộc về?

Nếu không, tôi mong đợi $cart->addItem(100, 20)thay thế.


3
Bạn chỉ ra nhược điểm lớn nhất: dựa vào những người nhớ làm việc hiếm khi là một giải pháp tốt. Về điều duy nhất bạn có thể dựa vào một con người để làm là họ sẽ quên làm điều gì đó.
corsiKa

@corsiKl Because Ho Ho Ho và abuzittin, điểm vững chắc, không thể tranh luận với điều đó - mọi người quên luôn . Tuy nhiên, mã tôi đã viết ở trên chỉ là một ví dụ, có những trường hợp sử dụng đáng kể trong đó một số thành viên dữ liệu được cập nhật sau. Một cách khác mà tôi thấy là bình thường hóa hơn nữa - tạo các lớp sao cho các thành viên dữ liệu được cập nhật độc lập ở các lớp khác và đẩy trách nhiệm cập nhật lên một số giao diện - để các lập trình viên khác (và chính bạn sau một thời gian) phải viết một phương thức - trình biên dịch nhắc nhở bạn rằng bạn phải viết nó. Nhưng điều đó sẽ thêm rất nhiều lớp nữa ...
site80443

1
@ site80443 Dựa trên những gì tôi thấy, đó là cách tiếp cận sai. Cố gắng mô hình hóa dữ liệu của bạn sao cho chỉ có dữ liệu được xác thực dựa trên dữ liệu đó. Ví dụ, giá của một mặt hàng không thể âm chỉ dựa vào chính nó. Nếu một mặt hàng được giảm giá, đừng tính yếu tố giảm giá vào giá - hãy trang trí nó với giảm giá sau. Lưu trữ 4,99 đô la cho mặt hàng và giảm giá 20% dưới dạng một thực thể riêng biệt và thuế 5% như một thực thể khác. Có vẻ như bạn nên xem xét mẫu Trang trí nếu các ví dụ đại diện cho mã thực tế của bạn.
corsiKa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.