第一个设计模式原则:按接口而不是按实现来编程。
以最简单的方式来讲就是,要将变量设置为一个抽象类或接口数据类型的实例,而不是一个具体实现的实例。
比如说,声明一个变量$bianliang
,那么其值类型应该是某个抽象类或接口。这在强类型语言中比较容易做到,假设有个接口ITest,那么我声明变量则应该是这样声明的ITest bianliang
,此时变量bianliang
的数据类型是ITest
。但对于PHP来说,这可能有些困难,因为PHP变量的数据类型是在给该变量赋值时而确定的,但抽象类或接口不能被直接实例化(因为需要被继承实现啊),所以也就无法得到一个接口数据类型的PHP变量。最多得到一个该接口具体实现类的数据类型的变量。看下列代码及注释:
//现有一个接口ITest
Interface ITest
{
...
}
//现有一个类Ceshi,实现了ITest
class Ceshi implenmts ITest
{
...
}
//那么,最多我声明的变量$bianliang的数据类型是Ceshi
$bianliang = new Ceshi;
但其实没关系,变量$bianliang
的数据类型不仅是Ceshi
,ITest
也可以作为该变量的数据类型。记住这一句话:一个对象实例的数据类型不仅是它实例化的对象类型,该对象的父类也将作为它的数据类型
在这里要注意一点是,按接口编程中的接口这个概念,并不是只用关键字Interface创建的才叫接口,我们这里的接口可以指Interface创建出来的也可以指某个抽象类,我觉得其实所有类都可以成为接口,不过这里我们就只要认为是Interface和抽象类吧,免得搞混。
来一个按接口编程的例子:
abstract class IAbstract
{
//对所有实现都可用的属性
protected $valueNow;
/*所有实现都必须包含以下两个方法*/
//必须返回十进制值
abstract protected function giveCost();
//必须返回字符串值
abstract protected function giveCity();
//这个具体函数对所有类实现都可以用,而不覆盖内容
public function displayShow()
{
$stringCost = $this->giveCost();
$stringCost = (string)$stringCost;
$allTogether = ("Cost: $": . $stringCost . " for " . $this->giveCity());
return $allTogether;
}
}
//接下来是这个抽象类的两个不同扩展,它们分别提供抽象方法的不同实现。
class NorthRegion extends IAbstract
{
//必须返回十进制值
protected function giveCost()
{
return 210.54;
}
//必须返回字符串值
protected function giveCity()
{
return "Moose Breath";
}
}
class WestRegion extends IAbstract
{
//必须返回十进制值
protected function giveCost()
{
$solarSavings = 2;
$this->valueNow=210.54/$solarSavings;
return $this->valueNow;
}
//必须返回字符串值
protected function giveCity()
{
return "Rattlesnake Gulch";
}
}
利用一个抽象类的两个不同实现,可以看到,这里所说的“按接口编程”实际上是指类的接口,而不是使用关键字interface定义的接口结构。最后,Client类建立了一个包含代码提示的方法,指定这个抽象类作为接口:
class Client
{
public function __construct()
{
$north = new NorthRegion;
$west = new WestRegion;
$this->showInterface($north);
$this->showInterface($west);
}
private function showInterface(IAbstract $region)
{
echo $region->displayShow().'<br>';
}
}
$worker = new Client();
最终输出结果:
Cost: $210.54 for Moose Breath
Cost: $105.27 for Rattlesnake Gulch
对于不同的地区,结果值是不同的,因为两个具体类NorthRegion
和WestRegion
分别采用了不同方式来实现这个抽象方法。如果使用了一个不正确的数据类型,则会报错。
所以,就其自身而言,类型提示可以帮助你尽可能遵守第一个设计模式原则,即按接口而不是按实现来编程。
如果想了解这种编程风格的好处,可以增加IAbstract
抽象类的实现,并增加到Client类中,可以看到,只要保持接口一致,完成增补和修改很容易。