MVCアーキテクチャでInstanceOfを使用するのを回避する方法 -- java フィールド と oop フィールド と model-view-controller フィールド と refactoring フィールド と instanceof フィールド 関連 問題

How to avoid using instanceof in MVC Architecture












0
vote

問題

日本語

Javaのパックマンのようなゲームを開発しています、そして、私は現在私が好きではないこれらのコード匂いに直面しています。

私の考えを説明しましょう:私のゲームはMVCアーキテクチャに設定されています。ビューモジュールでは、モデル上にあるすべての電源投入を探し、GUIに描画する要素リストに追加します。この問題は、インターフェイスを使用する3種類の電源投入量を持っているため、電源投入を追加している場合は、どのタイプがあるかを確認してから、コレスポンデントビューを追加する必要があります。私はいくつかのコードを見せて私はより明確になることができる:

 <コード> 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())); }   

第2の香りはゴーストに関連しているので、私のゲームは状態パターンを持ち、状態が変わったらゴーストの色を変更することができます。たとえば、状態が Frightened の場合、標準が Frozen の場合、ゴーストが青色などである場合は

SO GHOTH 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 を使用しないようにする方法です。

安全なまま!

英語

I'm developing a game like Pacman in Java and I am currently facing these code smells which I do not like.

Let me explain my thinking: my game is set up on a MVC architecture. On the view module I look for every power-up that is on the model and add it to the elements list to be drawn on the GUI. The problem is that I have 3 types of power-ups that use an interface, so when I am adding the power-ups I need to check what type are they and then add the correspondent view. Let me show some code so I can be clearer:

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())); } 

The second smell is related to the ghosts, my game has a State Pattern, and it is suposed to change the ghost's color if the state is changed. For example, if the state is Frightened I want the ghosts to be orange, if the state is Frozen I want the ghosts to be blue, etc.

So when creating the Ghost View I pass the state by argument, and it checks (again with instanceof) what state is currently on. Let me show some more code:

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 } 

My question is how to avoid using instanceof without changing my model module.

Stay Safe!

</div
              
     
     

回答リスト

0
 
vote
vote
ベストアンサー
 

それは実際には分離の懸念を目指すときに解決するための非常に一般的な問題です。たとえば、レンダリングがモデルから分離されていない場合は、 PowerUp.render() メソッドを使用しているため、自分自身をレンダリングして1日に電話することができます。

しかし、あなたはあらゆる種類の懸念が単一のクラスで終わることになる別の問題を抱えています。それはトレードオフです、あなたはオブジェクトの上に直接操作を実装するか、あなたがそれらの動作を抽出するか、それから何らかの方法でタイプマッチングを実行する必要があります。

抽象ファクトリーはここで興味深く、あなたが作る必要があるタイプベースの決定の数を減らすことができますが、あなたは他のタイプベースの決定を行う必要があるべきです(例:さまざまなパワーアップサウンド)、あなたはする必要があります。もう一度一致し、それからはない方法はありません。

言っているのは、訪問者パターンを使用して、タイプマッチングのための再利用可能なタイプセーフメカニズムを実装することができます。

e.g。 (近づきのためにアクセス修飾子が残っています)

<事前> <コード> 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

このパターンの主な利点は、インスタンスのチェックとは異なり、特定の種類の電源投入を処理するのを忘れた場合は、コンパイラがお知らせします。

他の提案された抽象ファクトリソリューションのような動的型ベースの解像度では、すべてのタイプの工場が存在することを確認するためにReflectionを使用してランタイムベースの検証を実装する必要があります。

訪問者のパターンは、抽象的な構文の木のような階層構造のために自然に気にすることが多いですが、ここでは便利です。

分離をより厳格にしないと同意する場合は、他にも他にもあります。たとえば、 IPlaySound 、<コード> (-> routes = ($stateProvider, $urlRouterProvider) -> $urlRouterProvider.otherwise "/home" $stateProvider .state 'main', url: '/' templateUr l: 'app/layout/main.html' resolve: { main: function() { return Main.resolve; }, controller: 'Main' controllerAs: 'main' routes.$inject = ['$stateProvider', '$urlRouterProvider'] angular .module('app') .config(routes) )() など、 PowerUp には、さまざまな動作に特殊なインタフェースを紹介できます。電源投入時の操作直接。境界はインターフェイスによってのみ描画されていますが、まだ存在します。

最後に、オブジェクトによって直接実行された操作を行うのではなく、それぞれのレンダラやサウンドプレーヤーのパワーアップを依頼することもできます。私はここに訪問者を好むと思いますが、あなたが期待する複雑さなどに応じて猫を肌に肌を肌に肌を肌に肌を塗る方法があります!

 

That's actually a very common problem to solve when you aim at segregating concerns. For instance, if rendering wasn't segregated from the model then you'd have a PowerUp.render() method so they can render themselves and call it a day.

However, you'd then have another problem where all sorts of concerns would end up in a single class. It's a trade-off, you either implement the operations directly on the objects or you extract those behaviors, but then have to perform type matching in some way.

The abstract factory can be interesting here as it reduces the number of type-based decisions you will have to make, but should you need to make other type-based decisions (e.g. different power-up sounds), you will have to type-match again and there's no way out of it.

That being said, you could implement a reusable type-safe mechanism for type-matching using the Visitor Pattern.

e.g. (access modifiers left out for brevity)

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 

The main advantage of this pattern is that now the compiler will tell you if you've forgot to handle a specific type of power-up, unlike the instanceof checks.

With dynamic type-based resolution like in the other proposed abstract factory solution, you'd have to implement runtime-based validation using reflection to make sure a factory exists for all types.

The Visitor Pattern often comes to mind naturally for hierarchical structures like abstract syntax trees, but it's just as useful here.

There's also other ways if you agree to make the segregation less strict. For instance, you could introduce a specialized interface for the various behaviors a PowerUp may have, such as IPlaySound, IHaveAView, etc. and implement these operations in the powerups directly. The boundaries are now drawn solely by interfaces, but they still exist.

Finally, you could also ask the powerups for their respective renderer or sound player instead of having the operation carried out by the object directly. I think I prefer the visitor here, but there's many ways to skin a cat depending on the complexity you expect, etc. It's all about trade-offs!

</div
 
 
   
   
0
 
vote

あなたが探しているもののようです抽象的な工場パターンです。それを実装する方法はいくつかあります。これはあなたが必要なものに最適なものかもしれません:

<事前> <コード> 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);
 

It seems like what you're looking for is the Abstract Factory pattern. There are several ways to implement it; this may be the best match for what you need:

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); 
</div
 
 

関連する質問

6  InstanceOfによって使用される引数を渡す  ( Passing an argument to be used by instanceof ) 
私はこの構築物をZillion Quewionに有するパーサーを持っています: <事前> <コード> if (tokens.first() instanceof CommaToken) { tokens.consume(); これを行う方法を知りた...

1  PHPのInstanceOfと任意のclassnameが使用されている場合、エラーは発生しません。  ( Error does not occur when arbitrary classname is used with instanceof in php ) 
次のコードでは、 instanceof で arbitrary classname( abcdexception )を使用して、致命的なエラーが発生しません。 <事前> <コード> <?php function fn($x) { if ($x ==...

66  InstanceOFは悪い練習と考えられていますか?もしそうなら、どのような状況の下ではまだ望ましいですか?  ( Is instanceof considered bad practice if so under what circumstances is instan ) 
年間の間に、可能な限り instanceof を避けようとしました。該当する場合には、多型または訪問者パターンを使用する。私はそれが単にいくつかの状況でメンテナンスを簡単にしていると思います...あることはありますか? 私はここでそれをJavaライブラリに表示...

5  特定の条件に依存するオブジェクトの作成を伴う-lesコードの匂いがない  ( Avoid if else code smell with creation of objects which depend upon specific con ) 
は、< Product のインクシン( Condition )のインスタンス( Condition )に対処するためのより良い方法があります( Condition )。コード> instanceof 次のコードは? <事前> <コード> import java...

5  識別用に変数VS InstanceOF  ( Type variable vs instanceof for identification ) 
抽象クラス<コード> A :のサブクラスである2つのクラスがあります。 <事前> <コード> class X extends A {} class Y extends A {} 関数に渡された A オブジェクトのサブクラスを識別したいと思います。私は2つの...

-1  配列内に格納されているオブジェクトのインスタンスの場合は、非静的メソッドにアクセスできますか?  ( How can i access non static methods for instances of objects stored inside of an ) 
私のクラスの1つのシミュレーションを作成しています。ここでいくつかの方向を探しています+任意のヒント。 これまで2つのクラスを持っていて、私は他のものを実装しようとしています。現時点で、私の他のクラスに保存されているアレイにある私のオブジェクトのポイント(x...

48  変数が配列またはオブジェクトであるかどうかを効率的にチェックする方法(NodeJS&V8)?  ( How to efficiently check if variable is array or object in nodejs v8 ) 
NodeJS&AMPの変数がオブジェクトまたは配列であるかどうかを効率的にチェックする方法はあります。 v8? MongoDBとNodeJのモデルを書いていて、オブジェクトツリーを通過するには、オブジェクトが単純か(番号、文字列、...)またはコンポジッ...

93  なぜ「TypeScript」で「InstanceOf」がエラー「「foo」というエラーはタイプを参照しますが、ここでは値として使用されています。」  ( Why does instanceof in typescript give me the error foo only refers to a ty ) 
このコードを書きました <事前> <コード> interface Foo { abcdef: number; } let x: Foo | string; if (x instanceof Foo) { // ... } しかしType...

164  Javaで使用される「InstanceOf」オペレータは何ですか?  ( What is the instanceof operator used for in java ) 
<コード> instanceof 演算子は何ですか?私はのようなものを見ました <事前> <コード> if (source instanceof Button) { //... } else { //... } しかし、それのどれも私にと...

0  nodejs:runinnewContextとInstanceOf  ( Nodejs runinnewcontext and instanceof ) 
私はこのようなものをするスクリプトを持っています: <事前> <コード> var context = {} vm.runInNewContext("var someFunc = function() {}", context); console.log(typ...




© 2022 cndgn.com All Rights Reserved. Q&Aハウス 全著作権所有