面向对象(五)抽象类与接口
抽象类是一种特殊的类,而接口是一种特殊的抽象类。他们通常配合面向对象的多态性一起使用
一、抽象类
1、抽象方法就是没有方法体的方法,是为了方便继承而引入的。不能使用private修饰为私有的
2、只要在声明类时有一个方法是抽象方法,那么这个类就是抽象类,抽象类也要使用abstract关键字修饰。抽象类中可以有不是抽象的成员方法。
3、由于抽象类中有没有被实现的抽象方法,所以抽象类是不能被实例化的,即创建不了对象,也就不能直接使用它。那使用抽象类有什么作用呢?
使用抽象类就包含了继承关系,它是为它的子类定义公共接口,将它的操作交给子类去实现。就是将抽象类作为子类重载的模板使用,定义抽象类就相当于定义了一种规范,这种规范要求子类去遵守。当子类继承抽象类之后,就必须把抽象类中的抽象方法按照子类自己的需要去实现。子类必须把父类中的抽象方法全部实现,否则子类中还存在抽象方法,所以还是抽象类,不能实例化对象
<?php
abstract class AbstractClass
{
// 强制要求子类定义这些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
/*
ConcreteClass1
FOO_ConcreteClass1
*/
4、子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。
abstract class AbstractClass
{
// 我们的抽象方法仅需要定义需要的参数
abstract protected function prefixName($name);
}
class ConcreteClass extends AbstractClass
{
// 我们的子类可以定义父类签名中不存在的可选参数
public function prefixName($name, $separator = ".") {
if ($name == "Pacman") {
$prefix = "Mr";
} elseif ($name == "Pacwoman") {
$prefix = "Mrs";
} else {
$prefix = "";
}
return "{$prefix}{$separator} {$name}";
}
}
$class = new ConcreteClass;
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";
二、接口技术
1、因为php只支持单继承,也就是说每个类只能继承一个父类。当声明的新类继承抽象类实现模板以后,它就不能再有其他父类了。为了解决这个问题,php引入了接口。
2、接口是一种特殊的抽象类,接口中声明的方法必须都是抽象方法,另外在接口中不能声明变量,只能使用const声明为常量的成员属性,而且接口中的所有成员都必须有public的访问权限。
3、可以使用接口名称在接口外面去获取常量成员的值;
接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。
4、也可以使用extends让一个接口去继承另一个接口,实现接口之间的扩展
5、如果需要使用接口中的成员,则需要通过子类去实现接口中的全部抽象方法,然后创建子类的对象去调用在子类中实现后的方法。但通过类去继承接口时需要使用implements关键字来实现,而并不是使用extends完成。
6、php是单继承的,一个类只能有一个父类,但是一个类可以实现多个接口。将要实现的多个接口之间使用逗号分隔开,而且在子类中要将所有接口中的抽象方法全部实现才可以创建对象。相当于一个类要遵守多个规范,实现多个接口时,接口中的方法不能有重名。就像我们不仅要遵守国家的法律,如果是在学校,还要遵守学校的校规一样。
7、还可以使用类继承一个类之后再使用implements实现多个接口
8、对于一些已经开发好的系统,在结构上进行较大的调整已经不太现实,这时可以通过定义一些接口并追加相应的实现来完成功能结构的扩展。
例1
// 声明一个'iTemplate'接口
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
// 实现接口
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
例2、可扩充的接口
interface a
{
public function foo();
}
interface b extends a
{
public function baz(Baz $baz);
}
// 正确写法
class c implements b
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
// 错误写法会导致一个致命错误
class d implements b
{
public function foo()
{
}
public function baz(Foo $foo)
{
}
}
例3、继承多个接口
interface a
{
public function foo();
}
interface b
{
public function bar();
}
interface c extends a, b
{
public function baz();
}
class d implements c
{
public function foo()
{
}
public function bar()
{
}
public function baz()
{
}
}
例4、使用接口常量
interface a
{
const b = 'Interface constant';
}
// 输出接口常量
echo a::b;
// 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
class b implements a
{
const b = 'Class constant';
}
还没有评论,来说两句吧...