非常有趣的问题,但缺少一个重要的细节-
上下文
什么将创造城市,什么将进入城市?哪些城市、地区等将负责?它们只是数据实体吗?我必须先回答这些问题,然后才能帮助你。因此,让我们开始设计我们的域模型:
客户(顺其自然
main
方法)将通过
CountryBuilder
界面客户将通过
Registry
界面位置将是不可变的(客户端不允许直接修改位置数据)。允许客户端更改现有位置的唯一方法是通过
CountryBuilder公司
.所有位置
有
并且(按你的要求)知道(有名字)它是封闭的地方。
State
没有封闭的地方,但可以拥有
Districts
.
District
名称为
状态
并包含
Cities
,城市不能包含任何地方,但有其所有者的姓名
(ZipAddress)
当然,只使用一个抽象就可以达到同样的效果
Place
,但随后您需要使用一些检查来确定这个地方是什么,因为不是所有的地方都可以包含其他地方(例如城市),也不是所有地方都被其他地方包含(例如州),并且一些地方可以包含其他地方,也可以包含某些地方(地区)。为了避免检查,为了知道那个地方是市、区还是州,我使用了三种不同的抽象。您可以创建
状态
,而不创建
City
也没有
地区
,但您无法创建
城市
没有指定
状态
和
地区
。请阅读代码
小心
并阅读我在下面的评论:
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)
{
build("ImaginaryState" , "ImaginaryDistrict", "MadCity");
build("ImaginaryState" , "ImaginaryDistrict", "EastCity");
build("ImaginaryState" , "ImaginaryDistrict", "WestCity");
build("DamnGoodState" , "DamnGoodDistrict", "DamnGoodCity");
build("ImaginaryState" , "ProgrammersDistrict", "NoLifersCity");
build("DamnGoodState" , "DamnGoodDistrict", "DamnBadCity");
build("DamnGoodState" , "DamnBadDistrict", "DamnGoodCity");
try
{
traverseWorld();
} catch (NoSuchPlaceException ex)
{
Logger.getLogger(CountryClient.class.getName()).log(Level.SEVERE, null, ex);
}
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);
}
}
国家.java
数据实体接口(市、区、州)的持有者,访问器(注册表)和Mutator(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;
}
}
}
注册表.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);
}
}
}
注册实施.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设计的大图。事实上,这真的很棒,因为你有足够的热情去寻找整个星期的答案。我的建议是,如果你开始学习一个新概念,就找一本好书读一读。面向对象的设计和软件架构非常庞大。而且很漂亮。但是,如果你想看到它的辉煌,你需要掌握它。阅读书籍
图案上的Holub
,它一定会帮助你。
附笔
请随时询问有关代码的问题,如果发现错误,请通知我。祝你好运