一、场景问题
设计出省、市、区机构的区域程序代码。省市区结构如下
安徽省
--合肥市
----肥东县
--芜湖市
----三山区
浙江省
--杭州市
----滨江区
--温州市
----永嘉县
江苏省
--镇江市
----京口区
二、解决方案
1、传统解决方案
1.1、实现思路
定义省市区/县三个类,省包含市,市包含区/县。
-
代码展示
-
区/县
public class District { /** * 名称 */ private String name; public District(String name) { this.name = name; } /** * 区信息 * @author xianzilei **/ public void print() { System.out.println("----"+name); } }
-
市
public class City { /** * 名称 */ private String name; /** * 包含的区县信息 */ private List<District> districtList = new ArrayList<>(); public City(String name) { this.name = name; } /** * 添加区/县 * * @author xianzilei **/ public void add(District district) { districtList.add(district); } /** * 市信息 * @author xianzilei **/ public void print() { System.out.println("--" + name); for (District district : districtList) { district.print(); } } }
-
省
public class Province { /** * 名称 */ private String name; /** * 包含的市信息 */ private List<City> cityList = new ArrayList<>(); public Province(String name) { this.name = name; } /** * 添加市 * * @author xianzilei **/ public void add(City city) { cityList.add(city); } /** * 省信息 * * @author xianzilei **/ public void print() { System.out.println(name); for (City city : cityList) { city.print(); } } }
-
-
客户端测试
public static void main(String[] args) { //区/县 District district1 = new District("肥东县"); District district2 = new District("瑶海区"); District district3 = new District("三山区"); //市 City city1 = new City("合肥市"); city1.add(district1); city1.add(district2); City city2 = new City("芜湖市"); city2.add(district3); //省 Province province = new Province("安徽省"); province.add(city1); province.add(city2); //打印信息 province.print(); }
输出
安徽省 --合肥市 ----肥东县 ----瑶海区 --芜湖市 ----三山区
1.2、传统方式实现的优缺点
- 优点:实现简单,代码易读。
- 缺点:缺点很明显,我们必须区分不同层级的省市区,可能这个例子不太形象,我们用分类来举例。每个分类都可能会有子分类,即可能为叶子节点,也可能为非叶子节点。但是这些节点其实是同样的,没有必要区别开来,这样可能会不利于扩展,操作起来也可能不够方便。
2、代理模式方案
明白了上面写法的弊端,我们将这省市区看成一种类型,使用抽象类或接口聚合公共部分。
2.1、代码实现
-
定义抽象区域类
public abstract class RegionComposite { /** * 区域名称 */ private String name; protected RegionComposite(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 新增子区域信息 * @author xianzilei **/ protected void add(RegionComposite regionComposite) { throw new UnsupportedOperationException(); } /** * 移除子区域信息 * @author xianzilei **/ protected void remove(RegionComposite regionComposite) { throw new UnsupportedOperationException(); } /** * 打印区域信息 * * @author xianzilei **/ public abstract void print(); }
-
省市区代码修改
-
省
public class Province extends RegionComposite { /** * 子区域信息 */ private List<RegionComposite> childList = new ArrayList<RegionComposite>(); public Province(String name) { super(name); } @Override protected void add(RegionComposite regionComposite) { childList.add(regionComposite); } @Override protected void remove(RegionComposite regionComposite) { childList.remove(regionComposite); } @Override public void print() { System.out.println(getName()); for (RegionComposite regionComposite : childList) { regionComposite.print(); } } }
-
市
public class City extends RegionComposite { /** * 子区域信息 */ private List<RegionComposite> childList = new ArrayList<RegionComposite>(); public City(String name) { super(name); } @Override protected void add(RegionComposite regionComposite) { childList.add(regionComposite); } @Override protected void remove(RegionComposite regionComposite) { childList.remove(regionComposite); } @Override public void print() { System.out.println("--" + getName()); for (RegionComposite regionComposite : childList) { regionComposite.print(); } } }
-
区/县
public class District extends RegionComposite { public District(String name) { super(name); } @Override public void print() { System.out.println("----" + getName()); } }
-
-
客户端测试
public static void main(String[] args) { //区/县 RegionComposite district1 = new District("肥东县"); RegionComposite district2 = new District("瑶海区"); RegionComposite district3 = new District("三山区"); //市 RegionComposite city1 = new City("合肥市"); city1.add(district1); city1.add(district2); City city2 = new City("芜湖市"); city2.add(district3); //省 RegionComposite province = new Province("安徽省"); province.add(city1); province.add(city2); //打印信息 province.print(); }
输出
安徽省 --合肥市 ----肥东县 ----瑶海区 --芜湖市 ----三山区
2.2、方案解析
组合模式将组合对象和叶子对象统一起来了,使得用户在操作的时候无需区分类型,可以将操作统一化。
三、模式剖析
1、模式定义
组合模式(Composite Pattern):将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
2、模式结构
组合模式一般包含如下角色
-
Component
:抽象构件角色为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
abstract class Component { //增加成员 public abstract void add(Component c); //删除成员 public abstract void remove(Component c); //获取成员 public abstract Component getChild(int i); //业务方法 public abstract void operation(); }
-
Leaf
:树叶构件角色组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
class Leaf extends Component { public void add(Component c) { //抛出异常或错误提示 } public void remove(Component c) { //抛出异常或错误提示 } public Component getChild(int i) { //抛出异常或错误提示 } public void operation() { //叶子构件具体业务方法的实现 } }
-
Composite
:树枝构件角色组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含
add
()、remove
()、getChild
() 等方法。class Composite extends Component { //子构建集合 private ArrayList<Component> list = new ArrayList<Component>(); public void add(Component c) { list.add(c); } public void remove(Component c) { list.remove(c); } public Component getChild(int i) { return (Component)list.get(i); } public void operation() { //容器构件具体业务方法的实现 //递归调用成员构件的业务方法 for(Component component:list) { component.operation(); } } }
3、模式结构图
4、优缺点
- 优点
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码
- 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”
- 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单
- 缺点
- 增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
5、使用场景
- 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型