我试图在OOP中模拟地理位置。区位类型如下:大陆、乡村、州、县或更具体(如城市、城镇、村庄,均为单一类型)。各洲有大陆代码,县有大陆代码和国家代码,各州有大陆代码、国家代码和adm1code,而更具体的县有大陆代码、国家代码、adm1code和adm2code。我需要所有的位置与方法"isContainedIn(Location )“实现一个接口。这将根据管理代码检查当前位置是否包含在loc中。因此,对于一些国家,这种方法首先将检查loc是否为欧洲大陆--如果不是,则返回false。如果是大陆,则将检查loc的大陆代码是否与国家的大陆代码相同。
我可以使用带有类型字段的单个类对位置进行建模,也可以使用继承。我更愿意用一个名为location的基类建模,它表示比adm2更具体的位置,所有管理位置类型(国家、代码、州和县)都会扩展(这在概念上是不正确的--我是说城镇是大陆的父类)。这样,我也可以简单地覆盖其他方法,例如等于运算符(例如,如果有相同的国家代码,两个国家是平等的)。但是,基类需要包含大陆代码、国家代码、adm1code和adm2code,这样我就可以在每种情况下实现isContainedIn(Location )方法。国家代码对大陆没有意义,adm1code对国家没有意义等等。我可以有空白字符串时,他们没有意义,但这会违反Liskov替代原则或任何其他标准设计原则?如果是的话,你能为这个问题提出其他的设计方案吗?
编辑:我希望客户机类获得两个位置实例,比如l1和l2,并且能够调用l1.isContainedIn(l2),而不知道该位置的特定类型。让我详细说明如何将这些类建模如下: 1.父类只包含对所有位置(名称、纬度和经度)有意义的变量。2.子类(大陆、国家、州、县和更具体的),它们都实现了isContainedIn(Location )接口。大陆有大陆代码,国家有大陆代码和国家代码等等。
有了这种结构,我就无法在任何子类中写出isContainedIn(Location )的逻辑。例如,一个位置包含在一个大陆中,如果它不是一个大陆,而它具有与大陆相同的大陆代码。但是Location类没有大陆代码。我希望这能澄清这个问题,非常感谢你对此进行调查!
EDIT2:这里是一些示例代码(而不是接口,而是一个抽象类):
abstract class Location {
protected String name;
protected double lat;
protected double lng;
@Override
public boolean equals(Object loc) {
if(!(loc instanceof Location)) {
return false;
}
Location l = (Location) loc;
return this.lat==l.lat && this.lng==l.lng;
}
abstract boolean isContainedIn(Location loc);
}
class Continent extends Location {
protected String continentCode;
@Override
public boolean equals(Object loc) {
if(!(loc instanceof Continent)) {
return false;
}
Location l = (Continent) loc;
return this.continentCode.equals(loc.continentCode);
}
boolean isContainedIn(Location loc) {
if(loc instance of Continent) {
return false;
}
//the following is the logic but won't work since location has no
continentCode variable
//return loc.continentCode.equals(continentCode);
}
}
class Country extends Location {
protected String continentCode;
protected String countryCode;
@Override
public boolean equals(Object loc) {
if(!(loc instanceof Country)) {
return false;
}
Location l = (Country) loc;
return this.continentCode.equals(loc.continentCode) && this.countryCode.equals(loc.countryCode);
}
boolean isContainedIn(Location loc) {
if(loc instance of Continent|| loc instance of Country) {
return false;
}
//the following is the logic but won't work since location has no
countryCode or continent code variable
//return loc.continentCode.equals(continentCode) && loc.countryCode.equals(countryCode);
}
}//其他类将类似
--这就是客户机类可能看起来像的样子
class ClientClass {
void someMethod {
Location l1 = someClass.getLocation(String...searchParameters);
Location l2 = someClass.getLocation(String...searchParameters);
if(l1.isContainedIn(l2)) {
//do something
}
}目标:建模位置类,使客户端代码能够在不知道特定位置类型的情况下使用isContainedIn方法。如果父类不了解大陆代码、国家代码等知识,可以这样做吗?
交替类设计
//for locations contained in counties
class Location {
String name;
double lat;
double lng;
String continentCode;
String countryCode;
String adm1code;
String adm2code;
}
class Continent extends Location {
String countryCode ="";
String adm1code = "";
String adm2code = "";
public Continent(String continentCode) {
this.continentCode = continentCode;
}
//all logic will work but does that violate design principles since Continent technically has no country code, adm1code or adm2code - blank strings returned for these cases?谢谢。
发布于 2018-04-19 10:00:34
你陷入了经典的OOP陷阱,仅仅因为两件事代表着相同的概念,在地球表面的一个位置,它们应该共享一个基类。
在问题的开头,您可以声明希望它们“都被建模为单一类型”。您选择的单个类型,单点Location没有意义,并导致您在代码中询问:location1.contains(location2)。一个点怎么能包含另一个点呢?此外,您也不会使用lat/long来决定一个位置是否包含另一个位置,因此对于这个问题来说,它是一个红色的鲱鱼。如果出于其他原因要添加位置,则它应该是项的属性,而不是基类。
所以,我知道这违背了你的整个前提,但我要提出另一个解决方案。
interface ContinentEntity {
Continent getContinent();
}
// Logically anything that is of a country is also of a continent
interface CountryEntity extends ContinentEntity {
Country getCountry();
// We can satisfy this here in Java 8
default Continent getContinent() {
return getCountry().getContinent();
}
}
public final class Continent {
private final String continentCode;
public Continent(String continentCode) {
this.continentCode = continentCode;
}
// As long as the entity reports the same continent, we are good
// I still don't know whether it's a City or country etc, so it
// ticks the box of being type agnostic at this point.
public boolean contains(ContinentEntity continentEntity) {
return this.equals(continentEntity.getContinent());
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Continent) obj).continentCode.equals(continentCode);
}
@Override
public int hashCode() {
return continentCode.hashCode();
}
}
public final class Country implements ContinentEntity {
// Could be the code, but consider this over stringly type
private final Continent continent;
private final String coutryCode;
Country(Continent continent, String coutryCode) {
this.continent = continent;
this.coutryCode = coutryCode;
}
@Override
public Continent getContinent() {
return continent;
}
public boolean contains(CountryEntity countryEntity) {
return this.equals(countryEntity.getCountry());
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Country) obj).coutryCode.equals(coutryCode);
}
@Override
public int hashCode() {
return coutryCode.hashCode();
}
}
public final class City implements CountryEntity {
private final Country country;
private final String name;
public City(Country country, String name) {
this.country = country;
this.name = name;
}
@Override
public Country getCountry() {
return country;
}
}因此,您可以定义一个新的类:
public final class Village implements CountryEntity {
private final Country country;
private final String name;
public Village(Country country, String name) {
this.country = country;
this.name = name;
}
@Override
public Country getCountry() {
return country;
}
}而且您可以在不修改任何现有类的情况下测试country.contains(village)或continient.contains(village)。这是我们设计正确的一个好迹象。见OCP。
那么这在用法上看上去如何呢:
Continent europe = new Continent("EUROPE");
Country uk = new Country(europe, "UK");
City london = new City(uk, "London");
// Sensible questions compile:
europe.contains(uk);
uk.contains(london);
europe.contains(london);
// Non-sense does not even compile - yay for typesafety for free:
uk.contains(europe);发布于 2018-04-19 19:32:17
这是我的最后一段代码,以防对别人有帮助。
public interface GeoEntity {
Optional<GeoEntity> getParent();
boolean contains(GeoEntity child);
}
public interface ContinentEntity extends GeoEntity {
Continent getContinent();
}
public interface CountryEntity extends ContinentEntity {
Country getCountry();
default Continent getContinent() {
return getCountry().getContinent();
}
}
public class Continent implements GeoEntity {
private final String continentCode;
public Continent(String continentCode) {
this.continentCode = continentCode;
}
public boolean contains(ContinentEntity continentEntity) {
return this.equals(continentEntity.getContinent());
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Continent) obj).continentCode.equals(continentCode);
}
@Override
public int hashCode() {
return continentCode.hashCode();
}
@Override
public Optional<GeoEntity> getParent() {
return Optional.empty();
}
@Override
public boolean contains(GeoEntity child) {
if(!ContinentEntity.class.isAssignableFrom(child.getClass())) {
return false;
}
ContinentEntity continentEntity = (ContinentEntity) child;
return contains(continentEntity);
}
}
public class Country implements ContinentEntity {
final Continent continent;
final String countryCode;
public Country(String continentCode, String countryCode) {
this.continent = new Continent(continentCode);
this.countryCode = countryCode;
}
@Override
public Continent getContinent() {
return continent;
}
public boolean contains(CountryEntity countryEntity) {
return this.equals(countryEntity.getCountry());
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Country) obj).countryCode.equals(countryCode);
}
@Override
public int hashCode() {
return countryCode.hashCode();
}
@Override
public Optional<GeoEntity> getParent() {
return Optional.of(continent);
}
@Override
public boolean contains(GeoEntity child) {
if(!CountryEntity.class.isAssignableFrom(child.getClass())) {
return false;
}
CountryEntity countryEntity = (CountryEntity) child;
return contains(countryEntity);
}
}使用:
public class Mock {
public static void main(String...args) {
GeoEntity geo = new Continent("EU");
GeoEntity geo2 = new Country("EU", "FR");
//returns true
System.out.println(geo.contains(geo2));
//returns false
System.out.println(geo2.contains(geo));
}
}https://stackoverflow.com/questions/49914802
复制相似问题