可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm working on a project for university where we're programming a game. In this game several effects can occur and one effect can affect how another effect behaves. Now one of the idea's was using a composite pattern which seemed like a solid solution at first. The biggest problem here is that the way an effect behaves depends on what effect it's coupled with and the only way we saw to fix this was using .getClass() or instanceof which we want to avoid at all cost.
What are some ways to design this issue?
Edit (a small example): I don't have a explicit code example but I'll try to clarify a bit with the following example:
So the game has grenades (which can obviously explode and cause damage), this explosion is seen as an "ExplosionEffect". The square that the grenade is positioned on can have a powerfailure at runtime (PowerfailureEffect). The ExplosionEffect and PowerfailureEffect can be coupled and this causes the explosion to be stronger and cause more damage. The ExplosionEffect can also be coupled with other effects causing the explosion damage to behave even more differently
回答1:
You could use the Visitor Design Pattern. All effect class implements an Effect interface. Main class ask the EffectB class using the Effect interface method how it should behave. Implementation of Effect's method in EffectB class call the right method in main class.
回答2:
How about another interface. Maybe have various Modifier
interfaces that the effects implement. These modifiers could do things like "increaseExplosivePower". You can do instanceof
to see if the class implements the modifier and then one effect can modify the other.
Note: doing an instanceof
on an interface is quite acceptable - you are not working out if it can do something based on what it is but on what APIs it has.
回答3:
public void test(){ methodForWaterEffect(new WaterEffect()); combinationAttack1(new FireEffect(), new WaterEffect()); } interface Effect{ public void activateEffect(); } class FireEffect implements Effect{ @Override public void activateEffect() { System.out.println("Fire!"); } } class WaterEffect implements Effect{ @Override public void activateEffect() { System.out.println("Water!"); } } public void combinationAttack1(FireEffect fe, WaterEffect we){ //your algorithm here } public void methodForWaterEffect(WaterEffect fe){ fe.activateEffect(); }
I have a very simple suggestion. Why not make classes for each effect that implements an interface called Effect. Furthermore, if you are trying to simulate combination attacks, why not make functions for each of those combination attacks?
So in your example, it's going to be
public void combination2(PoisonEffect pe, PowerFailureEffect pf){ }
This might seems load of work, but I think this decouples your code very well and later you might find it easier to manage your code.
Please note that for the simplicity, they are not in separate class files =D.
回答4:
A common anti-pattern is using type checking, for example with instanceof
, instead of polymorphism. Here's a simple example.
public class Shape { public void draw() { if (this instanceof Square) { // Draw a square } else if (this instanceof Circle) { // Draw a circle } } } public class Square extends Shape { // ... } public class Circle extends Shape { // ... }
Note that in the draw()
method in class Shape
, we determine what to draw by looking at the type of the current object, and then executing the appropriate code. This has several disadvantages: class Shape
needs to know about all of its subclasses, and if you want to add a new shape, you'll have to modify class Shape
too. It's much better to use polymorphism, by overriding the draw()
method in the subclasses:
public abstract class Shape { public abstract void draw(); } public class Square extends Shape { public void draw() { // Draw a square } } public class Circle extends Shape { public void draw() { // Draw a circle } }
This way, class Shape
does not need to know about all of its subclasses, you don't need to modify Shape
to add a new shape and all logic that belongs to a particular shape is in the place where it belongs (the class for that shape).