[英]Avoding instanceof in Java
在我的應用程序中,我有一個2d實體數組來表示網格。 網格中的每個位置都可以為空或由實體占用(在這種情況下,它只是一個人或牆)。 現在我使用instanceof
來檢查一個實體是一個人還是一個牆。
我在考慮為每個實體提供一個方法,該方法返回一個enum
其類型的enum
,即一個wall實體將返回EntityType.WALL
。 我想知道,這是去除使用的最好的想法instanceof
或者是instanceof
適用於這種情況?
使用Tell,Do not Ask :而不是詢問對象是什么,然后對此作出反應,告訴對象該做什么,然后牆或人們確定他們如何做他們需要做的事情。
例如:
而不是像這樣的東西:
public class Wall {
// ...
}
public class Person {
// ...
}
// later
public class moveTo(Position pos) {
Object whatIsThere = pos.whatIsThere();
if (whatIsThere instanceof Wall) {
System.err.println("You cannot move into a wall");
}
else if (whatIsThere instanceof Person) {
System.err.println("You bump into " + person.getName());
}
// many more else branches...
}
做這樣的事情:
public interface DungeonFeature {
void moveInto();
}
public class Wall implements DungeonFeature {
@Override
public void moveInto() {
System.err.println("You bump into a wall");
}
// ...
}
public class Person implements DungeonFeature {
private String name;
@Override
public void moveInto() {
System.err.println("You bump into " + name);
}
// ...
}
// and later
public void moveTo(Position pos) {
DungeonFeature df = currentPosition();
df.moveTo(pos);
}
這有一些優點。
首先,每次添加新的地下城功能時,您都不需要調整巨型樹。
其次,地牢特征中的代碼是自包含的,邏輯全部在所述對象中。 您可以輕松測試並移動它。
以精確方式移除instanceof
的理論解決方案是訪問者模式的使用。 工作原理是需要知道另一個元素是牆或人的對象將自身作為參數調用該對象,並且該特定對象回調,從而提供有關其類型的信息。
例,
public class Person {
void magic() {
if(grid.getAdjacent() instanceof Person) {
Person otherPerson = (Person)grid.getAdjacent();
doSomethingWith(otherPerson);
} else if(grid.getAdjacent() instanceof Wall) {
Wall wall = (Wall)grid.getAdjacent();
doOtherThingWith(wall);
}
}
}
可以變成
public class Person extends Entity {
void magic() {
grid.getAdjacent().visit(this);
}
void onVisit(Wall wall) {
doOtherThingWith(wall);
}
void onVisit(Person person) {
doSomethingWith(person);
}
public void visit(Person person) {
person.onVisit(this);
}
}
public class Wall extends Entity {
public void visit(Person person) {
person.onVisit(this);
}
}
如果您按照其他答案並實現訪問者模式或使用枚舉,您將不會犯錯。
但是,它也可能有助於思考您想要對切換邏輯(無論是實例還是訪問者)做什么,因為有時候有一種更簡單的方法可以做到這一點。
例如,如果您要做的只是檢查實體是否以阻塞方式占用網格,那么您只需通過接口向每個實體添加一個方法boolean isSolid()
。 你可以使用默認方法來獲得額外的美感:
public interface GridPhysics {
default boolean isSolid() {
return true;
}
// other grid physics stuff
}
public class Wall implements GridPhysics {
// nothing to do here, it uses the default
}
// in your game logic
public boolean canMoveTo(GridPhysics gridCell) {
return !gridCell.isSolid() && otherChecks();
}
您可能還想查看實體組件系統(例如Artemis ),它基本上將“構圖優於繼承”的概念發揮到極致。
我會讓person和wall繼承自一個抽象的超類(例如Tile),它有一個方法getType()返回一個enum或int並在Wall和Person中實現這個方法返回相應的
答案是非常好的,這里沒什么可說的,但如果我在這種情況下,如果它被允許比我本來已經去了一個2d int數組可能值0(默認分配為空)和1,2為人或牆。
正如在另一個問題中所提到的,現代Java編譯器在諸如instanceof之類的操作中非常有效。 你可以使用它。
事實上,其中一個提供了測試instanceOf和字符串比較的答案,而instanceOf明顯更快。 我建議你堅持使用它。
ligi的回答是肯定的。 (無論是誰投票,我都不知道他們在想什么。)作為替代方案,請考慮以下事項:
abstract class Tile
{
public final EntityType type;
protected Tile( EntityType type )
{
this.type = type;
}
}
abstract class Pedestrian extends Tile
{
public Pedestrian()
{
super( EntityType.PEDESTRIAN );
}
}
abstract class Wall extends Tile
{
public Wall()
{
super( EntityType.WALL );
}
}
這背后的基本原理是實體的“類型”是實體的永久特征,因此適合在構造函數中指定並在final
成員字段中實現。 如果它是由虛方法(java用語中的非最終方法)返回的,那么后代可以在一個時間點自由返回一個值,而在另一個時間點返回另一個值,這會造成嚴重破壞。
哦,如果你真的無法忍受公眾最后的成員,請繼續為它添加一個吸氣劑,但我的建議是,沒關系純粹主義者,沒有吸氣劑的公共最終成員完全沒問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.