我正在用Java开发一个像Pacman这样的游戏,我目前正面临着这些我不喜欢的代码味道。
让我解释一下我的想法:我的游戏是建立在MVC架构上的。在视图模块上,我查找模型上的每个加电,并将其添加到要在GUI上绘制的元素列表中。问题是,我有三种类型的加电使用一个接口,所以当我添加加电时,我需要检查它们是什么类型,然后添加相应的视图。让我展示一些代码,以便我可以更清楚:
for (PowerUp powerUp : level.getPowerUps()) {
if (powerUp instanceof Invincibility) elements.add(new InvincibilityView(powerUp.getPosition()));
if (powerUp instanceof Freeze) elements.add(new FreezeView(powerUp.getPosition()));
if (powerUp instanceof Fright) elements.add(new FrightView(powerUp.getPosition()));
}第二个气味与鬼魂有关,我的游戏有一个State模式,如果状态改变,它就会改变鬼魂的颜色。例如,如果状态为Frightened,我希望重影为橙色,如果状态为Frozen,我希望重影为蓝色,依此类推。
因此,在创建Ghost View时,我通过参数传递状态,它会检查(同样使用instanceof)当前处于什么状态。让我展示更多的代码:
public void draw(graphics) {
String color = "#FF0000";
if (state instanceof Invincible) color = "#585858";
if (state instanceof Frozen) color = "#00FFFF";
if (state instanceof Frightened) color = "#FF7F50";
// draw ghost
}我的问题是如何在不更改模型模块的情况下避免使用instanceof。
注意安全!
发布于 2020-05-14 13:34:48
当你的目标是分离关注点时,这实际上是一个非常常见的问题。例如,如果渲染没有从模型中分离出来,那么你就会有一个PowerUp.render()方法,这样他们就可以渲染自己并调用它。
但是,您还会遇到另一个问题,即所有类型的关注点最终都会集中在一个类中。这是一种权衡,你要么直接在对象上实现操作,要么提取那些行为,但随后必须以某种方式执行类型匹配。
抽象工厂在这里可能很有趣,因为它减少了您必须做出的基于类型的决策的数量,但是如果您需要做出其他基于类型的决策(例如,不同的加电声音),您将不得不再次进行类型匹配,并且没有办法。
也就是说,您可以使用Visitor Pattern为类型匹配实现一个可重用的类型安全机制。
例如(为简洁起见,省略了访问修饰符)
interface PowerUpVisitor {
visit(Invincibility powerUp);
visit(Freeze powerUp);
visit(Fright powerUp);
}
interface PowerUp {
accept(PowerUpVisitor visitor);
...
}
class Freeze implements PowerUp {
accept(PowerUpVisitor visitor) { visitor.visit(this); }
}
class PlayPowerUpSound implements PowerUpVisitor {
visit(Invincibility powerUp) { playInvicibilitySound(); }
...
}
class RenderPowerUp implements PowerUpVisitor {
visit(Invincibility powerUp) { renderInvisibility(); }
...
}
//In practice you would most likely reuse the same visitor instances
somePowerUp.accept(new PlayPowerUpSound()); //to play sound
somePowerUp.accept(new RenderPowerUp()); //to render此模式的主要优点是,现在编译器会告诉您是否忘记处理特定类型的加电,这与instanceof检查不同。
与其他建议的抽象工厂解决方案一样,使用基于类型的动态解析,您必须使用反射实现基于运行时的验证,以确保所有类型都存在一个工厂。
对于抽象语法树这样的分层结构,访问者模式通常很自然地出现在脑海中,但在这里它也同样有用。
如果你同意让隔离变得不那么严格,还有其他方法。例如,您可以为PowerUp可能具有的各种行为引入一个专门的接口,如IPlaySound、IHaveAView等,并直接在加电中实现这些操作。边界现在完全由界面绘制,但它们仍然存在。
最后,您还可以向电源请求其各自的渲染器或声音播放器,而不是让对象直接执行操作。我想我更喜欢这里的访问者,但是根据你期望的复杂性,有很多方法可以给猫剥皮,等等。这一切都是关于权衡的!
发布于 2020-05-14 10:23:13
看起来您正在寻找的是抽象工厂模式。有几种方法可以实现它;这可能是您所需的最佳匹配:
interface ViewFactory<V extends View> {
boolean canHandlePowerUp(PowerUp target);
V createView(PowerUp target);
}
class FreezeViewFactory extends ViewFactory<FreezeView> { ... }
...
List<ViewFactory<? extends V>> factories;
View v = factories.stream()
.filter(f -> f.canHandlePowerUp(p))
.findFirst()
.map(f -> createView(p))
.orElseThrow(() -> new IllegalStateException("no ViewFactory found for " + p);https://stackoverflow.com/questions/61787453
复制相似问题