这是@Peter Meyer在这个问题(What does it mean to "program to an interface"?)中给出的答案的后续问题。
首先,让我首先说我不愿意将这个问题作为一个新问题。但是(我喜欢stackoverflow,但我必须在这里有点批评)1)我不能私下给Peter Meyer留言(阅读:https://meta.stackexchange.com/questions/93896/a-proposal-for-private-messaging-within-the-stack-exchange-network),2)我无法发布'后续'问题(阅读:{ {3}})和3),问题被锁定以避免“爆炸药”,唉,我没有足够的声誉在那里问。
所以,我必须发一个新问题。
在那个帖子中,Peter Meyer展示了一个非常有趣的例子,说明何时使用接口以及为什么编程接口非常重要。
我的问题是:不会使用包装类来解决他的问题吗?
你不能写:
interface IPest {
void BeAnnoying();
}
class HouseFly inherits Insect implements IPest {
void FlyAroundYourHead();
void LandOnThings();
void BeAnnoying() {
FlyAroundYourHead();
LandOnThings();
}
}
class Telemarketer inherits Person implements IPest {
void CallDuringDinner();
void ContinueTalkingWhenYouSayNo();
void BeAnnoying() {
CallDuringDinner();
ContinueTalkingWhenYouSayNo();
}
}
class DiningRoom {
DiningRoom(Person[] diningPeople, IPest[] pests) { ... }
void ServeDinner() {
when diningPeople are eating,
foreach pest in pests
pest.BeAnnoying();
}
}
以这种方式:
class IPest {
HouseFly houseFly;
public IPest(HouseFly houseFly) {
this.houseFly = houseFly;
}
Telemarketer telemarketer;
public IPest(Telemarketer telemarketer) {
this.telemarketer = telemarketer;
}
void BeAnnoying() {
if(houseFly != null)
houseFly.BeAnnoying();
else
telemarketer.BeAnnoying();
}
}
class HouseFly inherits Insect {
void FlyAroundYourHead();
void LandOnThings();
void BeAnnoying() {
FlyAroundYourHead();
LandOnThings();
}
}
class Telemarketer inherits Person {
void CallDuringDinner();
void ContinueTalkingWhenYouSayNo();
void BeAnnoying() {
CallDuringDinner();
ContinueTalkingWhenYouSayNo();
}
}
class DiningRoom {
DiningRoom(Person[] diningPeople, IPest[] pests) { ... }
void ServeDinner() {
when diningPeople are eating,
foreach pest in pests
pest.BeAnnoying();
}
}
虽然我将此标记为与语言无关,但我真的是“Java-ifying”这个问题,因为这是我最熟悉的,所以请原谅我。但正如我所看到的,使用接口方法存在缺点。例如,如果要覆盖不同类型的“toString()”方法,则根据它是否表示为“IPest”或“HouseFly”来返回不同的值,使用该界面无法执行此操作。您不能为“HouseFly”提供不同的toString值,而不是HouseFly为接口实现IPest接口(因为HouseFly将始终通过类定义实现接口)。包装类将为您提供比界面更广泛的功能。
举例说明:假设您想要在列表中显示所有“IPest”,但您希望列表在每个列表上都有一个区别标记,以显示该害虫是Fly还是Telemarketer。然后使用包装器类,这很容易:
class IPest {
HouseFly houseFly;
public IPest(HouseFly houseFly) {
this.houseFly = houseFly;
}
Telemarketer telemarketer;
public IPest(Telemarketer telemarketer) {
this.telemarketer = telemarketer;
}
void BeAnnoying() {
if(houseFly != null)
houseFly.BeAnnoying();
else
telemarketer.BeAnnoying();
}
public String toString() {
return (houseFly == null? "(T) " + telemarketer.toString() : "(F) " + houseFly.toString()) +
}
}
然后,在另一个地方,如果你有一个列表来表示HouseFly本身(不是作为IPest而是作为HouseFly),那么你可以为toString()提供不同的值。
这不仅限于toString(),而是这些类可能具有的任何其他方法,当对象被表示为IPest与表示为HouseFly时,您可能希望覆盖这些方法以提供不同的功能或电话推销员。
我希望我的问题有道理。
我的理论是:如果您正在编程API或任何人将使用的任何东西,您应该避免使用具体的类并尝试使用接口。但是,如果您直接编写客户端代码并且对代码重用没有期望(或可能性),那么“编程到接口”似乎不是那么大。
我期待着任何反馈。我在这里偏离基地吗?我编写代码时很糟糕吗?希望Peter Meyer能够提供他的意见......
答案 0 :(得分:3)
对我来说,这使代码变得更加丑陋,没有明显的好处。 (那听起来很严厉,这不是我想要的......实际问题是+ 1)。
最大的缺点是你的IPest课程。当你继续添加可能的害虫时,这个类会因未使用的变量和代码而变得庞大而臃肿。如果您有30种不同类型的害虫,那么您的IPest类增长了15倍,比您的示例有2种,需要大量额外代码来支持所有这些类。
更糟糕的是,绝大多数此代码实际上与实例化对象无关。如果IPest应该代表HouseFly,那么有几个实例变量(每个其他类型的IPest一个)都是空的,以及大量未使用的代码。更糟糕的是,如果IPest的多个值不为空,会发生什么?它是什么? (BrundleFly电话推销员!)
将它与纯接口进行比较,随着更多类的实现,它不再增长(因为它不关心。)
最后,我认为只有两个(或更多)对象(例如HouseFly对象和IPest对象)表示的单个概念性概念(例如单个苍蝇)通常是有用的(或者一个好主意)。 。更多对象,因为您想要添加更多功能。对于每个包装器,您添加另一个对象,这是另一个可能跟踪和更新的对象。
这并不是说不可能有一些非常专业的案例,这样的事情不是一个好主意......但我在这里没有看到它,原因我在上面描述。