web-dev-qa-db-ja.com

有効な状態を維持するために、つまりオブジェクトのデータメンバーを更新するために、クラスで1つの大きなプライベート関数を定義することは良い考えですか?

以下のコードでは、eコマースサイトでの単純な単一アイテムの購入が使用されていますが、私の一般的な質問は、すべてのデータメンバーを更新して、オブジェクトのデータを常に有効な状態に保つことです。

「一貫性」と「状態は悪」が関連するフレーズとして見つかりました。ここで説明します: 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();

欠点、またはこれを行うためのより良い方法はありますか?

これは、リレーショナルデータベースに裏打ちされた実際のアプリケーションで繰り返し発生する問題であり、ストアドプロシージャを広範囲に使用してすべての検証をデータベースにプッシュしない場合です。データストアはデータを格納するだけでよく、コードはすべてのランタイム状態維持作業を実行する必要があると思います。

編集:これは関連する質問ですが、有効な状態を維持するための単一の大きな関数に関するベストプラクティスの推奨事項はありません: https://stackoverflow.com/questions/1122346/c-sharp-object-oriented- design-maintaining-valid-object-state

EDIT2:@eignesheepの答えが最善ですが、この答え- https://softwareengineering.stackexchange.com/a/148109/208591 -@eigensheepの答えと私が望んでいたものの間の線を埋めるものです知っている-コードは処理するだけであり、グローバルな状態はオブジェクト間でのDI対応の状態の受け渡しによって置き換えられるべきです。

18
site80443

他はすべて等しいので、不変条件をコードで表現する必要があります。この場合、不変条件があります

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

これをコードで表現するには、taxメンバー変数を削除し、getTaxAmt()を次のように置き換えます

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

総コストメンバー変数を取り除くために、同様のことを行う必要があります。

コードで不変条件を表現すると、バグの回避に役立ちます。元のコードでは、setPriceまたはsetShippingが呼び出される前にチェックされると、合計コストは正しくありません。

29
eigensheep

不利な点[?]

承知しました。このメソッドは、何かを行うために全員が常に覚えているに依存しています。すべての人に常に依存する方法は必ず失敗するときどき

これを行うためのより良い方法はありますか?

式を覚える負担を回避する1つの方法は、@ eigensheepが示唆するように、必要に応じて他のプロパティに依存するオブジェクトのプロパティを計算することです。

もう1つは、カートアイテムを不変にし、コンストラクター/ファクトリーメソッドでそれを計算することです。オブジェクトを不変にしても、通常は「必要に応じて計算する」メソッドを使用します。しかし、計算に時間がかかりすぎて何度も読み込まれる場合。 「作成中に計算」オプションを選択できます。

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

あなた自身に尋ねるべきです。価格のないカートアイテムは理にかなっていますか?アイテムの価格は変更できますか?それが作成された後?税計算後? etcたぶんあなたはCartItemを不変にして価格を決め、コンストラクタで配送する必要があります:

_$i = new CartItem(100, 20);
_

カートアイテムは、それが属しているカートなしで意味がありますか?

そうでなければ、代わりに$cart->addItem(100, 20)を期待します。

13