霜天部落 | 专注PHP研发,研究LAMP高性能架构部署与优化

php面向对象之接口和抽象类

在我才接触php的时候,也觉得这个东西没有什么用,在后来的逐渐接触大系统以后,才发现其实这些东西还是有一定的作用的,下面我就简单的说说。

1.php 接口类:interface

其实他们的作用很简单,当有很多人一起开发一个项目时,可能都会去调用别人写的一些类,那你就会问,我怎么知道他的某个功能的实现方法是怎么命名的呢,这个时候php接口类就起到作用了,当我们定义了一个接口类时,它里面的方式是下面的子类必须实现的,比如

interface Shop
{
public function buy($gid);
public function sell($gid);
public function view($gid);
}

我声明一个shop接口类,定义了三个方法:买(buy),卖(sell),看(view),那么继承此类的所有子类都必须实现这3个方法少一个都不行,如果子类没有实现这些话,就无法运行。实际上接口类说白了,就是一个类的模板,一个类的规定,如果你属于这类,你就必须遵循我的规定,少一个都不行,但是具体你怎么去做,我不管,那是你的事,如:


class BaseShop implements Shop
{
public function buy($gid)
{
echo '你购买了ID为 :' . $gid . '的商品';
}
public function sell($gid)
{
echo '你购卖ID为 :' . $gid . '的商品';
}

public function view($gid)
{
echo '你浏览了ID为 :' . $gid . '的商品';
}
}

你想想,在一个多人合作的大项目里面,有了接口类是多么的方便,这样你就不用去问别人,你的某某功能的方法名是什么了,当然如果你们喜欢这样我也没有办法。

结论 : 接口类就是一个类的领导者,指明方向,子类必须完成它指定方法。

2.php 抽象类 : abstract

其实抽象类和接口类有一部分很像,记得在哪里看见这样一句话,抽象类就把类像的部分抽出来,这句看上去很搞笑,其实它说出了抽象类的真理,抽象类的作用是,当你发现你的很多类里面用很多方法你不断的在重复写,那你就可以考虑使用抽象类了,你可能会说“我不是可以重写一个类每个公共类我个实例化一个这个公共类,调用相同的方法就可以了”,这里是可以,实际上抽象类做的工作也就是这个,不过他省去了你实例化的这个步骤,让你就像直接调用本类方法一样方便,而且你还可以重载这个方法。如:


abstract class BaseShop {
public function buy($gid)
{
echo '你购买了ID为 :' . $gid . '的商品';
}
public function sell($gid)
{
echo '你购卖ID为 :' . $gid . '的商品';
}

public function view($gid)
{
echo '你浏览了ID为 :' . $gid . '的商品';
}
}

class BallShop extends BaseShop
{
var $itme_id = null;

public function __construct()
{
$this->itme_id = 2314;
}

public function open()
{
$this->sell($this->itme_id);
}
}

这里是一个例子,想上面一样我定义了一个商店类,抽所了它所有像的部分,买(buy),卖(sell),看(view),并且抽象类里都实现了这些方法,那么继承它的子类就自动获得了这些方法,子类就做它自己独特的东西,介绍代码的重复,提高复用性。

结论: 抽象类就是一个类的服务提供商,拥有众多服务,你不用必须用,当需要的时候你来用就可以,如果你觉得不提供服务不满意,你还可以自己来做。

抽象类和接口是OO里比较难理解的概念,也是比较容易混淆的。

抽象类和接口都不能直接实例化。在抽象类里可以实现一些功能,但在接口里面则不能实现功能。
LZ的例子不好,我给你换个例子:(这里我只是从<<权威编程>>里copy一些例子,因为我也懒,呵呵。) 复制内容到剪贴板
代码:
抽象类:

abstract class Shape
{
function setCenter($x, $y)
{

$this->x = $x;
$this->y = $y;

}

abstract function draw();
protected $x, $y;
}

接口:

interface Loggable
{
function logString();
}

这里很清楚的显示了在抽象类里,我们贯彻了设原用到点setCenter()的方法,这样,当子类继承它时,就不用再重复写这个设原点的方法了。那么为什么draw的方法要设定成抽象方法呢?这是因为不同的子类要用不同的实现方法。那么我们干脆不要这个什么抽象方法,而在子类里再实现不是更好吗?哈哈,你忘了很多程序员都自称是懒人吗?在抽象类里先定义一个抽象方法,就是告诉你,在子类里面一定要用到这个方法,如果你忘了,漏了,那是不行的。但是因为每个做法不一样,所以我抽象类这里就不把它先做好了。

接口的作用与抽象类相似的地方在于:所有方法都是抽象的,也就是说,如果你要继承这个接口,那么所有的这些方法,你都要在子类里实现,一个不能漏,一个不能忘。

子类继承的例子: 复制内容到剪贴板
代码:
抽象类的继承:

class Square extends Shape
{

function draw()

{

// Here goes the code which draws the Square
...

}

}

class Circle extends Shape
{
function draw()

{
// Here goes the code which draws the Circle
...

}

}

接口的继承:

class Person implements Loggable
{

private $name, $address, $idNumber, $age;

function logString()
{

return "class Person: name = $this->name, ID = $this >idNumbern";

}

}

class Product implements Loggable
{

private $name, $price, $expiryDate;

function logString()
{

return "class Product: name = $this->name, price = $this >pricen";

}

}

LZ说:没实际功能为什么还要继承?
因为这是OO设计上的要求。实际应用时的例子不会这么简单。在OO设计时,会把一些通用的方法放在一个类里,但考虑到子类继承时的差异性,又不能把它事先实现,就只好用到这个抽象类或接口了。

LZ说:但是不如一个类离开了父类车,不是照样可以完成自己的功能?
当然你可以在子类里实现所有的功能,甚至于不要父类,但这样做,与过程编程没什么两样,也就谈不上什么OO思想了。

先顶结构后开发实现代码提高系统的可扩展和快速变动系统架构

LZ说:一般的类继承后可以覆盖父亲的方法,但是抽象类完全没有功能,难道就是为了好看?
因为你举的例子不好,所以抽象类完全没有功能。但就算抽象类完全没有功能,像接口一样,那也不是为了好看,你以为懒人会吃饱了撑着没事干要找活干吗?道理上面讲过了。

LZ说:麻烦解答下我帖子上关于接口的问题?
原理上面讲过了。

LZ说:想了很久,只是觉得,抽象类和接口都是为了方便读代码的人,或者说是写代码的规范?
错!!!道理上面讲过了。

紫幻凌说:我也一直疑惑为什么要弄个抽象类?普通的父类不也能好好的被继承?为什么一定要抽象呢?
道理讲完了,还疑惑吗?

LZ说:问题是什么时候用。。。想不出什么时候用?
什么时候用抽象类,什么时候用接口,这个嘛,嘿嘿,谁用谁知道!(你以为是卖广告啊?!)

在讲之前,大家要记住OO的一个原则:一个类只能extends一个父类,但可以贯彻多个接口。
比如说

class Square extends Shape implements B, C, ... {
...
}

有时候,我们需要设计一个物品,比如shape,这个shape有不同的形状,有不同的颜色,可作不同的运动,等等。当然我们可以把这些都放在一个类里面,但这样,如前面说的,只是过程编程,不是OO编程。你会说,像上面例子一样,做一个抽象类,然后在子类里实现所有的功能。好象不错,进步了。这样子类里面要实现形状,颜色,运动等功能,OK吗?记住,OO里面还有一条原则,为了最大程度的做到代码重用,一个类里最好只实现一个功能,也就是说,一个类做形状,一个类做颜色,一个类做运动 。这样如果其他的类要用到颜色的功能时,我们就可以重用这个颜色类,同理,这样我们可以很容易的重用运动类,形状类,等等。

但这样问题出来了,现在我们要用到上面所有的功能,但又分散在三个类里面,怎么办?
这时我们就要用到抽象类和接口了。选定一个跟shape关联最大的一项–形状,把它做成抽象类, 其他两样,把它们做成接口,然后在子类里面extends一个抽象类,再implements两个接口就行了。如:

class Square extends Shape implements 颜色, 运动, ... {
...
}

什么是接口 (interface) ?

接口是方法的抽象,如果不同的类有同样的方法,那么就应该考虑使用接口。
(1)接口是一个行为的规范、协议。其实就是类和类之间的一种协定,一种约束
(2)C#不支持多继承,但是他把这个功能交给接口来实现。
(3)类与类之间的系统资源调用方式不一样,导致他们之间的通信很困难,而接口可以屏蔽掉它们之间的差异,能使他们顺利通信。

什么是抽象类(abstract class)?

1. 抽 象类仅提供一个类型的部分实现。抽象类可以有实例变量,以及一个或多个构造函数。抽象类可以同时有抽象方法和具体方法。一个抽象类不会有实例,这些构造函 数不能被客户端调用来创建实例。一个抽象类的构造函数可以被其子类调用,从而使一个抽象类的所有子类都可以有一些共同的实现,而不同的子类可以在此基础上 有其自己的实现。

2. 抽象类的用途1) 具体类不是用来继承的。 Scott Meyers曾指出,只要有可能,不要丛具体类继承。2) 假设有2个具体类,类A和类B,类B是类A 的子类,那么一个最简单的修改方案是应当建立一个抽象类(或java接口)C,然后让类A和类B成为抽象类C的子类。3) 抽象类应当拥有尽可能多的共同代码。以提高代码的复用率。4) 抽象类应当拥有尽可能少的数据。

3. 基于抽象类的模式和原则1) 针对抽象编程,不要针对具体编程。2) 尽量使用合成(Composition),而不要使用继承来达到复用的目的。3) 使用摸板方法模式

4. 什么时候应当使用继承复用1) 子类是超类的一个特殊种类,而不是超类的一个角色,也就是要区分”is – a” 和“has-a”两种关系。2) 永远不会出现需要将子类换成另一个子类的情况。如果设计师不是很肯定一个类回不会在将来变成另一个类的子类的话,就不应当把这个类设计成这个超类的子类。 3) 子类具有扩展超类的责任,而不是具有置换掉(Override)或注销掉(Nullify)超类的责任。4) 只有在分类学上有意义时,才可以使用继承,不要丛工具类继承。

抽象方法是必须实现的方法。且只能在抽象类中。

接口与抽象类

一个类可以继承多个接口。。。
一个类只能继承一个抽象类。。。

抽象方法是必须实现的方法。就象动物都要呼吸。但是鱼用鳃呼吸,猪用肺呼吸。
动物类要有呼吸方法。怎么呼吸就是子类的事了。

现在有很多讨论和建议提倡用interface代替abstract类,两者从理论上可以做一般性的混用,但是在实际应用中,他们还是有一定区别的。抽象类一般作为公共的父类为子类的扩展提供基础,这里的扩展包括了属性上和行为上的。而接口一般来说不考虑属性,只考虑方法,使得子类可以自由的填补或者扩展接口所定义的方法,就像JAVA王子所说的事件中的适配器就是一个很好的应用。

用一个简单的例子,比如说一个教师,我们把它作为一个抽象类,有自己的属性,比如说年龄,教育程度,教师编号等等,而教师也是分很多种类的,我们就可以继承教师类而扩展特有的种类属性,而普遍属性已经直接继承了下来。

而接口呢~还是拿教师做例子,教师的行为很多,除了和普通人相同的以外,还有职业相关的行为,比如改考卷,讲课等等,我们把这些行为定义成无body的方 法,作为一个集合,它是一个interface。而教师张三李四的各自行为特点又有不同,那么他们就可以扩展自己的行为body。从这点意义上来说,interface偏重于行为。

总之,在许多情况下,接口确实可以代替抽象类,如果你不需要刻意表达属性上的继承的话。