PHP中的抽象类、接口与性状
网络上对抽象类、接口、性状的总结都是停留在概念层面,代码也都是相似的。我觉得大部分作者自己都不知道怎么使用,这里从代码的层面简单总结一下它们常见使用场景,加深理解,记住一切皆类型就对了。
一、接口(interface)
对具体的实现类的类型进行约束,只要实现了接口的方法,原来的类就会多一种类型。接口不能直接实例化,可以继承。如果你编写的类有大量相同的行为,那么你可以把它定义成接口。
实现接口
<?php // 接口 interface Animal { public function eat(); } // 实现接口的具体类 class Dog implements Animal { public function eat() { print "dog eat \n"; } } // 实例化类 $dog = new Dog(); var_dump($dog instanceof Animal); var_dump($dog instanceof Dog); // 输出: # bool(true) # bool(true)
可以看出,当一个类实现了接口以后,原来的类就会多一种类型,可以利用接口的特性进行参数约束和类型转换(协变和逆变)。
使用场景
实现多态、里氏替换、面向接口编程。
<?php // 接口 interface Animal { public function eat(); } // 实现接口的具体类 class Dog implements Animal { public function eat() { print "dog eat \n"; } } // 实现接口的具体类 class Cat implements Animal { public function eat() { print "cat eat \n"; } } function call(Animal $animal) { print $animal->eat(); } call(new Dog()); call(new Cat());
可以看出,对于同一个函数call,参数设置为接口类型(Animal类型),任何实现接口的类(Cat、Dog)都会多出一种类型(Animal),所以函数能正常接收和调用,实现多态和里氏替换的效果。
二、抽象类(abstract class)
对共性保留,对差异开放,即求同存异,只要实现了抽象类的抽象方法,原来的类就会多一种类型,抽象类不能被直接实例化,可以继承。如果你编写的类有大量相同的代码,可以提取到抽象类中复用。
实现抽象类
<?php // 抽象类 abstract class Animal { public function animalShout() { print "hello, "; $this->shout(); } abstract function shout(); } // 实现抽象类的具体类 class Dog extends Animal { function shout() { print "dog shout \n"; } } // 实现抽象类的具体类 class Cat extends Animal { function shout() { print "cat shout \n"; } } $dog = new Dog(); $dog->animalShout(); $cat = new Cat(); $cat->animalShout(); # 输出 # hello, dog shout # hello, cat shout
可以看出,Cat类和Dog类都调用了同一个方法
animalShout
打印hello
(求同),然后各自实现了各自的shout
方法(存异),可以使用抽象类的这个特性实现多态和里氏替换的效果。
三、性状(trait)
将公共的代码提取出来,在多个地方进行嵌入复用。性状不能被直接实例化,性状中可以嵌套性状。如果你编写的业务类中有和业务无关的代码,比如:实现单例和一些工具方法,你可以提取到性状中复用。
- 使用性状
<?php
// 实现单例
trait Singleton {
private static $_instance = null;
/**
* @return static
*/
public static function getInstance() {
self::$_instance || self::$_instance = new self();
return self::$_instance;
}
private function __clone() {
}
private function __construct() {
}
}
class User {
// 使用性状
use Singleton;
public $age;
/**
* @param $age
* @return $this
*/
public function setAge($age) {
$this->age = $age;
return $this;
}
/**
* @return mixed
*/
public function getAge() {
return $this->age;
}
}
print User::getInstance()->setAge(20)->getAge();
print User::getInstance()->setAge(21)->getAge();
# 输出
# 20
# 21
可以看出,只需要嵌入性状,就可以在类中调用性状的方法,实现单例,任何需要单例的地方只需要嵌入就行,避免写重复的代码。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: