什么时候该用接口,什么时候该用抽象类?

抽象类与接口

抽象类的侧重点在于复用,而接口的侧重点在于解耦。

抽象类可以有属性和方法,没有方法体的方法叫抽象方法,含抽象方法的类一定是抽象类。抽象类不能被实例化,但是可以被继承,子类继承抽象类必须实现父类的抽象方法。

接口只有抽象方法,没有属性和具体的方法体,类实现接口时,必须实现所有的方法。

抽象类实际上就是类,只不过是一种特殊的类,这种类不能被实例化为对象,只能被子类继承。我们知道,继承关系是一种 is-a 的关系,那抽象类既然属于类,也表示一种 is-a 的关系。相对于抽象类的 is-a 关系来说,接口表示一种 has-a 关系,表示具有某些功能。对于接口,有一个更加形象的叫法,那就是协议(contract)。

用类来模拟接口

使用抽象类来模拟接口非常的简单,只要抽象类里没有任务属性和方法,只有抽象方法即可。

abstract Log
{
   abstract public function log ();
}

那么如何用一般类来模拟接口呢?一般类是没有抽象方法的,另外一般类都是可以实例化的。首先,我们解决掉能够实例化的问题,这个问题简单,只需要将构造方法设置为protacted即可。但如何模拟抽象方法呢?我们可以这样,在方法体内直接抛出一个异常,如果子类没有重写该方法时,就会抛出异常。

class Log
{
   protected function __construct()
  {
  }

   public function log ()
  {
       throw new \Exception('请重写该方法');
  }
}

如何决定是使用抽象类还是使用接口

实际上,判断的标准很简单。如果我们要表示一种 is-a 的关系,并且是为了解决代码复用的问题,我们就用抽象类;如果我们要表示一种 has-a 关系,并且是为了解决抽象而非代码复用的问题,那我们就可以使用接口。

从类的继承层次上来看,抽象类是一种自下而上的设计思路,先有子类的代码重复,然后再抽象成上层的父类(也就是抽象类)。而接口正好相反,它是一种自上而下的设计思路。我们在编程的时候,一般都是先设计接口,再去考虑具体的实现。