创建 Java 个子类的层次结构
Creating a hierarchy of Java subclasses
我正在尝试在 java 中设置位置层次结构。地点可大可小,较大的地点包含较小的子位置。因此,世界包含国家,国家包含城市,依此类推直至建筑物和房间。
我创建了一个名为 Place 的抽象 class,它是所有这些类型的 parent。每个地方都包含一个子位置图、一个 parent 位置以及获取 parent 子位置(也就是自己的邻居)
的能力
我遇到的主要问题是一个人的 parent 的子位置应该是一个人的 class 的实例,但是没有办法用 java 来指定它。
我考虑了 hard-coding 所有类型,因此城市的 getParent returns 一个国家和 getSublocations returns 一个建筑图,但这会在改变功能时产生一些问题classes 或添加新的 classes,并且涉及大量的代码复制。还有其他方法可以解决这个问题吗?
编辑:
谢谢大家的回答。这是我到目前为止所拥有的。它并不完美,但也不算太糟糕。我必须复制的唯一代码是 getNeighbors() 的实现。它曾经直接在抽象 class (this.getParent().getSublocations().getEdges(this) ) 中实现,但那只是一行代码。
import java.util.HashSet;
public abstract class Place {
//World, Region, City, Building, Room
public enum Direction {
East, Northeast, North, Northwest, West, Southwest, South, Southeast, Invalid;
public static Direction fromInt(int i) {
switch (i) {
case 0: return East;
case 1: return Northeast;
case 2: return North;
case 3: return Northwest;
case 4: return West;
case 5: return Southwest;
case 6: return South;
case 7: return Southeast;
default: return Invalid;
}
}
public static Direction fromAngle(double angle) {
return fromInt((int) (angle+22.5)/45);
}
}
protected final int locked = Integer.MAX_VALUE;
protected int x, y; //longitude and latitude
public abstract String getName();
public abstract <S extends Place> Graph<S> getSublocations();
public abstract <T extends Place> T getParent();
public abstract <U extends Place> HashSet<Passage<U>> getNeighbors();
public final Direction getDirection(Place otherPlace) {
if (otherPlace == null) {
return Direction.Invalid;
} else if (this.getClass() == otherPlace.getClass()) {
return Direction.fromAngle(Math.atan2(otherPlace.y-y, otherPlace.x-x));
} else {
return Direction.Invalid;
}
}
}
我遇到过几次这个问题,当你遇到这样的事情时,我不建议在这种情况下使用泛型,因为你已经知道每个 Place 将返回什么。希望这段代码能有所帮助。
public interface Place {
// Place can be an abstract class if you want.
// Making it abstract could cause some problems because it might make you use generics
// I recommend an interface
List<? extends Place> getSublocations();
Place getParent();
}
public class World implements Place {
private List<Country> countries;
@Override
public List<Country> getSublocations() {
return this.countries;
}
@Override
public Place getParent() {
return null;
}
}
public class Country implements Place {
private List<City> cities;
private World parent;
@Override
public List<City> getSublocations() {
return this.cities;
}
@Override
public World getParent() {
return this.parent;
}
}
public class City implements Place {
private Country parent;
@Override
public List<? extends Place> getSublocations() {
return null;
}
@Override
public Country getParent() {
return this.parent;
}
}
代码中不应该有太多的复制和粘贴,但我认为这是你最好的选择。
假设每个类型都直接从 Place
扩展(与 City extends Country
之类的东西相反,这是错误的),您应该能够使用泛型限制您的样板文件,例如:
public class Place<P extends Place<?, ?>, C extends Place<?, ?>> {
// ...
public final P getParent() { ... }
public final Set<C> getChildren() { ... }
}
public class City extends Place<Country, Building> { ... )
不过,在实践中,您可能会发现在这两种情况下简单地 return Place
更灵活,并且避免在类型系统中对层次结构的不同级别之间的关系进行硬编码。如果事情发生变化(例如,您在 Country
和 City
之间引入了 State
类型),您必须更正每个依赖于先前关系的调用站点。
看看复合模式,也许可以使用一些标记界面。
我正在尝试在 java 中设置位置层次结构。地点可大可小,较大的地点包含较小的子位置。因此,世界包含国家,国家包含城市,依此类推直至建筑物和房间。
我创建了一个名为 Place 的抽象 class,它是所有这些类型的 parent。每个地方都包含一个子位置图、一个 parent 位置以及获取 parent 子位置(也就是自己的邻居)
的能力我遇到的主要问题是一个人的 parent 的子位置应该是一个人的 class 的实例,但是没有办法用 java 来指定它。
我考虑了 hard-coding 所有类型,因此城市的 getParent returns 一个国家和 getSublocations returns 一个建筑图,但这会在改变功能时产生一些问题classes 或添加新的 classes,并且涉及大量的代码复制。还有其他方法可以解决这个问题吗?
编辑:
谢谢大家的回答。这是我到目前为止所拥有的。它并不完美,但也不算太糟糕。我必须复制的唯一代码是 getNeighbors() 的实现。它曾经直接在抽象 class (this.getParent().getSublocations().getEdges(this) ) 中实现,但那只是一行代码。
import java.util.HashSet;
public abstract class Place {
//World, Region, City, Building, Room
public enum Direction {
East, Northeast, North, Northwest, West, Southwest, South, Southeast, Invalid;
public static Direction fromInt(int i) {
switch (i) {
case 0: return East;
case 1: return Northeast;
case 2: return North;
case 3: return Northwest;
case 4: return West;
case 5: return Southwest;
case 6: return South;
case 7: return Southeast;
default: return Invalid;
}
}
public static Direction fromAngle(double angle) {
return fromInt((int) (angle+22.5)/45);
}
}
protected final int locked = Integer.MAX_VALUE;
protected int x, y; //longitude and latitude
public abstract String getName();
public abstract <S extends Place> Graph<S> getSublocations();
public abstract <T extends Place> T getParent();
public abstract <U extends Place> HashSet<Passage<U>> getNeighbors();
public final Direction getDirection(Place otherPlace) {
if (otherPlace == null) {
return Direction.Invalid;
} else if (this.getClass() == otherPlace.getClass()) {
return Direction.fromAngle(Math.atan2(otherPlace.y-y, otherPlace.x-x));
} else {
return Direction.Invalid;
}
}
}
我遇到过几次这个问题,当你遇到这样的事情时,我不建议在这种情况下使用泛型,因为你已经知道每个 Place 将返回什么。希望这段代码能有所帮助。
public interface Place {
// Place can be an abstract class if you want.
// Making it abstract could cause some problems because it might make you use generics
// I recommend an interface
List<? extends Place> getSublocations();
Place getParent();
}
public class World implements Place {
private List<Country> countries;
@Override
public List<Country> getSublocations() {
return this.countries;
}
@Override
public Place getParent() {
return null;
}
}
public class Country implements Place {
private List<City> cities;
private World parent;
@Override
public List<City> getSublocations() {
return this.cities;
}
@Override
public World getParent() {
return this.parent;
}
}
public class City implements Place {
private Country parent;
@Override
public List<? extends Place> getSublocations() {
return null;
}
@Override
public Country getParent() {
return this.parent;
}
}
代码中不应该有太多的复制和粘贴,但我认为这是你最好的选择。
假设每个类型都直接从 Place
扩展(与 City extends Country
之类的东西相反,这是错误的),您应该能够使用泛型限制您的样板文件,例如:
public class Place<P extends Place<?, ?>, C extends Place<?, ?>> {
// ...
public final P getParent() { ... }
public final Set<C> getChildren() { ... }
}
public class City extends Place<Country, Building> { ... )
不过,在实践中,您可能会发现在这两种情况下简单地 return Place
更灵活,并且避免在类型系统中对层次结构的不同级别之间的关系进行硬编码。如果事情发生变化(例如,您在 Country
和 City
之间引入了 State
类型),您必须更正每个依赖于先前关系的调用站点。
看看复合模式,也许可以使用一些标记界面。