バーバラリスコフの置換原則(前提条件と事後条件)

なぜそんなに多くの人がこの原則に問題を抱えているのですか?「乱暴」ではなく、より単純な定義をとると、次のようになります。





継承クラスは、基本クラスの動作をオーバーライドするのではなく、補完する必要があります。





それは明確で非常に論理的に聞こえますが、私たちは同意しません。しかし、これを達成する方法を気にしないでください?何らかの理由で、多くの人は単に前提条件事後条件に関する情報をスキップします。これは、何をする必要があるかを完全に説明しているだけです。





この記事では、すでに多くの材料が存在するこの原理の一般的な例については考慮しません正方形と長方形またはサーモスタットコントロールの例)。ここでは、「前提条件」「事後条件」などの概念についてもう少し詳しく説明し共分散、反変性、不変性とは何か、および「歴史的制約」または「歴史のルール」とは何かを検討します。





サブクラスで前提条件を強化することはできません

️言い換えると、子クラスは、ビジネス動作を実行するために、基本クラスで定義されているよりも多くの前提条件を作成するべきではありません。次に例を示します。





<?php

class Customer
{
    protected float $account = 0;

    public function putMoneyIntoAccount(int|float $sum): void
    {
        if ($sum < 1) {
            throw new Exception('       1$');
        }

        $this->account += $sum;
    }
}

class  MicroCustomer extends Customer
{
    public function putMoneyIntoAccount(int|float $sum): void
    {
        if ($sum < 1) {
            throw new Exception('       1$');
        }

        //  
        if ($sum > 100) { 
            throw new Exception('      100$');
        }

        $this->account += $sum;
    }
}
      
      



​ . !





«», , .





, , .





, , Bar->process()



, .





<?php

class Foo
{
    public function process(int|float $value)
    {
       // some code
    }
}

class Bar extends Foo
{
    public function process(int|float|string $value)
    {
        // some code
    }
}
      
      



, VIPCustomer



putMoneyIntoAccount



( ) Money



, ( Dollars



).





<?php

class Money {}
class Dollars extends Money {}

class Customer
{
    protected Money $account;

    public function putMoneyIntoAccount(Dollars $sum): void
    {
        $this->account = $sum;
    }
}

class VIPCustomer extends Customer
{
    public function putMoneyIntoAccount(Money $sum): void
    {
        $this->account = $sum;
    }
}
      
      



, , .





​️ , . .





<?php

class Customer
{
    protected Dollars $account;

    public function chargeMoney(Dollars $sum): float
    {
        $result = $this->account - $sum->getAmount();

        if ($result < 0) { // 
            throw new Exception();
        }

        return $result;
    }
}

class  VIPCustomer extends Customer
{
    public function chargeMoney(Dollars $sum): float
    {
        $result = $this->account - $sum->getAmount();

        if ($sum < 1000) { //   
            $result -= 5;  
        }
       
        //    
      
        return $result;
    }
}
      
      



​ , . !





- «», (?!), .





. render()



, JpgImage



, Image



, Renderer



.





<?php

class Image {}
class JpgImage extends Image {}

class Renderer
{
    public function render(): Image
    {
    }
}

class PhotoRenderer extends Renderer
{
    public function render(): JpgImage
    {
    }
}
      
      



​️ . . :)





.





- .





— , . , .





.





<?php 

class Wallet
{
    protected float $amount;
    //        
}
      
      



(« »):





.





, . , . 





<?php

class Deposit
{
    protected float $account = 0;

    public function __construct(float $sum)
    {
        if ($sum < 0) {
            throw new Exception('      ');
        }

        $this->account += $sum;
    }
}

class VipDeposit extends Deposit
{
    public function getMoney(float $sum)
    {
        $this->account -= $sum;
    }
}
      
      



Deposit



. VipDeposit



, account



, Deposit



. .





.





, , , , .





前後の状態を取り除くために努力することは言及する価値があります。理想的には、メソッドの入力/出力パラメーターとして定義する必要があります(たとえば、既製の値オブジェクトを署名に渡し、特定の有効なオブジェクトを出力に返すことによって)。





お役に立てば幸いです。





のソース

  1. Wiki-バーバラリスコフの置換原則





  2. メタニット





  3. PHP.watch





  4. 電報チャンネル、短いメモ付き








All Articles