2022年9月10日 星期六

PHP 類別物件的 Covariance(協變)、Contravariance(逆變)

Covariance(協變)、Contravariance(逆變):
子類別、父類別,互相替代的問題。


Covariance(協變):原本規定是父類,但用子類取代。如果類別設計符合里氏替換原則(子類別必須能替換父類別),這通常是被允許的。
Contravariance(逆變):原本規定是子類,但用父類取代。

範例:
//一個食物類別
class Food {
    
}

//動物食物(食物類別的子類)
class AnimalFood extends Food {
    
}

//一個動物類別,有一個吃食物的方法
class Animal {

    public function eat(AnimalFood $ff) {//規定只能吃"動物食物"
        echo get_class($this) . " 吃 " . get_class($ff);
    }

}

//有一個動物收容所介面,有一個領養動物的方法,想開動物收容所,須實現此方法
interface AnimalShelter {

    public function adoption(): Animal; //規定只能領養動物類別
}
#Covariance(協變):
//1.有一個人,想要開狗收容所,所以要實現狗領養的方法
//2.收容所介面,規定只能領養 Animal 類別,但可用 Dog 子類別取代
//  原本規定是父類 Animal,但用子類 Dog 取代=>協變

class Dog extends Animal {//收容所要有狗,建立 Dog 類別 
}

//狗收容所
class DogShelter implements AnimalShelter {

    //規定只能領養Animal類別,但可用Dog子類別取代(協變)
    public function adoption(): Dog {//返回值協變
        return new Dog();
    }

}

//一個狗收容所
$ds = new DogShelter();
//從狗收容所領養了一隻動物
$aa = $ds->adoption();
//看看從狗收容所領養了什麼動物
echo "領養了:" . get_class($aa); //領養了:Dog
//這是狗,能吃AnimalFood,餵牠吃
$aa->eat(new AnimalFood()); //Dog 吃 AnimalFood
//但不能吃 Food (全部的食物)
//Fatal error: Uncaught TypeError: Animal::eat(): Argument #1 ($ff) must be of type AnimalFood, Food given
//$aa->eat(new Food());
#Contravariance(逆變):
//有一個人,想要開豬收容所,所以要實現豬領養的方法
//1.收容所要有豬,所以建立 Pig 類別,
//2.建立 Pig 類別時,才想到豬不只可吃動物食物 class AnimalFood,而是所有食物 class Food 都可以吃
//  但動物類別 class Animal 裡的 eat(AnimalFood $ff) 方法,已經限制只能吃動物食物 class AnimalFood
//3.所以只好將豬的 eat(AnimalFood $ff) 覆寫,改成 eat(Food $ff)
//  原本規定是子類 AnimalFood,但用父類 Food 取代=>逆變

class Pig extends Animal {

    //規定用AnimalFood,但覆寫後,改用Food父類取代(逆變)
    public function eat(Food $ff) {//方法參數逆變
        echo get_class($this) . " 吃 " . get_class($ff);
    }

}

//豬收容所
class PigShelter implements AnimalShelter {

    public function adoption(): Pig {
        return new Pig();
    }

}

//一個豬收容所
$ds = new PigShelter();
//從豬收容所領養了一隻動物
$aa = $ds->adoption();
//看看從豬收容所領養了什麼動物
echo "領養了:" . get_class($aa); //領養了:Pig
//這是豬,什麼都能吃,AnimalFood、Food 都餵牠吃
$aa->eat(new AnimalFood()); //Pig 吃 AnimalFood
$aa->eat(new Food()); //Pig 吃 Food



參考:

沒有留言:

張貼留言