PS1句:终究还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。由于CSDN也支持MarkDown语法了,牛逼啊!
【工匠若水 http://blog.csdn.net/yanbober】 浏览前1篇《设计模式(结构型)之桥接模式(Bridge Pattern)》 http://blog.csdn.net/yanbober/article/details/45366781
组合模式又叫做部份-整体模式,使我们在树型结构的问题中模糊简单元素和复杂元素的概念,客户程序可以像处理简单元素1样来处理复杂的元素,从而使得客户程序与复杂元素的内部结构解耦。组合模式可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。
概念: 组合多个对象构成树形结构以表示具有“整体―部份”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有1致性,组合模式又可以称为“整体―部份”(Part-Whole)模式,它是1种对象结构型模式。
重点: 组合模式结构重要核心模块:
抽象构件(Component)
组合中的对象声明接口,在适当的情况下实现所有类共有接口的默许行动。声明1个接口用于访问和管理Component子部件。
树叶构件(Leaf)
在组合中表示树的叶子结点对象,叶子结点没有子结点。
容器构件(Composite)
定义有枝节点的部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
核心:组合模式的关键是定义1个抽象构件类,它既可以代表Leaf,又可以代表Composite,而客户端针对该抽象构件类进行编程,不必知道它到底表示的是叶子还是容器,可以对其进行统1处理。同时容器对象与抽象构件类之间还建立1个聚合关联关系,在容器对象中既可以包括叶子,也能够包括容器,以此实现递归组合,构成1个树形结构。
在具有整体和部份的层次结构中,希望通过1种方式疏忽整体与部份的差异,客户端可以1致地对待它们。
在1个使用面向对象语言开发的系统中需要处理1个树形结构。
在1个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加1些新的类型。
假定问题环境:
就拿Android开发中最多见的1种情形来分析吧。我们有1种需求是遍历全部cache文件夹下的所有文件及文件夹,打印出来(实际指定不是打印,而是逻辑操作,这里演示而已)。以下我们先不使用组合模式。
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
class MediaFile {
private String mName;
private String mDescription;
public MediaFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public String toString() {
return "MediaFile# Name="+mName+", Description="+mDescription;
}
}
class OfficeFile {
private String mName;
private String mDescription;
public OfficeFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public String toString() {
return "OfficeFile# Name="+mName+", Description="+mDescription;
}
}
class PackageFile {
private String mName;
private String mDescription;
public PackageFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public String toString() {
return "PackageFile# Name="+mName+", Description="+mDescription;
}
}
class BaseFolder {
private String mName;
private String mDescription;
private List<BaseFolder> mBaseFolderList = new ArrayList<>();
private List<MediaFile> mMediaFileList = new ArrayList<>();
private List<OfficeFile> mOfficeFileList = new ArrayList<>();
private List<PackageFile> mPackageFileList = new ArrayList<>();
public BaseFolder(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
public void addBaseFolder(BaseFolder baseFolder) {
mBaseFolderList.add(baseFolder);
}
public void addMediaFile(MediaFile mediaFile) {
mMediaFileList.add(mediaFile);
}
public void addOfficeFile(OfficeFile officeFile) {
mOfficeFileList.add(officeFile);
}
public void addPackageFile(PackageFile packageFile) {
mPackageFileList.add(packageFile);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("*****BaseFolder# enter folder is:"+mName+"
");
for (BaseFolder baseFolder : mBaseFolderList) {
builder.append(baseFolder.toString()+"
");
}
for (MediaFile mediaFile : mMediaFileList) {
builder.append(mediaFile.toString()+"
");
}
for (OfficeFile officeFile : mOfficeFileList) {
builder.append(officeFile.toString()+"
");
}
for (PackageFile packageFile : mPackageFileList) {
builder.append(packageFile.toString()+"
");
}
return builder.toString();
}
}
public class Main {
public static void main(String[] args) {
BaseFolder folder = new BaseFolder("cache", "app cache dir.");
MediaFile mediaFile = new MediaFile("test.png", "test picture.");
folder.addMediaFile(mediaFile);
mediaFile = new MediaFile("haha.mp4", "test video.");
folder.addMediaFile(mediaFile);
PackageFile packageFile = new PackageFile("yanbo.apk", "an android application.");
folder.addPackageFile(packageFile);
BaseFolder childDir = new BaseFolder("word", "office dir.");
OfficeFile officeFile = new OfficeFile("rrrr.doc", "office doc file.");
childDir.addOfficeFile(officeFile);
folder.addBaseFolder(childDir);
System.out.println(folder.toString());
}
}
有了如上实现那末问题来了。。。
如上代码你会发现客户端代码过量地依赖于容器对象复杂的内部实现结构,容器对象内部实现结构的变化将引发客户代码的频繁变化,带来了代码保护复杂、可扩大性差等弊端。组合模式的引入将在1定程度上解决这些问题。
准么办?升个级呗,用1下组合模式。
升个级吧
给上面代码来个重构? 那就依照组合模式结构重要核心模块来对上面代码升级重构1把吧。
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
abstract class AbstractComponent {
public abstract void add(AbstractComponent c);
public abstract AbstractComponent get(int index);
public abstract String getString();
}
class MediaFile extends AbstractComponent {
private String mName;
private String mDescription;
public MediaFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
//unused
}
@Override
public AbstractComponent get(int index) {
return null;
}
@Override
public String getString() {
return "MediaFile# Name="+mName+", Description="+mDescription;
}
}
class OfficeFile extends AbstractComponent {
private String mName;
private String mDescription;
public OfficeFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
//unused
}
@Override
public AbstractComponent get(int index) {
return null;
}
@Override
public String getString() {
return "OfficeFile# Name="+mName+", Description="+mDescription;
}
}
class PackageFile extends AbstractComponent {
private String mName;
private String mDescription;
public PackageFile(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
//unused
}
@Override
public AbstractComponent get(int index) {
return null;
}
@Override
public String getString() {
return "PackageFile# Name="+mName+", Description="+mDescription;
}
}
class BaseFolder extends AbstractComponent {
private String mName;
private String mDescription;
private List<AbstractComponent> abstractComponents = new ArrayList<>();
public BaseFolder(String mName, String mDescription) {
this.mName = mName;
this.mDescription = mDescription;
}
@Override
public void add(AbstractComponent c) {
abstractComponents.add(c);
}
@Override
public AbstractComponent get(int index) {
return abstractComponents.get(index);
}
@Override
public String getString() {
StringBuilder builder = new StringBuilder("*****BaseFolder# enter folder is:"+mName+"
");
for (AbstractComponent baseFolder : abstractComponents) {
builder.append(baseFolder.getString()+"
");
}
return builder.toString();
}
}
public class Main {
public static void main(String[] args) {
AbstractComponent folder = new BaseFolder("cache", "app cache dir.");
AbstractComponent mediaFile = new MediaFile("test.png", "test picture.");
folder.add(mediaFile);
mediaFile = new MediaFile("haha.mp4", "test video.");
folder.add(mediaFile);
AbstractComponent packageFile = new PackageFile("yanbo.apk", "an android application.");
folder.add(packageFile);
BaseFolder childDir = new BaseFolder("word", "office dir.");
AbstractComponent officeFile = new OfficeFile("rrrr.doc", "office doc file.");
childDir.add(officeFile);
folder.add(childDir);
System.out.println(folder.getString());
}
}
上例通过组合模式代码具有了良好的可扩大性,在增加新的文件类型时,不必修改现有类库代码,只需增加1个新的文件类作为AbstractFile类的子类便可(不用在BaseFolder中再去修改添加),但是由于在AbstractFile中声明了大量用于管理和访问成员构件的方法,例如add(),我们需要在新增的文件类中实现这些方法,提供对应的毛病提示和异常处理。这下你就明白了,其实如上代码还可以继续优化的,把每一个子类实现的毛病提示和异常处理写入抽象的基类中作为默许处理,这样也能够减少代码重复。哈哈。
当发现需求中是体现部份与整体层次结构时,和你希望用户可以疏忽组合对象与单个对象的不同,统1地使用组合结构中的所有对象时,就应当斟酌组合模式了。
组合模式优点以下:
组合模式缺点以下:
【工匠若水 http://blog.csdn.net/yanbober】 继续浏览《设计模式(结构型)之装潢者模式(Decorator Pattern)》 http://blog.csdn.net/yanbober/article/details/45395747
下一篇 列表 环 判定 初始位置