首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免在MVC架构中使用instanceof

如何避免在MVC架构中使用instanceof
EN

Stack Overflow用户
提问于 2020-05-14 09:01:38
回答 2查看 94关注 0票数 0

我正在用Java开发一个像Pacman这样的游戏,我目前正面临着这些我不喜欢的代码味道。

让我解释一下我的想法:我的游戏是建立在MVC架构上的。在视图模块上,我查找模型上的每个加电,并将其添加到要在GUI上绘制的元素列表中。问题是,我有三种类型的加电使用一个接口,所以当我添加加电时,我需要检查它们是什么类型,然后添加相应的视图。让我展示一些代码,以便我可以更清楚:

代码语言:javascript
复制
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)当前处于什么状态。让我展示更多的代码:

代码语言:javascript
复制
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

注意安全!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-05-14 13:34:48

当你的目标是分离关注点时,这实际上是一个非常常见的问题。例如,如果渲染没有从模型中分离出来,那么你就会有一个PowerUp.render()方法,这样他们就可以渲染自己并调用它。

但是,您还会遇到另一个问题,即所有类型的关注点最终都会集中在一个类中。这是一种权衡,你要么直接在对象上实现操作,要么提取那些行为,但随后必须以某种方式执行类型匹配。

抽象工厂在这里可能很有趣,因为它减少了您必须做出的基于类型的决策的数量,但是如果您需要做出其他基于类型的决策(例如,不同的加电声音),您将不得不再次进行类型匹配,并且没有办法。

也就是说,您可以使用Visitor Pattern为类型匹配实现一个可重用的类型安全机制。

例如(为简洁起见,省略了访问修饰符)

代码语言:javascript
复制
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可能具有的各种行为引入一个专门的接口,如IPlaySoundIHaveAView等,并直接在加电中实现这些操作。边界现在完全由界面绘制,但它们仍然存在。

最后,您还可以向电源请求其各自的渲染器或声音播放器,而不是让对象直接执行操作。我想我更喜欢这里的访问者,但是根据你期望的复杂性,有很多方法可以给猫剥皮,等等。这一切都是关于权衡的!

票数 0
EN

Stack Overflow用户

发布于 2020-05-14 10:23:13

看起来您正在寻找的是抽象工厂模式。有几种方法可以实现它;这可能是您所需的最佳匹配:

代码语言:javascript
复制
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);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61787453

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档