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


18

以下のコードでは、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-有効なオブジェクトの状態

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
多くの言語にはゲッターがあり、そのような関数はプロパティであるふりをします。両方の最高!
curiousdannii

優れた点ですが、私の一般的なユースケースは、コードがリレーショナルデータベースの複数のテーブルの複数の列にデータをフェッチして保存し(主にMySQL)、ストアドプロシージャ(議論の余地のある独自のトピック)を使用したくない場合です。コード内の不変式のアイデアをさらに進めると、これはすべての計算を「連鎖」させる必要があることを意味します。getTotalCost()呼び出しgetTaxAmt()などです。これは、計算されていないもののみを保存することを意味します。関数型プログラミングに向かって少し動いていますか?これにより、計算されたエンティティのテーブルへの格納が複雑になり、すばやくアクセスできます...実験が必要です!
site80443

13

不利な点[?]

承知しました。この方法は、常に何かをすることを覚えている全員に依存しています。全員に依存する方法は常に失敗します。

これを行うより良い方法ですか?

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

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

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

自問するべきです。価格のないカート商品は理にかなっていますか?アイテムの価格は変更できますか?作成後は?税が計算された後?etc多分CartItem、コンストラクターで不変でassigの価格と配送を行う必要があります。

$i = new CartItem(100, 20);

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

そうでない場合は、$cart->addItem(100, 20)代わりに期待しています。


3
あなたは最大の欠点を指摘します:物事を覚えている人々に頼ることはめったに良い解決策ではありません。人間に頼ることができる唯一のことは、彼らが何かをするのを忘れることです。
corsiKa

@corsiKlause Ho Ho Hoとabuzittin(堅実な点)は、それについて議論することはできません。人々は常に忘れています。ただし、上で書いたコードは一例に過ぎず、一部のデータメンバーが後で更新されるというかなりのユースケースがあります。私が見る他の方法は、さらに正規化することです-独立して更新されたデータメンバーが他のクラスにあるようにクラスを作成し、更新の責任をいくつかのインターフェイスにプッシュします-他のプログラマー(およびしばらくしてから)コンパイラは、あなたがそれを書かなければならないことを思い出させます。しかし、それはより多くのクラスを追加します...
site80443

1
@ site80443私が見るものに基づいて、それは間違ったアプローチです。自分自身に対して検証されたデータのみが含まれるように、データをモデル化してください。たとえば、アイテムの価格をマイナスにすることはできません。アイテムが割引される場合、価格に割引を織り込まないでください-後で割引で装飾します。アイテムの$ 4.99と20%の割引を別のエンティティとして保存し、5%の税金をさらに別のエンティティとして保存します。実際の例が実際のコードを表している場合、デコレータパターンを考慮する必要があるように見えます。
corsiKa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.