接口或抽象类:使用哪一个?
请解释什么时候应该使用接口,什么时候应该使用抽象类?
我怎样才能改变我的抽象类到一个接口?
当你想迫使系统中的开发人员(包括你自己)在他们将要构build的类上实现一定数量的方法时,使用一个接口。
当你想迫使在你的系统中工作的开发人员(包括你自己)实现一系列方法, 并且你想提供一些基本的方法来帮助他们开发他们的子类时,使用一个抽象类。
另外要记住的是,客户端类只能扩展一个抽象类,而他们可以实现多个接口。 所以,如果你在抽象类中定义行为契约,那就意味着每个子类只能符合一个契约。 有时这是一件好事,当你想强迫你的用户程序员沿着特定的path。 其他时候这将是糟糕的。 想象一下,如果PHP的Countable和Iterator接口是抽象类而不是接口。
当你不确定要走哪条路时(如下面的cletus所述),一种常见的方法是创build一个接口,然后让你的抽象类实现这个接口。
Abstract Class
和Interface
的区别
抽象类
一个抽象类可以提供一些function , 剩下的就是派生类
-
派生类可以覆盖或不覆盖基类中定义的具体函数
-
从抽象类扩展的子类应该在逻辑上是相关的
接口
一个接口不能包含任何function 。 它只包含方法的定义
-
派生类必须为接口中定义的所有方法提供代码
完全不同和不相关的类可以使用一个接口在逻辑上组合在一起
为什么要使用抽象类? 以下是一个简单的例子。 可以说我们有以下代码:
<?php class Fruit { private $color; public function eat() { // chew } public function setColor($c) { $this->color = $c; } } class Apple extends Fruit { public function eat() { // chew until core } } class Orange extends Fruit { public function eat() { // peeling // chew } }
现在我给你一个苹果,你吃了。 尝起来怎么样? 它尝起来像一个苹果。
<?php $apple = new Apple(); $apple->eat(); // Now I give you a fruit. $fruit = new Fruit(); $fruit->eat();
这是什么味道? 那么,这没有什么意义,所以你不应该这样做。 这是通过使Fruit类抽象以及其内部的吃法来实现的。
<?php abstract class Fruit { private $color; abstract public function eat(){} public function setColor($c) { $this->color = $c; } } ?>
抽象类就像一个接口,但是你可以在一个抽象类中定义方法,而在一个接口中它们都是抽象的。 抽象类可以有空的和工作/具体的方法。 在接口中,定义的函数不能有一个主体。 在抽象类中,他们可以。
一个真实世界的例子:
<?php abstract class person { public $LastName; public $FirstName; public $BirthDate; abstract protected function write_info(); } final class employee extends person{ public $EmployeeNumber; public $DateHired; public function write_info(){ //sql codes here echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>"; } } final class student extends person{ public $StudentNumber; public $CourseName; public function write_info(){ //sql codes here echo "Writing ". $this->LastName . "'s info to student dbase table <br>"; } } ///---------- $personA = new employee; $personB = new student; $personA->FirstName="Joe"; $personA->LastName="Sbody"; $personB->FirstName="Ben"; $personB->LastName="Dover"; $personA->write_info(); // Writing Sbody's info to emloyee dbase table $personB->write_info(); // Writing Dover's info to student dbase table
最好的做法是使用一个接口来指定合同和一个抽象类作为它的一个实现。 这个抽象类可以填充很多样板,所以你可以创build一个实现,只需重写你需要或想要的内容,而不用强迫你使用特定的实现。
只是为了把这个混合起来,但正如Cletus使用一个接口与一个抽象类一起提到的,我经常使用接口来阐明我的devise思想。
例如:
<?php class parser implements parserDecoratorPattern { //... }
这样,任何阅读我的代码(谁知道什么是装饰模式)的人都会马上知道a)我如何构build我的分析器,以及b)能够看到用来实现装饰器模式的方法。
另外,我可能不是Java / C ++ / etc程序员,但是数据types可以在这里发挥作用。 你的对象是一个types,当你通过编程方式传递types的时候。 将可签约的项目移动到接口中仅指示方法返回的types,而不指定实现它的类的基types。
现在已经很晚了,我想不出一个更好的psudo代码的例子,但是这里有:
<?php interface TelevisionControls {}; class Remote implements TelevisionControls {}; class Spouse implements TelevisionControls {}; Spouse spouse = new Spouse(); Remote remote = new Remote(); isSameType = (bool)(remote == spouse)
主要区别是抽象类可以包含默认实现,而接口不能。
接口是没有任何实现的行为契约。
从一个哲学的angular度来看:
-
抽象类表示一个“是”的关系。 可以说我有水果,好吧,我会有一个水果抽象类,共同的责任和共同的行为。
-
一个接口代表一个“应该做的”关系。 一个界面,我认为(这是一个初级开发者的意见),应该通过一个行动来命名,或者接近一个行动(对不起,找不到这个词,我不是英语母语的人)让我们说IEatable。 你知道它可以吃,但你不知道你吃什么。
从编码的angular度来看:
-
如果你的对象有重复的代码,这表明它们有共同的行为,这意味着你可能需要一个抽象类来重用代码,这是你不能用接口做的。
-
另一个区别是一个对象可以实现尽可能多的接口,但是由于“钻石问题”,你只能拥有一个抽象类(请点击这里查看原因! http://en.wikipedia.org/wiki/ Multiple_inheritance#The_diamond_problem )
我可能会忘记一些观点,但我希望能澄清一些事情。
PS:Vivek Vermani的回答是“应该做的”,我并不是想偷窃他的答案,只是为了重用这些条款,因为我喜欢他们!
要添加一些已经很好的答案:
-
抽象类让你提供一定程度的实现,接口是纯粹的模板。 一个接口只能定义function ,它永远不能实现它。
-
任何实现接口的类都承诺实现它定义的所有方法,或者它必须声明为抽象的。
-
接口可以帮助pipe理像Java这样的事实,PHP不支持多inheritance。 一个PHP类只能扩展一个父类。 然而,你可以做一个类的承诺,实现尽可能多的接口,你想要的。
-
键入:对于其实现的每个接口,该类将采用相应的types。 因为任何类都可以实现一个接口(或多个接口),所以接口可以有效地join那些不相关的types。
-
一个类既可以扩展超类,也可以实现任意数量的接口:
class SubClass extends ParentClass implements Interface1, Interface2 { // ... }
请解释什么时候应该使用接口,什么时候应该使用抽象类?
当你只需要提供一个没有实现的模板的时候,使用一个接口,并且你要确保任何实现了这个接口的类都和其他实现它的类(至less)有相同的方法。
当您想要为其他对象(部分构build的类)创build基础时使用抽象类。 扩展抽象类的类将使用一些定义/实现的属性或方法:
<?php // interface class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code. // abstract class class X extends Y { } // this is saying that "X" is going to complete the partial class "Y". ?>
我怎样才能改变我的抽象类到一个接口?
这是一个简单的案例/例子。 拿出任何实施细节。 例如,将您的抽象类从:
abstract class ClassToBuildUpon { public function doSomething() { echo 'Did something.'; } }
至:
interface ClassToBuildUpon { public function doSomething(); }
另外,在这里只是想补充一点,就是因为任何其他的OO语言都有某种接口和抽象,并不意味着它们和PHP有相同的含义和目的。 抽象/接口的使用略有不同,而PHP中的接口实际上并没有真正的function。 它们仅用于语义和scheme相关的原因。 关键是要让项目尽可能灵活,可扩展,并且可以安全地用于未来的扩展,而不pipe开发者以后是否有完全不同的使用计划。
如果你的英文不是本地的,你可能会查找抽象和接口实际上是什么。 并寻找同义词。
这可能会帮助你作为一个比喻:
接口
比方说,你用草莓烤一个新蛋糕,然后你制定了一个配方和步骤的配方。 只有你知道为什么它的品尝如此之好,而且你的客人喜欢它。 然后你决定发布你的食谱,以便其他人也可以尝试这个蛋糕。
这里的重点是
– 做正确的
– 要小心
– 防止可能变坏的东西(比如草莓太多)
– 让那些试用它的人保持容易
– 告诉你要做多长时间(如搅拌)
– 告诉你可以做哪些事情,但不必做
确切地说,这是描述接口的东西。 这是一个指导,一套指导,观察配方的内容。 就像在PHP中创build一个项目一样,你希望在GitHub上或者你的队友等等上提供代码。 界面是人们可以做的事情,你不应该做的事情。 拥有它的规则 – 如果违反规则,整个构造将被打破。
ABSTRACTION
在这里继续这个比喻…想象一下,这次你吃这个蛋糕是你的追求。 那么你现在正在尝试使用这个配方的蛋糕。 但是你想添加新的成分或改变/跳过配方中描述的步骤。 那么接下来呢? 计划一个不同版本的蛋糕。 这次用黑浆果而不是草莓浆果和更多香草奶油…美味。
这是你可以考虑的原始蛋糕的延伸。 你基本上是通过创build一个新的配方来抽象它,因为它是一个不同的。 它有一些新的步骤和其他成分。 然而,黑莓的版本有你从原来的一些部分 – 这是每一种蛋糕必须有的基础步骤。 就像牛奶一样 – 这就是每一个派生类的成分。
现在你想交换配料和步骤,这些必须在新版本的蛋糕中定义。 这些都是抽象的方法 ,必须为新的蛋糕定义,因为蛋糕中应该有一个水果,但是哪一个? 所以这次你吃黑莓。 完成。
你去了,你已经延长了蛋糕,按照界面和抽象的步骤和成分。
那么抽象类和接口之间的技术差异就已经被其他答案精确地列出了。 为了面向对象的编程,我想在编写代码时添加一个解释来在类和接口之间进行select。
一个类应该代表一个实体,而一个接口应该代表这个行为。
让我们举个例子。 计算机监视器是一个实体,应该表示为类。
class Monitor{ private int monitorNo; }
那么它的devise就是为你提供一个显示界面。 所以function应该由一个接口定义。
interface Display{ void display(); }
那么在其他答案中还有很多其他的东西需要考虑,但这是大多数人在编写代码时忽略的最基本的东西。