继承和构成混乱?

时间:2016-03-06 10:13:50

标签: java class oop inheritance composition

我有一个抽象类Area,其子类为ProvinceDivisionDistrictCity

现在,我需要在City类中指定districtcity存在。所以,我会在District类(Composition)中有一个City类的实例,这样我就可以将特定id的{​​{1}}传递给district并将存储在数据库城市表中。但是,它不遵循组成规则。作为区有城市而不是其他方式。

另一个问题是这两个类都是使用继承和组合,我觉得这是不对的。

我一直试图通过谷歌搜索和其他东西自己解决这个问题。但是,我无法解决这个问题。这是我猜的最后一个希望。 我该如何解决这个问题?任何一个例子?

2 个答案:

答案 0 :(得分:1)

我只看到这里的抽象。您有一个Area,可以是ProvinceDivisionDistrictCity,但就继承而言这就是关系。当然,这确实提出了一个问题,“除了一个名称足以创建继承层次结构而不是接口之外,省,区,区或城市之间是否有足够的共同点?”< / p>

请记住,继承(以及范围,接口)始终定义为 is-a 关系。也就是说,正如您的系统现在一样,District AreaCity Area。这两种听起来都不错,而且这种表现形式并没有什么问题。实际上,您可以将其移动到界面,这可能会很好。

关系可能是双向的,但主要的前提是:

  • City 有一个 District
  • City 有一个 Division
  • City 有一个 Province

...以上所有三个也有相同的对应City

使用这两种技术都很好,但你必须明确:

  • 为什么您正在使用它们,
  • 时最适合使用它们。

在这种情况下,它确实感觉继承具有最少的意义,因为getName()方法可以轻松通过四者之间的公共接口来描述,并且没有继承需要被引入。

答案 1 :(得分:0)

非常有趣的问题,但缺少一个重要的细节 - 上下文。什么会创造城市,什么将进入城市?哪些城市,地区等将负责?它们只是数据实体吗?在我能帮助你之前,我必须回答这些问题。因此,让我们开始设计我们的域模型:

客户端(让它为main方法)将通过CountryBuilder界面创建地点。客户端将通过Registry界面访问它们。位置将是不可变的(客户端不允许直接修改位置数据)。客户端允许客户通过CountryBuilder添加新地点的唯一突变。所有地方都有和(根据您的要求)知道(有名称)它的封闭地点。 State没有封闭的地方,但可以拥有DistrictsDistrict的名称为State且包含Cities,城市不能包含任何地点,但其名称包含(ZipAddress)。当然,只使用一个抽象Place就可以达到同样的效果,但是你需要使用一些检查来确定这个地方是什么,因为不是所有地方都可以包含其他地方(例如城市),并非所有地方都是由其他人(例如国家)包含,有些地方可以包含其他地方,也包含在某些地方(地区)。为了避免检查,为了知道该地方是城市,区域还是州,我需要使用三种不同的抽象。您可以创建State,而无需同时创建CityDistrict,但无法在未指定CityState的情况下创建District 。请仔细阅读代码 并阅读以下评论:

CountryClient.java 这是一个客户端类。只能访问Country类的两种工厂方法。

package com.ooDesign;

import com.ooDesign.Country.Country;
import com.ooDesign.Country.Country.City;
import com.ooDesign.Country.Country.District;
import com.ooDesign.Country.Country.State;
import com.ooDesign.Country.Registry.NoSuchPlaceException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CountryClient 
{
    public static void main(String[] args)
    {
        /*Creating various combinations of places.*/      
        build("ImaginaryState" , "ImaginaryDistrict", "MadCity");
        build("ImaginaryState" , "ImaginaryDistrict", "EastCity");
        build("ImaginaryState" , "ImaginaryDistrict", "WestCity");
        build("DamnGoodState" , "DamnGoodDistrict", "DamnGoodCity");
        build("ImaginaryState" , "ProgrammersDistrict", "NoLifersCity");
        build("DamnGoodState" , "DamnGoodDistrict", "DamnBadCity");
        /*"DamnGoodCity" in "DamnBadDistrict" is not the same as "DamnGoodCity" in "DamnGoodDistrict"
           since they are located in different districts. You can easily find out how to change implementation
           to not allow to build multiple cities with same name even if they are in different districts.*/
        build("DamnGoodState" , "DamnBadDistrict", "DamnGoodCity");

        /*Printing what we just created*/
        try
        {
            traverseWorld();
        } catch (NoSuchPlaceException ex)
        {
            Logger.getLogger(CountryClient.class.getName()).log(Level.SEVERE, null, ex);
        }

        /*Getting state of speciffic city (thanks to ZipCode interface)*/
        try
        {
            print(Country.registry().state("DamnGoodState").district("DamnBadDistrict").city("DamnGoodCity").zipCode().state());
        } catch (NoSuchPlaceException ex)
        {
            Logger.getLogger(CountryClient.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    static void print(String string)
    {
        System.out.println(string);
    }

    static void traverseWorld() throws NoSuchPlaceException
    {
        for(State s : Country.registry())
        {
            print("Districts in state \"" + s.name() + "\" :");
            for(District d : s)
            {
                print("   Cities in district \"" + d.name() + "\" :");
                for(City c : d)
                {
                    print("      " + c.name());
                }
            }
            print("---------------------------------------------");
        }
    }

    static void build(String state, String district, String city)
    {
        Country.builder().build().state(state).district(district).city(city);
    }

    static void build(String state, String district)
    {
        Country.builder().build().state(state).district(district);
    }

    static void build(String state)
    {
        Country.builder().build().state(state);
    }
}

Country.java 数据实体接口(City,District,State)和Accessor(Registry)和Muttator(CountryBuilder)抽象的静态工厂的持有者。

package com.ooDesign.Country;

import java.util.HashMap;
import com.ooDesign.Country.Registry.NoSuchPlaceException;

public final class Country
{
    private static HashMap<String, State> states = new HashMap<>();

    public static CountryBuilder builder()
    {
        return new CountryBuilderImpl(states);
    }

    public static Registry registry()
    {
        return new RegistryImpl(states);
    }

    public interface Place
    {
        String name();
    }

    public interface State extends Place, Iterable<District>
    {
        public District district(String districtName) throws NoSuchPlaceException;
    }

    public interface District extends Place, Iterable<City>
    {
        public City city(String cityName) throws NoSuchPlaceException;
        public String inState();
    }

    public interface City extends Place
    {
        public ZipCode zipCode();
    }

    public interface ZipCode
    {
        String state();
        String district();
        String city();
    }
}

CountryBuilder.java 我喜欢这种组合对象的构建方式,因为它具有可读性。然后你可以像这样实例化对象  Builder.build().firstObject(irstparams).secondObject(secondParams)...etc

package com.ooDesign.Country;

public interface CountryBuilder
{
    public StateBuilder build();

    public interface StateBuilder
    {
       public DistrictBuilder state(String stateName);
    }

    public interface DistrictBuilder
    {
        public CityBuilder district(String districtName);
    }

    public interface CityBuilder
    {
        public void city(String cityName);
    }

}

CountryBuilderImpl.java CountryBuilder抽象的实现。

package com.ooDesign.Country;

import com.ooDesign.Country.Country.State;
import static com.ooDesign.Country.Country.*;
import com.ooDesign.Country.Registry.NoSuchPlaceException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

class CountryBuilderImpl implements CountryBuilder
{
    private Map<String, State> states;

    public CountryBuilderImpl(Map<String, State> states)
    {
        this.states = states;
    }

    @Override
    public StateBuilder build()
    {
        return new StateBuilder()
        {
            @Override
            public DistrictBuilder state(String stateName)
            {

                StateImpl currentState;
                if (states.containsKey(stateName))
                {
                    currentState = (StateImpl)states.get(stateName);
                } else
                {
                    currentState = new StateImpl(stateName);
                    states.put(stateName, currentState);
                }

                return new DistrictBuilder()
                {
                    @Override
                    public CityBuilder district(String districtName)
                    {
                        DistrictImpl currentDistrict = currentState.addDistrict(districtName);

                        return new CityBuilder()
                        {
                            @Override
                            public void city(String cityName)
                            {
                                currentDistrict.addCity(cityName);
                            }
                        };
                    }
                };
            }
        };
    }

    private static class StateImpl implements State
    {

        private final Map<String, District> districts;
        private final String stateName;

        StateImpl(String stateName)
        {
            this.districts = new HashMap<>();
            this.stateName = stateName;
        }

        DistrictImpl addDistrict(String districtName)
        {
            if (!districts.containsKey(districtName))
            {
                districts.put(districtName, new DistrictImpl(stateName, districtName));
            }
            return (DistrictImpl)districts.get(districtName);
        }

        @Override
        public District district(String districtName) throws Registry.NoSuchPlaceException
        {
            if (!districts.containsKey(districtName))
            {
                throw new Registry.NoSuchPlaceException("District \"" + districtName + "\" in state of " + stateName + " does not exists");
            } else
            {
                return districts.get(districtName);
            }
        }

        @Override
        public String name()
        {
            return stateName;
        }

        @Override
        public Iterator<Country.District> iterator()
        {
            return districts.values().iterator();
        }

    }

    private static class DistrictImpl implements District
    {

        private final Map<String, Country.City> cities;
        private final String stateName, districtName;

        DistrictImpl(String stateName, String districtName)
        {
            this.cities = new HashMap<>();
            this.stateName = stateName;
            this.districtName = districtName;
        }

        void addCity(String cityName)
        {
            if (!cities.containsKey(cityName))
            {
                cities.put(cityName, new CityImpl(new ZipImpl(stateName, districtName, cityName)));
            }
        }

        @Override
        public City city(String cityName) throws NoSuchPlaceException
        {
            if (!cities.containsKey(cityName))
            {
                throw new Registry.NoSuchPlaceException("City \"" + cityName + "\" in state of " + stateName + " in district of " + districtName + " does not exists");
            } else
            {
                return cities.get(cityName);
            }
        }

        CityImpl getCity(String cityName)
        {
            return (CityImpl)cities.get(cityName);
        }

        @Override
        public String inState()
        {
            return stateName;
        }

        @Override
        public String name()
        {
            return districtName;
        }


        @Override
        public Iterator<Country.City> iterator()
        {
            return cities.values().iterator();
        }

    }

    private static class CityImpl implements City
    {

        private final Country.ZipCode zipCode;

        public CityImpl(Country.ZipCode zipCode)
        {
            this.zipCode = zipCode;
        }

        @Override
        public Country.ZipCode zipCode()
        {
            return zipCode;
        }

        @Override
        public String name()
        {
            return zipCode.city();
        }

    }

    private static class ZipImpl implements ZipCode
    {

        private final String state, district, city;

        public ZipImpl(String state, String district, String city)
        {
            this.state = state;
            this.district = district;
            this.city = city;
        }

        @Override
        public String state()
        {
            return state;
        }

        @Override
        public String district()
        {
            return district;
        }

        @Override
        public String city()
        {
            return city;
        }

        public String toString()
        {
            return "ZIP_CODE: STATE - " + state + "; DISTRICT - " + district + "; CITY - " + city;
        }
    }
}

Registry.java 用于访问地点。

package com.ooDesign.Country;

import com.ooDesign.Country.Country.State;
import java.util.Set;

public interface Registry extends Iterable<State>
{
    Set<String> listStates();
    State state(String stateName) throws NoSuchPlaceException;

    public static class NoSuchPlaceException extends Exception
    {
        public NoSuchPlaceException(String message)
        {
            super(message);
        }  
    }
}

RegistryImpl.java 名称说明了它的目的。

package com.ooDesign.Country;

import com.ooDesign.Country.Country.State;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

class RegistryImpl implements Registry
{
    private final Map<String, State> states;

    public RegistryImpl(Map<String, State> states)
    {
        this.states = states;
    }

    @Override
    public Set<String> listStates()
    {
        return states.keySet();
    }

    @Override
    public State state(String stateName) throws NoSuchPlaceException
    {
        if(!states.containsKey(stateName)) 
            throw new NoSuchPlaceException("State \"" + stateName + "does not exists");
        return states.get(stateName);
    }

    @Override
    public Iterator<State> iterator()
    {
        return states.values().iterator();
    }

}

如您所见,实现与客户端隔离,因为它们位于不同的包中,而实现类不是公共的。客户端只能通过接口与它们进行交互。接口有许多用途和优点。它们是OO设计的核心。我将离开,找出如何让所有特定州的城市,特定州的所有地区,特定地区的所有城市等。这很容易。如果需要,您可以实现许多便捷方法,各种管理类(如果您正在编写高质量,可维护的软件,则必须)。所有这些代码只是为了向您展示OO设计的全貌。真的很棒,你是一个充满热情的人,整个星期都在寻找答案。我的建议是找一本好书,如果你开始学习新概念,请阅读它。面向对象设计和软件架构是巨大的。而且很美。但是如果你想要看到那种美丽的荣耀,你需要掌握它。阅读书籍 Holub on patterns ,它一定会对你有所帮助。 P.S 随意提出有关代码的问题,如果您发现错误,请通知我。祝你好运!