在我写的基于精灵的游戏中,2D网格中的每个字段都包含一堆精灵。最重要的是最重要的。
在游戏的规则模块中,我有很多这样的代码:
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(!(board.getTopSpriteAt(point) instanceof Box))
return false;
return true;
}
Upadate: //Do something
会计算每个Box
之上是否有Target
。我不知道如何简单地将doSomething()
添加到Sprite,除非doSomething()
如果精灵是一个盒子,则返回1,否则返回0。 (这与instanceof相同)。
我知道instanceof被认为是有害的,因为它杀死了面向对象编程的想法。
但我不知道如何在我的情况下修复代码。以下是我的一些想法:
isABox()
界面添加Sprite
方法不会更好。Box
是一个接口会有帮助吗,那么其他类可以获得相同的权限吗?我希望你尝试过类似的东西,并能指出我正确的方向。
答案 0 :(得分:7)
使用polymorphism:
class Sprite {
..
someMethod(){
//do sprite
}
..
}
class Box extends Sprite {
..
@Overrides
someMethod(){
//do box
}
..
}
所以,你只需要在你的例子中调用sprite.someMethod()。
答案 1 :(得分:5)
Instanceof
:(差不多)总是有害
我看了你帖子的所有答案,并试图了解你在做什么。我得出的结论是instanceof
完全你想要什么,你的原始代码样本很好。
你澄清说:
您不违反了Liskov替换原则,因为没有任何Box代码使Sprite代码无效。
您不使用对instanceof
的回复来分叉代码。这就是为什么人们说实例是坏的;因为人们这样做:
if(shape instanceof Circle) {
area = Circle(shape).circleArea();
} else if(shape instanceof Square) {
area = Square(shape).squareArea();
} else if(shape instanceof Triangle) {
area = Triangle(shape).triangleArea();
}
这就是人们避免使用instanceof的原因。但这不是你在做什么。
Box
与赢得游戏之间存在一对一关系(没有其他精灵可以赢得比赛)。因此,您不需要额外的“赢家”精灵抽象(因为Boxes == Winners)。
您只需检查该主板,以确保每个顶级商品 一个Box。这正是instanceof
的目的。
其他人的回答(包括我自己的回答)增加了一个额外的机制来检查Sprite是否是一个Box。但是,它们不会增加任何稳健性。实际上,您正在使用已经由语言提供的功能,并在您自己的代码中重新实现它们。
Tomas Narros认为你应该在代码中区分“语义类型”和“java类型”。我不同意。您已经确定您拥有一个java类型Box
,其子类为Sprite
。所以你已经掌握了所需的所有信息。
在我看来,拥有第二个独立机制也报告“我是一个盒子”,违反了DRY(不要重复自己)。这意味着对于同一条信息没有两个独立的来源。您现在必须维护枚举和类结构。
所谓的“利益”是能够围绕完全填满目的的关键词旋转,并且在以更有害的方式使用时是有害的。
黄金法则使用你的头。不要把规则当成事实。问他们,了解他们为什么在那里,并在适当的时候弯曲它们。
答案 2 :(得分:3)
基本重载是去这里的方法。它是Sprite类层次结构,它应该知道该做什么以及如何做,如:
interface Sprite {
boolean isCountable();
}
class MyOtherSprite implements Sprite {
boolean isCountable() {
return false;
}
}
class Box implements Sprite {
boolean isCountable() {
return true;
}
}
int count = 0;
for (Point point : board.getTargetPoints()) {
Sprite sprite = board.getTopSpriteAt(point);
count += sprite.isCountable() ? 1 : 0;
}
修改强>
您对问题的编辑不会从根本上改变问题。你所拥有的是一些仅适用于Box
的逻辑。再次,将该特定逻辑封装在Box
实例中(参见上文)。你可以进一步为你的精灵创建一个通用的超类来定义isCountable()
的默认值(请注意,该方法类似于isBox一,但从设计角度来看实际上更好,因为它没有意义圆圈有一个isBox方法 - Box应该还包含一个isCircle方法吗?)。
答案 3 :(得分:3)
这是我的尝试。考虑使用不同的Sprite类型定义枚举:
class Sprite {
public enum SpriteType {
BOX, CAT, BOTTLE, HUMAN, TARGET, /* ... */, SIMPLE;
}
public SpriteType getSpriteType(){
return SIMPLE;
}
}
class Box extends Sprite {
@Override
public SpriteType getSpriteType(){
return Sprite.SpriteType.BOX;
}
}
最后:
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(board.getTopSpriteAt(point).getSpriteType()!=SpriteType.BOX)
return false;
return true;
}
这样,您就可以解决必须在Sprite中为每个类型X创建一个isATypeX()方法的问题。 如果您需要新类型,则向枚举添加新值,只有需要检查此类型的规则才需要知道它。
答案 4 :(得分:1)
基本上,而不是
if (sprite instanceof Box)
// Do something
使用
sprite.doSomething()
其中doSomething()
在Sprite
中定义,并在Box
中覆盖。
如果您希望将这些规则与Sprite类层次结构分开,可以将它们移动到单独的Rules
类(或接口),其中Sprite
具有getRules()
方法,并且子类返回不同的实现。这将进一步提高灵活性,因为它允许相同Sprite
子类的对象具有不同的行为。
答案 5 :(得分:1)
以下是您可能想要计算的每种类型的没有isAnX()方法的通用计数器的示例。假设您想要计算板上X型的数量。
public int count(Class type) {
int count = 0;
for (Point point : board.getTargetPoints())
if(type.isAssignable(board.getTopSpriteAt(point)))
count++;
return count;
}
我怀疑你真正想要的是
public boolean isAllBoxes() {
for (Point point : board.getTargetPoints())
if(!board.getTopSpriteAt(point).isABox())
return false;
return true;
}
答案 6 :(得分:1)
你真正在这里测试的是,
玩家赢得游戏,而
Sprite
位于主席顶部吗?
因此,我建议这些名字:
public boolean isGameWon(Board board) {
for (Point point : board.getTargetPoints())
if(!board.getTopSpriteAt(point).isWinningSprite())
return false;
return true;
}
拥有isBox
功能绝对没有意义。没有任何。您也可以使用instanceof
。
但如果Box
,Bottle
和Target
都是获胜的牌,那么你可以将它们全部归还
class Box {
public override bool isWinningSprite() { return true; }
}
然后,您可以添加其他类型的“获胜”精灵,而无需更改isGameWon
功能。
答案 7 :(得分:1)
如何使用访客。
每个点都继承了acceptBoxDetection方法:
boolean acceptBoxDetection(Visitor boxDetector){
return boxDetector.visit(this);
}
and then Visitor does:
boolean visit(Box box){
return true;
}
boolean visit(OtherStuff other){
return false;
}
答案 8 :(得分:0)
关于面向对象设计/重构的一般陈述很难给予恕我直言,因为“最佳”行动非常依赖于上下文。
你应该尝试将“做某事”移动到Sprite的虚拟方法中,它什么都不做。可以在循环中调用此方法。
Box然后可以覆盖它并执行“某事”。
答案 9 :(得分:0)
我认为人们向你建议的是正确的。您的doSomething()
可能如下所示:
class Sprite {
public int doSomething(int cnt){
return cnt;
}
}
class Box extends Sprite {
@Override
public int doSomething(int cnt){
return cnt + 1;
}
}
因此,在您的原始代码中,您可以执行此操作:
int cnt = 0;
for (Point point : board.getTargetPoints()) {
Sprite sprite = board.getTopSpriteAt(point)
cnt = sprite.doSomething(cnt);
}
否则,您也可以使用您的建议实现相同的目标,但每个循环可能需要额外的计算。
class Sprite {
public boolean isBox() {
return false;
}
}
class Box extends Sprite {
@Override
public boolean isBox(){
return true;
}
}
答案 10 :(得分:-1)
当你说'堆叠'和'最重要的一个'时,你能不能只拿最上面一个并用它做点什么?