PHP中抽象类abstract和接口interface的区别
PHP中抽象类abstract和接口interface的区别
abstract类
1、关于抽象类
1) 抽象类必须有关键字abstract来修饰,抽象类中可以不含有抽象方法,如果一个类包含抽象方法,则该类必须是抽象类。
2) 抽象类不能被直接实例化,它的意义在于被扩展(extends)
2、关于抽象类中的方法
1) 抽象方法中没有大括号:即只有方法名没有方法体
2) 只要方法没有用abstract声明,在其子类中就不用实现。而且在子类中该方法为公共方法。子类中可以重载这些方法。
3) 抽象类中的方法可以有参数,也可以为空。如果抽象方法有参数,那么子类的实现也必须有相同的参数个数。
4) 如果子类需要实例化,前提是它继承(extends)了该抽象类。实现了抽象类中的所有抽象方法,否则,子类也要用abstract标识为抽象类。
5)抽象方法是必须可以被子类继承实现的,所以不可以用private修饰符去限定abstract抽象方法,可以用public和protected去修饰。
作用:
1)当我们发现我们写的很多类里面用很多方法都是不断在重复写,那我们就可以考虑使用抽象类了。比起去重写一个公共类,每次调用相应方法之前必须得实例化这个类。使用抽象类省去了实例化的这个步骤,让我们就像直接调用本类方法一样方便,而且还可以重载这个方法。
2)跟继承普通类的区别,抽象类可以把类像的部分抽出来,只需要声明抽象方法,不会具体实现。待子类继承后根据具体业务自行去实现。
3、什么时候应该用抽象类?
抽象类通常代表一个抽象的概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所以,在一个以继承关系形成的等级结构里面,树叶节点应当是具体类,而树枝节点均应当是抽象类。
参考代码:
1 <?php 2 3 4 abstract class User 5 { 6 abstract public function findIdentity($id); 7 8 abstract protected function getAuthKey(); 9 10 public function getName($name) 11 { 12 echo $name; 13 } 14 15 public function getId() 16 { 17 echo 123; 18 } 19 } 20 21 abstract class User1 extends User 22 { 23 public function findIdentity($id) 24 { 25 echo $id; 26 } 27 } 28 29 class User2 extends User 30 { 31 public function findIdentity($id) 32 { 33 echo $id; 34 } 35 36 public function getAuthKey() 37 { 38 echo 'test'; 39 } 40 41 } 42 43 $user2 = new User2(); 44 $user2->getId(); 45 //结果输出: 123
interface类
抽象类提供了具体实现的标准,而接口则是纯粹的模版。接口只定义功能,而不包含实现的内容。
1)接口类就是一个类的领导者,指明方向,子类必须完成它指定方法。
2)定义了一个接口类,它里面的所有方法其子类必须实现。接口是类的一个模板,不能被直接实例化。其子类必须实现(implements)接口中定义的所有方法。
作用:当有很多人一起开发一个项目时,可能都会去调用别人写的一些类。如何知道别人是如何给相应功能的实现方法命名?这个时候接口类就发挥作用啦~
参考代码:
1 <?php 2 3 interface User 4 { 5 6 public function getName($name); 7 8 public function getAge($age); 9 10 } 11 12 class MyUser implements User 13 { 14 15 public function getName($name) 16 { 17 echo $name; 18 } 19 20 function getAge($age) 21 { 22 echo $age; 23 } 24 25 }
abstract与interface的区别
从表象上来说,抽象类需要继承,用extends,而接口需要实现,用implements。抽象类可以给出一些成员的实现,接口却不包含成员的实现,抽象类的抽象成员可被子类部分实现,接口的成员需要实现类完全实现,一个类只能继承一个抽象类,但可实现多个接口等等。不过这些都是从两者的形态上去区分的。
除此之外,还有三点是可以帮助我们去区分抽象类和接口的。
1、类是对对象的抽象;抽象类是对类的抽象;接口是对行为的抽象。接口是对类的局部(行为)进行的抽象,而抽象类是对类整体(字段、属性、方法)的抽象。如果只关注行为抽象,那么也可以认为接口就是抽象类。总之,不论是接口、抽象类、类甚至对象,都是在不同层次、不同角度进行抽象的结果,它们的共性就是抽象。
2、如果行为跨越不同类的对象,可使用接口。对于一些相似的类对象,用继承抽象类。比如猫呀狗呀它们其实都是动物,它们之间有很多相似的地方,所以我们应该让它们去继承动物这个抽象类,而飞机、麻雀、超人是完全不相关的类。叮当是动漫角色,孙悟空是古代神话人物,这也是不相关的类,但它们又是有共同点的,前三个都会飞,而后两个都会变出东西,所以此时让它们去实现相同的接口来达到我们的设计目的就很合适了。
其实实现接口和继承抽象类并不冲突,我们完全可以让超人继承人类,再实现飞行接口。
3、从设计角度讲,抽象类是从子类中发现了公共的东西,泛化出父类,然后子类继承父类,而接口是根本不知子类的存在,方法如何实现还不确认,预先定义。
从抽象类和接口设计的思维过程来说,抽象类时自底而上抽象出来的,而接口则是自顶向下设计出来的。
abstract的使用场合
在既需要统一的接口,又需要实例变量或缺省的方法的情况下,就可以使用abstract
- 定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口
- 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系
interface的使用场合
- 类与类之间需要特定的接口进行协调,而不在乎其如何实现
- 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
- 需要实现特定的多项功能,而这些功能之间可能完全没有任何联系
------------------------------------- 我是分割线 ---------------------------------------
2022.6.28 补充:抽象类是个特殊的存在
举例,代码如下:
1 <?php 2 3 namespace App\Model; 4 5 interface TestInterface 6 { 7 public function test1(); 8 9 public function test2(); 10 } 11 12 13 <?php 14 15 namespace App\Model; 16 17 abstract class Test1Model implements TestInterface 18 { 19 abstract public function test3(); 20 21 public function test1() 22 { 23 // TODO: Implement test1() method. 24 } 25 } 26 27 28 <?php 29 30 namespace App\Model; 31 32 class Test2Model extends Test1Model 33 { 34 public function test2() 35 { 36 // TODO: Implement test2() method. 37 } 38 39 public function test3() 40 { 41 // 实现父类中抽象方法:test3 42 echo 'test3'; 43 } 44 }
说明:抽象类比较特殊,比如这里Test1Model类 implements 接口类,可以不用实现接口类的方法,但是继承Test1Model的子类,如果没有abstract修饰类,就必须实现父类中定义的抽象方法,以及实现接口类中父类还未实现的所有方法
比如这里 Test2Model中如果没有实现test2方法,就会报错: class must be declared abstract or implement method ‘test2’
总结:仅仅理解这些是不够的,要想真正把抽象类和接口用好,还是需要好好用心地去学习设计模式,只有真正把设计模式理解好了,那么才能算是真正会合理应用抽象类和接口了。
参考资料:
程杰 《大话设计模式》