我曾经创建许多抽象类/方法。然后我开始使用接口。
现在,我不确定接口是否不会使抽象类过时。
您需要一个完全抽象的类吗?创建一个接口。您需要一个带有一些实现的抽象类吗?创建一个接口,创建一个类。继承该类,实现接口。另一个好处是某些类可能不需要父类,而只实现接口。
那么,抽象类/方法是否过时了?
我曾经创建许多抽象类/方法。然后我开始使用接口。
现在,我不确定接口是否不会使抽象类过时。
您需要一个完全抽象的类吗?创建一个接口。您需要一个带有一些实现的抽象类吗?创建一个接口,创建一个类。继承该类,实现接口。另一个好处是某些类可能不需要父类,而只实现接口。
那么,抽象类/方法是否过时了?
Answers:
没有。
接口不能提供默认实现,抽象类和方法可以。在许多情况下,这对于避免代码重复特别有用。
这也是减少顺序耦合的一种非常好的方法。没有抽象方法/类,就无法实现模板方法模式。我建议您看一下这篇维基百科文章:http : //en.wikipedia.org/wiki/Template_method_pattern
存在一个抽象方法,因此您可以从基类中调用它,但可以在派生类中实现它。因此,您的基类知道:
public void DoTask()
{
doSetup();
DoWork();
doCleanup();
}
protected abstract void DoWork();
在派生类不了解设置和清除活动的情况下,这是在中间模式中实现漏洞的一种相当不错的方法。没有抽象方法,你必须依靠派生类中实现DoTask
并记住调用base.DoSetup()
和base.DoCleanup()
所有的时间。
编辑
另外,感谢deadalnix发布了到模板方法模式的链接,这是我在上面描述的,而实际上并未知道其名称。:)
public void DoTask(Action doWork)
Fruit
,派生类是Apple
,则您要调用myApple.Eat()
,而不是myApple.Eat((a) => howToEatApple(a))
。另外,您也不必Apple
致电base.Eat(() => this.howToEatMe())
。我认为仅重写抽象方法会更清洁。
不,它们不是过时的。
实际上,抽象类/方法和接口之间存在模糊但根本的区别。
如果必须使用其中一个的一组类具有它们共享的共同行为(我是说相关的类),那么请使用Abstract类/方法。
示例:文员,干事,总监-所有这些类共有CalculateSalary()并使用抽象基类.CalculateSalary()可以以不同的方式实现,但某些其他类似GetAttendance()的示例在基类中具有通用定义。
如果您的类之间没有共同点(在选择的上下文中,不相关的类),但是在实现上有很大不同,那么请使用Interface。
示例:与牛,长凳,汽车,伸缩式无关的类,但是Isortable可以在那里对它们进行数组排序。
从多态性角度来看,通常会忽略这种差异。但我个人认为,由于上述原因,在某些情况下一个人比另一个人更合适。
除了其他好的答案之外,接口和抽象类之间还存在一个根本的差异,没有人特别提到过,即接口的可靠性远低于抽象类,因此带来了更大的测试负担。例如,考虑以下C#代码:
public abstract class Frobber
{
private Frobber() {}
public abstract void Frob(Frotz frotz);
private class GreenFrobber : Frobber
{ ... }
private class RedFrobber : Frobber
{ ... }
public static Frobber GetFrobber(bool b) { ... } // return a green or red frobber
}
public sealed class Frotz
{
public void Frobbit(Frobber frobber)
{
...
frobber.Frob(this);
...
}
}
我保证只有两条代码路径需要测试。Frobbit的作者可以依靠Frobber是红色或绿色的事实。
如果相反,我们说:
public interface IFrobber
{
void Frob(Frotz frotz);
}
public class GreenFrobber : IFrobber
{ ... }
public class RedFrobber : Frobber
{ ... }
public sealed class Frotz
{
public void Frobbit(IFrobber frobber)
{
...
frobber.Frob(this);
...
}
}
现在我知道绝对没有关于呼叫FROB存在的影响。我需要确保Frobbit中的所有代码对于IFrobber的任何可能实现都是健壮的,甚至包括那些不称职(不好)或对我或我的用户充满敌意(更糟糕)的人的实现。
抽象类使您可以避免所有这些问题。使用它们!
正如我在@deadnix帖子上评论的那样:部分实现是一种反模式,尽管事实上模板模式已将其正式化。
这个Wikipedia示例模板模式的干净解决方案:
interface Game {
void initialize(int playersCount);
void makePlay(int player);
boolean done();
void finished();
void printWinner();
}
class GameRunner {
public void playOneGame(int playersCount, Game game) {
game.initialize(playersCount);
int j = 0;
for (int i = 0; !game.finished(); i++)
game.makePlay(i % playersCount);
game.printWinner();
}
}
class Monopoly implements Game {
//... implementation
}
此解决方案更好,因为它使用composition而不是继承。模板模式在垄断规则的实现与游戏运行方式之间引入了依赖关系。但是,这是两个完全不同的职责,没有充分的理由将它们耦合在一起。
抽象类不是接口。它们是无法实例化的类。
您需要一个完全抽象的类吗?创建一个接口。您需要一个带有一些实现的抽象类吗?创建一个接口,创建一个类。继承该类,实现接口。另一个好处是某些类可能不需要父类,而只实现接口。
但是,那么您将获得一个非抽象的无用类。需要抽象方法来填充基类中的功能漏洞。
例如,给定此类
public abstract class Frobber {
public abstract void Frob();
public abstract boolean IsFrobbingNeeded { get; }
public void FrobUntilFinished() {
while (IsFrobbingNeeded) {
Frob();
}
}
}
您如何在既Frob()
没有类又没有类的类中实现此基本功能IsFrobbingNeeded
?
我认为接口不会让它们过时,但是策略模式可能会使它们过时。
抽象类的主要用途是推迟实现的一部分。这样说,“可以将类的实现的这一部分设置为不同的”。
不幸的是,一个抽象类迫使客户通过继承来做到这一点。策略模式将使您无需继承即可达到相同的结果。客户端可以创建类的实例,而不必总是定义自己的实例,并且类的“实现”(行为)可以动态变化。策略模式具有额外的优点,即行为不仅可以在设计时在运行时更改,而且在涉及的类型之间的耦合也很弱。
与ABC相比,与ABC相比,与纯接口相关的维护问题通常要多得多,甚至包括用于多重继承的ABC。YMMV-不知道,也许我们的团队没有充分利用它们。
就是说,如果我们使用现实世界的类比,那么完全没有功能和状态的纯接口有多少用途?如果以USB为例,那是一个相当稳定的接口(我认为我们现在使用的是USB 3.2,但它也保持了向后兼容性)。
但这不是一个无状态的接口。它并非没有功能。它更像是抽象基类,而不是纯接口。实际上,它更接近于具有非常特定的功能和状态要求的具体类,唯一的抽象是插入端口的内容是唯一可替换的部分。
否则,这将只是计算机中的“漏洞”,具有标准化的外形尺寸和更宽松的功能要求,直到每个制造商都想出自己的硬件来使该漏洞起作用为止,它自己不会做任何事情它变成了一个弱得多的标准,仅是一个“漏洞”和它应该做什么的规范,但没有关于如何做到的中央规定。同时,在所有硬件制造商尝试提出自己的方法来将功能和状态附加到该“漏洞”之后,我们可能最终会采用200种不同的方法来执行此操作。
到那时,我们可能会有某些制造商引入了与其他制造商不同的问题。如果我们需要更新规范,我们可能有200种不同的具体USB端口实现,而处理规范的方式完全不同,必须对其进行更新和测试。一些制造商可能会开发他们之间共享的事实上的标准实现(您的类比基类实现了该接口),但不是全部。有些版本可能比其他版本慢。有些可能具有更好的吞吐量,但延迟更糟,反之亦然。有些电池可能比其他电池消耗更多的电量。有些可能无法正常运行,并且不能与应该与USB端口一起使用的所有硬件一起使用。有些人可能需要安装一个核反应堆才能运行,这有可能使用户感到辐射中毒。
这就是我个人使用纯接口所发现的。在某些情况下,它们可能是有道理的,例如仅针对CPU机箱模拟主板的外形尺寸。实际上,与类似的“漏洞”一样,形状因数类比实际上几乎是无状态的并且没有功能。但是我经常认为团队认为在所有情况下都具有某种优势而不是接近优势是一个巨大的错误。
相反,我认为,如果是这两种选择,那么ABC会比接口更好地解决更多情况,除非您的团队如此庞大,以至于实际上需要类比超过200个竞争USB实现而不是一个中心标准,保持。在我曾任职的前团队中,我实际上不得不奋斗只是为了放松编码标准以允许ABC和多重继承,并且主要是为了应对上述这些维护问题。