23种设计模式----组合模式----结构型模式
原创
©著作权归作者所有:来自51CTO博客作者qq5963a5404b339的原创作品,请联系作者获取转载授权,否则将追究法律责任
组合模式
23种设计模式
1.什么是组合模式
日常生活中存在这样的一类型事物:
给你一个目录,你不知道是文件还是文件夹
给你一个链接,你不知道是具体的文本还是网页
一个大箱子可以容纳一个小箱子,这个小箱子又可以容纳更小的箱子。
同时,对一个分数进行二分,二分之一,四分之一,八分之一。。。
这就是递归,递归的本质就是递归的对象和本对象结构上完全相同。
还有比较著名的分形几何,都是递归实现的。
所以,为了解决递归,描述递归的对象,就有了组合模式。
2.组合模式的角色
抽象组合,
实现组合。
关系:
继承
继承
关联
实现组合1
抽象组合
实现组合2
3.核心思想
递归。
4.例子----目录遍历
首先这是项目结构:
4.1抽象组合–目录
目录有可能是文件,也有可能是文件夹
import java.io.File;
import cn.com.startimes.composite.enums.CatalogType;
import cn.com.startimes.composite.exception.CompositeException;
/**
* 目录
* 文件夹和文件的抽象类
* 一个目录类拥有这几种方法:
* 1.判断一个目录是文件还是文件夹
* 2.获取目录的 名字,类型,大小(类型,大小为file)
* 3.增加目录到目录(folder)
* 4.输出
*/
public abstract class Catalog {
private String name;
public abstract void scan(String path);
public abstract String getSize() throws CompositeException;
public abstract CatalogType getType();
public abstract void add(Catalog catalog) throws CompositeException;
public abstract void print(Catalog catalog);
public Catalog(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.2 实现组合–文件夹
import java.io.File;
import java.util.ArrayList;
import cn.com.startimes.composite.abs.Catalog;
import cn.com.startimes.composite.enums.CatalogType;
import cn.com.startimes.composite.exception.CompositeException;
/**
* 文件夹类
* 因为jdk中有File类等等,为了少一些
* 麻烦,我们的叫做My+...
*/
public class MyFolder extends Catalog{
private ArrayList<Catalog> catalogList = new ArrayList<Catalog>();
private File file = null;
public MyFolder(String name) {
super(name);
}
@Override
public void scan(String path) {
file = new File(path);
if(file.isDirectory()){
for(File f:file.listFiles()){
this.scan(f.getPath());
}
} else {
Catalog catalog = new MyFile(file.getName());
catalog.scan(file.getPath());
this.add(catalog);
}
}
@Override
public String getSize() throws CompositeException {
throw CompositeException.error(CompositeException.CAN_NOT_GET_FOLDER_SIZE);
}
@Override
public CatalogType getType(){
return CatalogType.FOLDER;
}
@Override
public void add(Catalog catalog) {
catalogList.add(catalog);
}
@Override
public void print(Catalog catalog) {
System.out.println(file.getPath());
for(Catalog c:catalogList){
c.print(c);
}
}
}
4.3 实现组合-文件
import java.io.File;
import cn.com.startimes.composite.abs.Catalog;
import cn.com.startimes.composite.enums.CatalogType;
import cn.com.startimes.composite.exception.CompositeException;
/**
* 文件类,因为jdk中File类
* 所以,我们的类叫做My+...
*
*/
public class MyFile extends Catalog{
private File file = null;
public MyFile(String name) {
super(name);
}
@Override
public void scan(String path) {
file = new File(path);
}
@Override
public String getSize() throws CompositeException {
return ((file.length()*100)/1024)/(double)100+" k";
}
@Override
public CatalogType getType() {
return CatalogType.FILE;
}
@Override
public void add(Catalog catalog) throws CompositeException{
throw CompositeException.error(CompositeException.CAN_NOT_ADD_FOR_FILE);
}
@Override
public void print(Catalog catalog) {
try {
System.out.println(file.getPath()+" "+catalog.getSize());
} catch (CompositeException e) {
e.printStackTrace();
}
}
}
4.4 目录类型
public enum CatalogType {
//文件
FILE(1),
//文件夹
FOLDER(2);
private Integer code;
public static CatalogType valueOf(int ordinal){
for(CatalogType catalogType:CatalogType.values()){
if(ordinal == catalogType.code){
return catalogType;
}
}
return null;
}
private CatalogType(Integer code){
this.code = code;
}
public void setCode(Integer code){
this.code = code;
}
public Integer getCode(){
return code;
}
}
4.5 组合异常
public class CompositeException extends Exception{
/**
*
*/
private static final long serialVersionUID = 2521498032586564566L;
public static final String CAN_NOT_GET_FOLDER_TYPE = "不能获取文件夹的类型";
public static final String CAN_NOT_GET_FOLDER_SIZE = "不能获取文件夹的大小";
public static final String CAN_NOT_ADD_FOR_FILE = "文件不能添加";
public static CompositeException error(String message){
System.out.println("---------error----------"+message);
return new CompositeException();
}
}
4.6 测试方法–main方法
import java.io.File;
import java.util.Scanner;
import cn.com.startimes.composite.abs.Catalog;
import cn.com.startimes.composite.impl.MyFile;
import cn.com.startimes.composite.impl.MyFolder;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("please input the root file/folder path");
String path = scanner.next();
File file = new File(path);
Catalog catalog = null;
if(file.isDirectory()){
catalog = new MyFolder(path);
} else {
catalog = new MyFile(path);
}
catalog.scan(path);
catalog.print(catalog);
scanner.close();
}
}
4.7 结果
文件夹
please input the root file/folder path
E:\Notepad++
E:\Notepad++\updater\README.md
E:\Notepad++\change.log 1.69 k
E:\Notepad++\contextMenu.xml 3.51 k
E:\Notepad++\functionList.xml 64.74 k
E:\Notepad++\langs.model.xml 320.44 k
E:\Notepad++\LICENSE 15.81 k
E:\Notepad++\localization\chineseSimplified.xml 69.06 k
E:\Notepad++\localization\english.xml 70.1 k
E:\Notepad++\notepad++.exe 3150.67 k
E:\Notepad++\NppShell_06.dll 224.67 k
E:\Notepad++\plugins\APIs\actionscript.xml 20.26 k
E:\Notepad++\plugins\APIs\BaanC.xml 18.05 k
E:\Notepad++\plugins\APIs\batch.xml 3.69 k
E:\Notepad++\plugins\APIs\c.xml 43.34 k
E:\Notepad++\plugins\APIs\cmake.xml 7.14 k
E:\Notepad++\plugins\APIs\coffee.xml 1.47 k
E:\Notepad++\plugins\APIs\cpp.xml 60.04 k
E:\Notepad++\plugins\APIs\cs.xml 15.01 k
E:\Notepad++\plugins\APIs\css.xml 11.25 k
E:\Notepad++\plugins\APIs\html.xml 19.1 k
E:\Notepad++\plugins\APIs\java.xml 200.55 k
E:\Notepad++\plugins\APIs\javascript.xml 21.39 k
E:\Notepad++\plugins\APIs\lisp.xml 15.82 k
E:\Notepad++\plugins\APIs\nsis.xml 7.99 k
E:\Notepad++\plugins\APIs\perl.xml 13.93 k
E:\Notepad++\plugins\APIs\php.xml 417.89 k
E:\Notepad++\plugins\APIs\python.xml 60.14 k
E:\Notepad++\plugins\APIs\rc.xml 1.6 k
E:\Notepad++\plugins\APIs\sql.xml 66.35 k
E:\Notepad++\plugins\APIs\tex.xml 66.28 k
E:\Notepad++\plugins\APIs\vb.xml 16.79 k
E:\Notepad++\plugins\APIs\vhdl.xml 2.57 k
E:\Notepad++\plugins\APIs\xml.xml 11.74 k
E:\Notepad++\plugins\Config\Hunspell\en_US.aff 2.97 k
E:\Notepad++\plugins\Config\Hunspell\en_US.dic 679.81 k
E:\Notepad++\plugins\DSpellCheck.dll 3159.17 k
E:\Notepad++\plugins\mimeTools.dll 117.17 k
E:\Notepad++\plugins\NppConverter.dll 153.67 k
E:\Notepad++\readme.txt 1.41 k
E:\Notepad++\SciLexer.dll 1400.17 k
E:\Notepad++\shortcuts.xml 1.54 k
E:\Notepad++\stylers.model.xml 161.94 k
E:\Notepad++\uninstall.exe 256.28 k
E:\Notepad++\updater\GUP.exe 204.67 k
E:\Notepad++\updater\gup.xml 3.87 k
E:\Notepad++\updater\libcurl.dll 533.17 k
E:\Notepad++\updater\LICENSE 7.62 k
E:\Notepad++\updater\README.md 2.92 k
文件
please input the root file/folder path
E:\Notepad++\change.log
E:\Notepad++\change.log 1.69 k
5.总结
1.使用了模板方法的思想:
抽象类中规定子类必须实现的方法
2.异常对类的方法进行过滤:
比如作为文件夹类,不能使用获取大小的方法,但是又不能不实现,所以只好抛出异常或者使用空方法。
异常的使用还有一点,就是作为抽象类,定义的方法不是抽象方法,而是实际的方法,但是实际的方法中只有抛出异常的代码。这样写,如果子类覆盖了父类的方法,程序可以正常的运行,如果没有实现方法,那么就会抛出异常。
同时异常也是对方法的使用做的一个安全措施,某些类不能使用某些方法,如果使用了,就抛出异常。避免方法被使用,(类似野指针的野方法,你不知道在哪里被用到,特别是使用了模板方法或者适配器模式之后,方法的调用从显示到了隐式方面,所以这个是一个较好的方法)。
3.递归:
一般来说递归是自己调用自己,但是使用组合模式就是利用,在递归内部调用抽象类的递归方法,根据实例对象的不同,调用抽象递归不同的实现。
4.优点:
使用组合模式可以轻易的处理一大堆类似但是不完全相同的处理。使用封装继承等,可以完成大量相似但是不同的操作。
5.难点:
递归调用的方法,传入参数的不同,会调用不同的方法,这里是难点。
也是递归能否正确执行的关键。