面向对象编程,有几个重要特征:继承性,抽象性,多态性等等。这里针对PHP语言,讨论一下
abstract
、interface
和trait
,其他语言可能有偏差。
abstract
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的。具体类是实例对象的抽象化,而 抽象类 则是具体类的抽象化。
定义一个具体类,使用关键字class
;定义一个抽象类,要带上关键字abstract
,即abstract class
。PHP语言的机制比较宽松,抽象类可以继承于其他抽象类,也可以继承于具体类,但是抽象类没有直接的实例化对象。
1 |
|
抽象方法
抽象类中可以设置属性,定义具体方法,特别,还可以定义 抽象方法。定义定义一个抽象方法,需要使用关键字abstract
,并且只能在抽象类中定义。抽象类中,只能声明抽象方法其调用方式(参数),不能定义其具体的功能实现;而在抽象类的具体子类中,一定要将父类中的所有抽象方法具体化:
- 具体方法不再带
abstract
声明; - 具体方法的访问控制必须和父类中一样(或者更为宽松);
- 具体方法定义的调用方式必须与抽象方法的声明匹配,即所需参数的数量、类型必须一致。如果要增加参数,只能在末尾追加,并且只能是可选参数。
interface
首先,接口不是类,既不是具体类,也不是抽象类,接口的定义就没有用到关键字class
。定义一个接口,使用关键字interface
:
1 |
|
接口有以下几个特点:
- 接口不能被实例化,因为接口不是类;
- 接口可以继承于其他接口,并且可以多重继承,但不能继承于类;
- 接口中不能设置属性,但是可以设置常量;
- 接口的方法只能是公有的,并且是只声明不定义;
- 具体类、抽象类都可以使用关键字
implements
来实现(多个)接口; - 具体类中一定要具体化所有要实现的接口方法,并且定义的调用方式必须与接口中所声明的匹配,即所需参数的数量、类型必须一致。如果要增加参数,只能在末尾追加,并且只能是可选参数。
trait
trait
,中文意思是 特点,算是PHP的一个新语法(自PHP5.4加入)。PHP本身是不支持多重继承的,trait
就是为了减少单继承语言的限制而提出,开发人员可以通过trait
自由地在不同层次结构内独立的类中复用代码。trait
示例:
1 |
|
trait
有以下几个特点:
trait
不是类,无法通过自身来实例化,也不能继承或者被继承;- 但是
trait
中,可以定义属性,定义具体方法,还可以声明抽象方法; - 正如
class
能够使用trait
一样,trait
也能够使用其他trait
。在trait
定义时可以使用一个或多个trait
。
使用trait
的时候还要注意:
优先级。从基类继承的成员会被
trait
插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了trait
的方法,而trait
则覆盖了被继承的方法。多个
trait
。通过逗号分隔,在use
声明列出多个trait
,可以都插入到一个类中。冲突问题。如果引入的两个
trait
都定义了一个同名方法,且没有明确的解决冲突,那么代码将会产生一个致命错误。为了解决多个 trait 在同一个类中的命名冲突,可以使用insteadof
操作符来明确指定使用冲突方法中的哪一个,或者使用as
操作符将其中一个冲突的方法以另一个名称来引入:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}as
操作符还可以用来调整方法的访问控制:1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
// 修改 sayHello 的访问控制
class MyClass1 {
use HelloWorld { sayHello as protected; }
}
// 给方法一个改变了访问控制的别名,原版 sayHello 的访问控制则没有发生变化
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
最后
PHP面向对象开发时,要灵活利用abstract
、interface
和trait
,不要生搬硬套。