Чи є хорошою ідеєю визначити одну велику приватну функцію в класі для підтримки дійсного стану, тобто оновлення членів даних об’єкта?


18

Хоча в наведеному нижче коді використовується проста покупка одного товару на веб-сайті електронної комерції, моє загальне питання стосується оновлення всіх членів даних, щоб постійно зберігати дані об’єкта у дійсному стані.

Я знайшов "послідовність" та "стан зло" як відповідні фрази, обговорювані тут: https://en.wikibooks.org/wiki/Object_Oriented_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();

Якісь недоліки чи, можливо, кращі способи це зробити?

Це повторювана проблема в реальних програмах, підкріплених реляційною базою даних, і якщо ви не широко використовуєте збережені процедури, щоб перенести всю перевірку в базу даних. Я думаю, що сховище даних повинно просто зберігати дані, тоді як код повинен виконувати всі роботи з підтримання стану виконання.

EDIT: це пов’язане питання, але не має рекомендації щодо найкращої практики щодо єдиної великої функції для підтримки дійсного стану: /programming/1122346/c-sharp-object-oriented-design-maintain- дійсний-об'єкт-стан

EDIT2: Хоча відповідь @ eignesheep найкраща, ця відповідь - /software//a/148109/208591 - це те, що заповнює рядки між відповіддю @ eigensheep і тим, що я хотів знати - код повинен лише обробляти, і глобальний стан повинен бути заміщений переходом стану між об'єктами з підтримкою DI.


Я уникаю змінних у відсотках. Ви можете прийняти відсоток від користувача або показати його користувачеві, але життя набагато краще, якщо змінні програми є співвідношеннями.
Кевін Клайн

Відповіді:


29

За умови рівності ви повинні виразити своїх інваріантів у коді. У цьому випадку у вас є інваріант

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

Щоб висловити це у своєму коді, видаліть змінну податкового члена та замініть getTaxAmt () на

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

Вам слід зробити щось подібне, щоб позбутися змінної загальної вартості учасників.

Виявлення ваших інваріантів у коді може допомогти уникнути помилок. У початковому коді загальна вартість є неправильною, якщо вона встановлена ​​перед тим, як встановити значення SetPrice або setShipping.


3
У багатьох мовах є геттери, тому такі функції роблять вигляд, що вони є властивостями. Найкраще з обох!
цікаводанні

Чудовий момент, але мій загальний випадок використання - це виведення коду та зберігання даних у декілька стовпців у кількох таблицях реляційної бази даних (переважно MySQL), і я не хочу самостійно використовувати збережені процедури (дискусійні та інші теми). Подальше подання ідеї про інваріантне кодування це означає, що всі обчислення повинні бути "прикованими": getTotalCost()дзвінки getTaxAmt()тощо. Це означає, що ми зберігаємо лише речі, що не розраховуються . Ми трохи рухаємось до функціонального програмування? Це також ускладнює зберігання обчислених сутностей у таблицях для швидкого доступу ... Потрібні експерименти!
site80443

13

Будь-які недоліки [?]

Звичайно. Цей метод покладається на те, щоб кожен завжди пам’ятав щось робити. Будь-який метод, який покладається на всіх і завжди, завжди може вийти з ладу .

можливо кращі способи зробити це?

Один із способів уникнути тягаря церемонії запам'ятовування - це обчислення властивостей об'єкта, які залежать від інших властивостей, якщо це потрібно, як @eigensheep запропонував.

Інша - зробити предмет кошика незмінним та обчислити його за допомогою конструктора / фабрики. Зазвичай ви використовуєте метод "обчислити за потребою", навіть якщо ви зробили об'єкт непорушним. Але якщо розрахунок занадто трудомісткий і його читали б багато-багато разів; Ви можете вибрати варіант "обчислити під час створення".

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

Ви повинні запитати себе; Чи має сенс кошик без ціни? Чи може змінюватися ціна товару? Після його створення? Після обчислення її податку? Можливо, вам слід зробити CartItemнезмінним і призначити ціну та доставку в конструкторі:

$i = new CartItem(100, 20);

Чи має сенс візок без кошика, до якого він належить?

Якщо ні, то я б очікував $cart->addItem(100, 20)натомість.


3
Ви вказуєте на найбільший недолік: покладатися на людей, які пам'ятають робити речі, рідко є хорошим рішенням. Про єдине, на що можна покластися людині, це те, що вони забудуть щось зробити.
corsiKa

@corsiKlause Ho Ho Ho і abuzittin, суцільний момент, не можуть з цим посперечатися - люди незмінно забувають . Однак код, про який я писав вище, є лише прикладом, є істотні випадки використання, коли деякі члени даних оновлюються пізніше. Інший спосіб, який я бачу, - це нормалізувати подальші дії - зробити класи такими, що незалежно оновлені члени даних є в інших класах і покладають на себе відповідальність за оновлення на деякі інтерфейси - так, щоб інші програмісти (і ви самі через деякий час) повинні написати метод - компілятор нагадує вам, що вам потрібно це написати. Але це додало б набагато більше класів ...
site80443

1
@ site80443 На основі того, що я бачу, це неправильний підхід. Постарайтеся моделювати свої дані таким чином, щоб до них входили лише ті дані, які підтверджені проти власних дій. Наприклад, ціна товару не може бути негативною, покладається лише на себе. Якщо предмет зі знижкою, не враховуйте знижку в ціну - прикрасьте її знижкою пізніше. Зберігайте 4,99 долара за предмет та 20% знижку як окрему особу та 5% податок як ще одну юридичну особу. Насправді це виглядає так, як вам слід розглянути шаблон декору, якщо приклади представляють ваш реальний код життя.
corsiKa
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.