用枚举类型设计省,市,区管理

@ 枚举类型可以将一组具有名称的值的优先级和创建为一种新的类型。我们可以像创建类一样创建枚举类型。比如

1
2
3
4
5
6
7

public enum Food {

APPLE, ORANGE, CHOCOLATE, GRAPE

}

@ 枚举类型有几个常用的方法。

  • values(),返回该枚举类型所有的常量数组。

  • ordinal(),返回某个常量所在枚举类型的位置,从0开始。Food.ORANGE.ordinal()的值是1

  • name(),返回当前常量的名字。

  • toString(),同name()

  • equals(),比较两个枚举类型的值是否相同。由于枚举类型是单例并且是常量,所以枚举类型的比较可以直接用“==”。

  • static valueOf(Class enumType, String name),根据枚举类型和名称找到该枚举的值。

  • compareTo(),比较两个枚举值的顺序,通过ordinal()的值做差得出结果。

  • valueOf(String name),这个方法不是静态方法,所有需要一个枚举实例来调用。

@ 枚举还有静态引用的功能,由于省略了类型名,对后期维护容易造成困惑,不推荐使用。

@ 枚举类型是常量,不可以被继承,也不能继承其他类,但是可以让枚举类型实现接口。

@ 枚举类型可以添加描述,即每个常量可以赋值,可以是基本类型,可以是类。

============

前段时间想实现国内各个省,市,区的管理功能,这在购物网站填写地址时会用到。我想了几种实现方法,比如:

方法一:省,市,区均设定为model,在数据库里分别创建表,写实际的中文名称,比如“北京”,“大连”等等。这种表示方式好处是简洁明了,缺点是每个地址都需要存储汉字,导致数据库内容重复。

方法二:省,市,区均设定model,在数据库里创建表,用键值对的形式表示地点。比如省份使用两位int的值表示,“10” - 北京,“12” - “辽宁”;四位表示市,“1212” - (辽宁)大连,前两位表示省份,后两位是市;六位表示市区,“121210” - (辽宁大连)甘井子。这种表示方式的好处是可以只通过市区就定位该市区具体行政关系。缺点是省和市的内容重复。

方法三:省,市,区均用int表示,显示的写在java代码里,并用map关联。这种方式的好处是不占用数据库资源,在数据库里只需要给出三个列表示省市区即可。缺点是int值很容易重复,表明意义不清晰,如果使用“101112”的形式表示则增加了数据位数,也显得冗余。

方法四:使用枚举表示省,市,区。这种方式好处是不占用数据库,常量的值不会被更改,表达意义清晰。缺点是设计和实现稍微复杂。

我尝试的用枚举构思并实现了一下,仅供参考。1.0版本的代码可以参考https://github.com/bejondshao/personal/commit/ed3fe52f99e6bf0f7156befd9443eda12f275714

  1. 从区入手,设计District接口,这个接口里显示的声明多个以城市为组,将区分组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

package com.bejond.enumregion;

/**
* Created by bejond on 4/9/16.
* From https://zh.wikipedia.org/wiki/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD%E5%8E%BF%E7%BA%A7%E4%BB%A5%E4%B8%8A%E8%A1%8C%E6%94%BF%E5%8C%BA%E5%88%97%E8%A1%A8
*/

public interface District {

// 注意, 只有常量用汉字, 类型仍然用英文

enum Beijing implements District {
东城区, 西城区, 朝阳区, 丰台区, 石景山区, 海淀区, 门头沟区, 房山区, 通州区,
顺义区, 昌平区, 大兴区, 怀柔区, 平谷区, 密云区, 延庆区
}

enum Tianjin implements District {
河西区, 河东区, 和平区, 南开区, 河北区, 红桥区, 东丽区, 西青区, 津南区,
北辰区, 武清区, 宝坻区, 宁河区, 静海区, 滨海新区, 蓟县
};

enum Shanghai implements District {
黄浦区, 徐汇区, 长宁区, 静安区, 普陀区, 虹口区, 杨浦区, 闵行区, 宝山区,
嘉定区, 浦东新区, 金山区, 松江区, 青浦区, 奉贤区崇明县
}

enum Chongqing implements District {
渝中区, 大渡口区, 江北区, 南岸区, 北碚区, 渝北区, 巴南区, 长寿区, 沙坪坝区,
万州区, 涪陵区, 黔江区, 永川区, 合川区, 江津区, 九龙坡区, 南川区, 綦江区,
大足区, 璧山区, 铜梁区, 荣昌区, 潼南区, 梁平县, 开县, 忠县, 城口县, 垫江县,
武隆县, 丰都县, 奉节县, 云阳县, 巫溪县, 巫山县, 石柱土家族自治县,
秀山土家族苗族自治县, 酉阳土家族苗族自治县, 彭水苗族土家族自治县

}

enum Shenyang implements District {
沈河区, 和平区, 大东区, 皇姑区, 铁西区, 苏家屯区, 浑南区, 沈北新区, 于洪区,
辽中区, 新民市, 康平县, 法库县
};

enum Dalian implements District {
西岗区, 中山区, 沙河口区, 甘井子区, 旅顺口区, 金州区, 普兰店区, 瓦房店市,
庄河市, 长海县
};

enum Shijiazhuang implements District {
长安区, 桥西区, 新华区, 裕华区, 井陉矿区, 藁城区, 鹿泉区, 栾城区, 晋州市,
新乐市, 井陉县, 正定县, 行唐县, 灵寿县, 高邑县, 深泽县, 赞皇县, 无极县,
平山县, 元氏县, 赵县
}

enum Xiamen implements District {
思明区, 海沧区, 湖里区, 集美区, 同安区, 翔安区
}

// 剩下的自己补吧, 特殊地区根据业务自行补充
}

由于枚举可以实现接口,所以声明的"Beijing", "Dalian"都是接口,虽然是市名,但这个类型其实是表达这个市所包含的市区,所以类型是“区”

  1. 设计市。以辽宁省的市为例,将辽宁省所包含的市设为常量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

package com.bejond.enumregion.city;

import com.bejond.enumregion.City;
import com.bejond.enumregion.District;

/**
* Created by bejond on 4/9/16.
*/
public enum LiaoningCity implements City {
沈阳市(District.Shenyang.class), 大连市(District.Dalian.class);

private District[] districts;

LiaoningCity(Class<? extends District> district) {
districts = district.getEnumConstants();
}

public District[] getDistricts() {
return districts;
}
}

本来想声明市也是个接口,这样可以和区一样设计并管理,但是由于市和区要进行关联,关联的过程需要有构造函数,而接口不允许有构造函数。并且我们需要在市的层级上添加方法,获取该市的区,所以无法通过继承接口实现。

构造函数

1
LiaoningCity(Class<? extends District> district)

有两个功能,一是将特定的市的区赋值给不同的市,二是初始化districts,这个变量可以返回某个市的所有区,而不是辽宁省的所有市的所有区。

  1. 设计省,设计方式和市一样,这个类型就是辽宁省,所以只有一个常量,将LiaoningCity赋给它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

package com.bejond.enumregion.province;

import com.bejond.enumregion.Province;
import com.bejond.enumregion.city.LiaoningCity;

/**
* Created by bejond on 4/9/16.
*/
public enum LiaoningProvince implements Province {
辽宁省(LiaoningCity.class);

private LiaoningCity[] liaoningCities;

LiaoningProvince(Class<? extends LiaoningCity> liaoningCity) {
liaoningCities = liaoningCity.getEnumConstants();
}

public LiaoningCity[] getLiaoningCities() {
return liaoningCities;
}
}

以上就是设计区,市,省的过程,测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

package com.bejond.enumregion.test;

import com.bejond.enumregion.District;
import com.bejond.enumregion.city.LiaoningCity;
import com.bejond.enumregion.province.LiaoningProvince;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

/**
* Created by bejond on 4/9/16.
*/
public class TestProvince {
@Test
public void testGetDistrictsByCityName() {
/*Province province = Liaoning.大连市;
for (Province city : province.getClass().getEnumConstants())
Province district = city.getClass().getEnumConstants();*/
String city = "dalian";
city = city.toLowerCase();
//city = (char)(city.charAt(0) - 32) + city.substring(1);
System.out.println(city);

/**
* this cityMap should be done by other class or method.
*/
Map<String, String> cityMap = new HashMap<String, String>();
cityMap.put("dalian", "大连市");
cityMap.put("shenyang", "沈阳市");

String cityName = cityMap.get(city);

LiaoningCity liaoningCity = LiaoningCity.valueOf(cityName);

for (District district : liaoningCity.getDistricts()) {
System.out.println(district);
}
}

@Test
public void getDistrictsByProvinceName() {
String name = "liaoning";
name = name.toLowerCase();

/**
* this provinceMap should be done by other class or method.
*/
Map<String, String> provinceMap = new HashMap<String, String>();
provinceMap.put("liaoning", "辽宁省");
provinceMap.put("hebei", "河北省");

String provinceName = provinceMap.get(name);

String enumName = (char)(name.charAt(0) - 32) + name.substring(1) + "Province";

/**
* this is stupid
*/
if (enumName.equals(LiaoningProvince.class.getSimpleName())) {
for (LiaoningProvince liaoningProvince : LiaoningProvince.values()) {
System.out.println(liaoningProvince.name());
for (LiaoningCity liaoningCity0 : liaoningProvince.getLiaoningCities()) {
System.out.println(liaoningCity0.name() + "--");
}

for (LiaoningCity liaoningCity : LiaoningCity.values()) {
System.out.println(liaoningCity.name());

for (District district : liaoningCity.getDistricts()) {
System.out.println(district);
}
System.out.println();
}
}
}

/*Province province = Province.valueOf(provinceName);

for (Enum city : province.getClass().getEnumConstants()) { // get cities
System.out.println(city.name());

for (District district : ((LiaoningCity) city).getDistricts()) {
System.out.println(district.toString());
}
break;

}*/
}
}

getDistrictsByCityName() 的运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

dalian

西岗区

中山区

沙河口区

甘井子区

旅顺口区

金州区

普兰店区

瓦房店市

庄河市

长海县

getDistrictsByProvinceName() 的运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

辽宁省

沈阳市--

大连市--

沈阳市

沈河区

和平区

大东区

皇姑区

铁西区

苏家屯区

浑南区

沈北新区

于洪区

辽中区

新民市

康平县

法库县



大连市

西岗区

中山区

沙河口区

甘井子区

旅顺口区

金州区

普兰店区

瓦房店市

庄河市

长海县

所以我们可以根据“省份”下拉框来动态生成“市”的下拉框,接着“市区”下拉框。

Share