2016-11-07 2 views
1

Liskov置換原則を破る古典的な設計例であるSquareおよびRectangleクラスに関連するSOには、いくつかのスレッドがあります。問題はSquareがRectangleを拡張するとLSPに違反することを証明することから始まります。多くの人がこれを解決する方法を尋ねます。 RectangleとSquareなどで拡張されたPolygonベースクラスを含むアイデアを聞いたことがあります。私もこれについて考えていましたが、これは間違った方向です - 間違った議論です。 SquareはRectangleを拡張しません。四角形は、長方形に機能やプロパティを追加しません。正方形は長方形です。これは一種の長方形です。SOLID/LSPを中断しない四角形の長方形実装

Rectangleクラスが存在し、Rectangleを再利用するSquareクラスを構築することを前提とすると、Rectangleのインスタンスである保護されたプロパティを持つSquareクラスを構築します。 Squareにも適用されるRectangleのすべてのプロパティ/メソッドを公開し、そうでないものは公開しません。 Rectangleに適用されないSquareのプロパティまたはメソッドは、すべてSquareクラスに表示されます。ここに私のソリューションは、PHPには:

class Square { 

    protected $rectangle; // instance of Rectangle 

    function __construct ($length) { 
     if (!is_numeric ($length)) 
      throw new Exception ("Square::__contruct - length must be numeric"); 
     $this->rectangle = new Rectangle ($length, $length); // given Rectangle (width, height) 
    } 

    public function getArea() { 
     return $this->rectangle->getArea(); 
    } 

    public function getLength() { 
     return $this->rectangle->getWidth(); // or height pick one 
    } 

    public function setLength ($length) { 
     if (!is_numeric ($length)) 
      throw new Exception ("Square::setLength - length must be numeric"); 
     $this->rectangle->setWidth ($length); 
     $this->rectangle->setHeight ($length); 
    } 

    public function isSquare() { 
     return ($this->rectangle->getHeight() == $this->rectangle->getWidth()); 
    } 

} 

引数は広場の場合の破壊LSPが長方形には問題がある拡張した理由は、他の誰かが何らかの要因によって、長方形の面積を変える長方形にメソッドを追加することである経つにつれ - それでは、係数を幅に掛けるように実装された

public function transformArea (factor) 

のようなシグネチャを持つメソッドを呼び出してみましょう。引数は、幅が高さと同じであり、幅が10%増加すると正方形が21%増加するため、正方形が因子の2乗で増加することを示しています。これは、これらのシステムが実際にどのように機能するかではないため、正確には当てはまりません。実際に何が起こるかは、因子によって幅が増加し、長方形の面積も因子によって大きくなるが、幅と高さがもはや同じではないので、Squareのインスタンスはもはや実際には正方形ではない(Square extends Rectangleの場合)。私のバージョンでは、他の誰かがそのメソッドをRectangleに追加することができ、Squareを壊すことはありません。Squareクラスによってインスタンスがまだ公開されていないため、誰もRectangleのtransformAreaメソッドを使用できません。

誰かがこのメソッドを追加することを指示し、その後、別のプログラマは、単純なようRectangleクラスからそれを暴露することによって広場クラスに追加する場合:

public function transformArea ($factor) { 
    $this->rectangle->transformArea ($factor); 
} 

、この人は自分の仕事をしていませんし、実行しますと仮定クラスの単体テストは、このメソッドを実行するたびにisSquareが破られたことを発見します。しかし、そのテストはテストのスタックの最後に追加され、バグを明らかにしないかもしれません。しかし、それは一般的にこのシステムの設計上の欠陥ではない開発における問題です。

正解は、長さをfactorの平方根で増やしてSquareに実装することです。

public function transformArea ($factor) { 
    if (!is_numeric ($factor)) 
     throw new Exception ("Square::transformArea - factor must be numeric"); 
    if ($factor <= 0) 
     throw new Exception ("Square::transformArea - factor must be greater than zero"); 
    $growBy = (float) sqrt ($factor); 
    $newLength = $this->getLength() * $growBy; 
    $this->setLength ($newLength); 
} 

もう1つの議論は、このクラスでは、内部の四角形が正方形(幅==高さ)のままであることを保証しないということです。同意しません。 Squareは、どちらか一方だけではなく両方で変化するメソッドを公開しません。 Rectangleには、破損する可能性のあるメソッドがありますが、露出しているメソッドはありません。

これは個人的に私には実世界にふさわしいものです。正方形は長方形です。すべての四角形は四角形ですが、四角形は四角形ではありません。

私のソリューションはSOLIDのルールを破りますか?

+1

Square/Rectangleの問題は、単に再利用したり、新しい署名を作成するよりも大きくなります。数学的に正方形は一種の矩形ですが、それをコーディングすると四角形は四角形として使用できません。四角形としての使用を制限するためです。 –

答えて

1

デザインは、すべてのトレードオフと「最良の」解決策についてですが、アプリケーションによって異なります。

  • Squareに伝統的な解決策はある-Rectangle問題は、クラス不変(トレードオフ)を作ることですis-a関係を維持しながら。
  • あなたの例は完全に有効な解決策である構成(Square has-a Rectangle)を示していますが、オブジェクトを変更可能に保ちながらis-a関係を断念しています。 Rectangleを受け取るメソッドにSquareインスタンスを渡すことはできなくなりました。

いずれかを選択できますが、両方を実行しようとすると、LSPが壊れてしまいます。


PS:あなたのSquareクラスはisSquare機能を持つべきではない、それはインスタンスが有効な状態に常にあることを保証するために、クラスの責任です。

関連する問題