我有一个我似乎无法弄清楚的设计问题。我正在实施纸牌游戏,我正在努力解决如何组织卡片的结构。游戏中的每张牌都有一些共享行为,这意味着Card应该是基类。
但是,每张卡可以是硬币,法力卡或健康卡。最重要的是,任何卡都可以是这三种类型的组合。我最初的想法是让Coin,Mana和Health各自成为一个接口,然后我就能用相关的接口来实现任何卡片:
public class SomeCard extends Card implements Coin, Mana {
}
通过这种方式,所有继承的类都将具有基本卡行为并实现必要的行为。问题产生于三个接口中的每一个都存在一些共享行为的事实。例如,对于Coin,有一个getWorth()函数对于所有Coin卡基本相同。如果我有相同的功能&实现然后应该有一种方法来抽象出冗余。这只是冗余的一个例子。
public class CardA extends Card implements Coin {
public int getWorth(){
return worth;
}
//Other inherited or implemented methods
}
public class CardB extends Card implements Coin, Health {
public int getWorth(){
return worth;
}
//Other inherited or implemented methods
}
不幸的是,由于Coin是一个接口,因此无法在所有卡上减少这种冗余。有没有办法删除冗余代码同时应用硬币,法力,健康类型或其中某些组合?
我想避免这样的事情:
public class SomeCard extends CoinManaCard {
}
我认为上面的实现非常糟糕,因为无论何时添加新类型,都会有更多类型的组合,维护也很重要。
提前感谢您的帮助!
答案 0 :(得分:0)
共享行为可以移动到超类。如果由于某种原因你不能修改卡,你可以引入另一个级别的继承 - 在我的环境中我们通常称之为BaseCard - 它实现了Card的所有方法(你可以在特定的方法中再次覆盖它们)卡类)或者是abstract class
因此它只能实现共享行为,而其他行为则由派生自它的类实现。然后你的实际CardA,CardB等等会扩展BaseCard,这也会使它们成为卡片。
(请注意,当你使用“abstract”这个词时,你几乎自己回答了这个问题。当你想要分解出共享行为时,另一层继承是一种方法,抽象类是一种很好的方法来保持相对容易,并明确表示你不打算直接使用它们。)
另一个解决方案是委托 - 将is-a关系更改为has-a,让每个卡拥有一个提供共享功能的实用程序类的实例,并让卡的方法调用该实用程序类的方法做好这份工作。同样,这可以让您分解出一组逻辑,但这完全独立于继承。效率稍低,但这是一个非常合理的解决方案。如果你需要从几个不同的类中获取行为,这是唯一的解决方案,因为Java只允许接口的多重继承,而不是类(即使它们是抽象的) - 一个超类(具有它具有的任何继承),以及尽可能多的接口。
(如果你想知道为什么Java有这个限制,请查看“钻石继承”。简单的答案是它可以非常快速地变得非常混乱,甚至在支持它的语言中,人们常常会使用更像Java的单继承加接口加委托模型。)