首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对父类使用继承,父类包含仅适用于子类的变量的空字符串。

对父类使用继承,父类包含仅适用于子类的变量的空字符串。
EN

Stack Overflow用户
提问于 2018-04-19 07:08:18
回答 2查看 91关注 0票数 2

我试图在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:这里是一些示例代码(而不是接口,而是一个抽象类):

代码语言:javascript
复制
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);
  }
}

//其他类将类似

--这就是客户机类可能看起来像的样子

代码语言:javascript
复制
class ClientClass {
  void someMethod {
   Location l1 = someClass.getLocation(String...searchParameters);
  Location l2 = someClass.getLocation(String...searchParameters);
  if(l1.isContainedIn(l2)) {
    //do something
  }
}

目标:建模位置类,使客户端代码能够在不知道特定位置类型的情况下使用isContainedIn方法。如果父类不了解大陆代码、国家代码等知识,可以这样做吗?

交替类设计

代码语言:javascript
复制
//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?

谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-04-19 10:00:34

你陷入了经典的OOP陷阱,仅仅因为两件事代表着相同的概念,在地球表面的一个位置,它们应该共享一个基类。

在问题的开头,您可以声明希望它们“都被建模为单一类型”。您选择的单个类型,单点Location没有意义,并导致您在代码中询问:location1.contains(location2)。一个点怎么能包含另一个点呢?此外,您也不会使用lat/long来决定一个位置是否包含另一个位置,因此对于这个问题来说,它是一个红色的鲱鱼。如果出于其他原因要添加位置,则它应该是项的属性,而不是基类。

所以,我知道这违背了你的整个前提,但我要提出另一个解决方案。

代码语言:javascript
复制
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;
    }
}

因此,您可以定义一个新的类:

代码语言:javascript
复制
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

那么这在用法上看上去如何呢:

代码语言:javascript
复制
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);
票数 2
EN

Stack Overflow用户

发布于 2018-04-19 19:32:17

这是我的最后一段代码,以防对别人有帮助。

代码语言:javascript
复制
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);
    }

}

使用:

代码语言:javascript
复制
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));
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49914802

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档