1. 简介
组合模式,同样引用百度百科的定义:
将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
从以上的解释,我们可以看出,为了保证整体和部分对象的使用一致性。那么,整体和部分就需要有相同的接口方法,那么怎么才能体现整体和部分的差别呢,那么就需要在整体中有对部分的引用,同时拥有对部分的增删查改的方法。
所以,组合模式多应用于树结构,导航栏这样的结构场景,它属于结构型对象模式。
组合模式一般分为透明式组合和安全组合。下面会具体说明两种模式的差别。
2. 类图
组合模式的设计类图也相对比较简单,主要分为以下三个角色:
抽象类部件:用于提供子类的共同方法,保证整体和部分的对象使用具有一致性。
叶子部件:叶子就是整体部分中的部分,不同的叶子有自己的行为。
整体部件:这个就是整体,它拥有所有叶子的集合,对叶子资源进行管理和控制。
3. 代码实例
场景
有如下场景,在界面上需要根据不同的图像类型进行显示,那么我们就需要视图层作为载体,将图像显示到对应的视图中。不同的图像显示方式不一样,所以需要的视图也就不一样。对图像进行实时显示的时候,我们需要通过事件进行实时刷新所有的视图,保证图像的实时性,那么就需要一个管理视图的类。这个场景刚好符合组合模式的应用,如下是简单的代码结构。
透明式组合
抽象类部件:
abstract class IImageView {
protected String strName;
public IImageView(String strName) {
this.strName = strName;
}
public String getName() {
return this.strName;
}
public void add(IImageView imageView) {
System.out.println("Can't add this view!");
}
public void remove(IImageView imageView) {
System.out.println("Can't remove this view!");
}
public abstract void show();
}
叶子部件:
class E2DImageView extends IImageView {
public E2DImageView(String strName) {
super(strName);
// TODO Auto-generated constructor stub
}
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("Shows a 2d image, this 2d image name is: " + this.strName);
}
}
class E3DImageView extends IImageView {
public E3DImageView(String strName) {
super(strName);
// TODO Auto-generated constructor stub
}
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("Shows a 3d image, this 3d image name is: " + this.strName);
}
}
整体部件:
class EImageView extends IImageView {
private ArrayList<IImageView> arrayImageList;
public EImageView(String strName) {
super(strName);
// TODO Auto-generated constructor stub
arrayImageList = new ArrayList<IImageView>();
}
public void add(IImageView imageView) {
arrayImageList.add(imageView);
System.out.println("Added a view, view name is: " + imageView.getName());
}
public void remove(IImageView imageView) {
arrayImageList.remove(imageView);
System.out.println("Deleted a view, view name is: " + imageView.getName());
}
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("Start showing images in views: ");
for(Object obj : arrayImageList) {
((IImageView)obj).show();
}
}
}
客户代码:
public static void main(String[] args) {
IImageView image2DView1 = new E2DImageView("2d_1");
IImageView image2DView2 = new E2DImageView("2d_2");
IImageView image3DView1 = new E2DImageView("3d_1");
IImageView image3DView2 = new E2DImageView("3d_2");
IImageView imgView = new EImageView("views");
imgView.add(image2DView1);
imgView.add(image2DView2);
imgView.add(image3DView1);
imgView.add(image3DView2);
imgView.show();
imgView.remove(image2DView1);
imgView.remove(image2DView2);
imgView.remove(image3DView1);
imgView.remove(image3DView2);
}
运行结果:
Added a view, view name is: 2d_1
Added a view, view name is: 2d_2
Added a view, view name is: 3d_1
Added a view, view name is: 3d_2
Start showing images in views:
Shows a 2d image, this 2d image name is: 2d_1
Shows a 2d image, this 2d image name is: 2d_2
Shows a 2d image, this 2d image name is: 3d_1
Shows a 2d image, this 2d image name is: 3d_2
Deleted a view, view name is: 2d_1
Deleted a view, view name is: 2d_2
Deleted a view, view name is: 3d_1
Deleted a view, view name is: 3d_2
安全组合
安全组合与透明式组合的区别在于,安全组合只提供叶子部件和整体部件共有的抽象方法。我们从透明式组合可以看出,叶子部件其实不需要add和remove方法,但是为了保证客户在获取到抽象对象的时候不需要进行向下转换为具体的子类对象来调用子类对象特有的方法。透明式组合就采用了这种方式,这种方式会导致的一个问题是,当客户误对叶子部件对象调用add和remove的方法的时候,会引起错误。
如下是安全组合的抽象部件:
abstract class IImageView {
protected String strName;
public IImageView(String strName) {
this.strName = strName;
}
public String getName() {
return this.strName;
}
public abstract void show();
}
这样的话,叶子部件只需要实现show方法,而整体部件根据需求提供对叶子部件的增删查改的接口方法。这样对用户的使用是安全的。
4. 总结
组合模式是有一定的局限性的,它的优点在于:
- 方便客户代码对单一部件和容器部件的统一性的使用。
- 解放了客户代码对复杂的部件的管理。
- 方便客户对单一部件的获取和使用,提供了统一的入口给客户。
当然组合模式也有自身的缺点:
首先是它的使用场景的局限性,当然这些场景也是常见的场景,可以说这既是它的优点也是它的缺点
组合模式难于对特殊的单一部件进行处理,如果你的系统足够复杂,其中不乏有一些特例的部件,在构建过程中就需要根据不同的情况进行处理。