设计模型之组合模式含UML完整实例)

设计模型之组合模式

1. 组合模式

1.1 定义与特点

  有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

  组合模式的主要优点有:

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

  其主要缺点是:

  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
  • 不容易限制容器中的构件;
  • 不容易用继承的方法来增加构件的新功能;
1.2 模式的结构

  组合模式包含以下主要角色:

  • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
  • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
  • 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含
    Add()、Remove()、GetChild() 等方法。

  组合模式分为透明式的组合模式和安全式的组合模式:

  (1) 透明方式:在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
  ((2) 安全方式:在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。

1.3 问题由来

  客户端面临处理一个树形结构时,并希望可以忽略组合对象和单个对象的差异时,比如文件夹和文件,就是一个树状结构,文件夹是组合对象,文件时单个对象。在比如生活中的公司就是一个树形结构,有部门这个子节点,员工这个子节点,部门是组合对象,员工是单个对象。

1.4 解决思路

  如果想让单个对象和组合对象的使用具有一致性,其实就是我们可以通过协议(编程语言中的抽象类或接口)来定义对象的属性和行为,这样就实现了单个对象和组合对象的使用具有一致性。但是实际开发中,一般都不要一致性,比如文件夹和文件,文件夹有添加文件夹和文件的功能,但是文件没有添加功能,文件夹和文件不需要一致性,所以实际开发中对树状结构的处理大多采用的是安全式的组合模式。

1.5 UML类图

在这里插入图片描述

图一 透明方式

在这里插入图片描述

图二 安全方式
1.6 解决方案

  1.透明方式


/**
 * @author tbb
 *      功能抽象
 */
public abstract class Component 
{
	private String name;
	abstract void addChild(Component component);
	abstract void removeChild(Component component);
	abstract  Component getChild(int i);
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	
}
public class File extends Component
{
	@Override
	public void addChild(Component component) {
		// TODO Auto-generated method stub
	}

	@Override
	public void removeChild(Component component) {
		// TODO Auto-generated method stub
	}

	@Override
	public Component getChild(int i) {
		return null;
	}

	public File(String name) {
		super.setName(name);
	}

	@Override
	public String toString() {
		return  super.getName();
	}
	
	
}
public class Folder extends Component
{
    private List<Component> componentList = new ArrayList<Component>();
	@Override
	public void addChild(Component component) 
	{
		this.componentList.add(component);
	}

	@Override
	public void removeChild(Component component) 
	{
		this.componentList.remove(component);
	}

	@Override
	public Component getChild(int i) {
		return this.componentList.get(i);
	}

	public List<Component> getComponentList() {
		return componentList;
	}

	public void setComponentList(List<Component> componentList) {
		this.componentList = componentList;
	}

	public Folder(String name) {
		super.setName(name);
	}
	
	@Override
	public String toString() {
		return super.getName();
	}

	

}

public class Test 
{
    public static void main(String[] args) 
    {
    	Folder dFolder = new Folder("D:");
    	Folder aFolder = new Folder("a文件夹");
    	dFolder.addChild(aFolder);
    	Folder bFolder = new Folder("b文件夹");
    	aFolder.addChild(bFolder);
    	File testFile = new File("test.txt");
    	bFolder.addChild(testFile);
    	System.out.println(dFolder.getChild(0).getChild(0).getChild(0));
    	
	}
}

  2.安全方式

/**
 * @author tbb
 * 文件夹和文件公有部分类
 */
public  class FileComponent 
{
	private String id;
	private String name;
	private String parentId;
	private Date createTime;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getParentId() {
		return parentId;
	}
	public void setParentId(String parentId) {
		this.parentId = parentId;
	}
	public Date getCreateTime() {
		return createTime;
	}
	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}
	
	
}
/**
 * @author tbb
 * 文件信息
 */
public class FileInfo extends FileComponent
{
	private String content;
	
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	
	
}

/**
 * @author tbb
 * 文件夹信息
 */
public class FolderInfo extends FileComponent
{

}

/**
 * @author tbb
 * 返回给业务层文件夹信息类
 */
public class  FolderInfoVO  extends FolderInfo
{
	/**
	   * 子文件夹节点
	 */
	private  List<FolderInfoVO> childFolderNodes = new ArrayList<FolderInfoVO>();
	/**
	 * 子文件节点
	 */
	private  List<FileInfo> childFileList = new ArrayList<FileInfo>();
	
	public List<FolderInfoVO> getChildFolderNodes() {
		return childFolderNodes;
	}
	public void setChildFolderNodes(List<FolderInfoVO> childFolderNodes) {
		this.childFolderNodes = childFolderNodes;
	}
	public List<FileInfo> getChildFileList() {
		return childFileList;
	}
	public void setChildFileList(List<FileInfo> childFileList) {
		this.childFileList = childFileList;
	}
	
	

}
public class Test 
{
	  private List<FolderInfo> folderInfoComponentList = new ArrayList<FolderInfo>();
	  private List<FileInfo> fileComponentList = new ArrayList<FileInfo>();
	  public FolderInfo addFolderInfo(FolderInfo folderInfo)
	  {
		  folderInfo.setId(UUID.randomUUID().toString());
		  folderInfo.setCreateTime(new Date());
		  folderInfoComponentList.add(folderInfo);
		  return folderInfo;
	  }
	  public FileInfo addFileInfo(FileInfo fileInfo)
	  {
		  fileInfo.setId(UUID.randomUUID().toString());
		  fileInfo.setCreateTime(new Date());
		  fileComponentList.add(fileInfo);
		  return fileInfo;
	  }
	  
	  public List<FileInfo> getFileInfoListByFolderId(String id)
	  {
		return fileComponentList.stream().filter(e->id.equals(e.getParentId())).collect(Collectors.toList());
		  
	  }
	  
	  public   List<FolderInfoVO> getNode()
	  {
		  List<FolderInfo> folderInfoComponentList = this.folderInfoComponentList;
		  if (folderInfoComponentList.size() == 0 || folderInfoComponentList == null)
		  {
			  return null;
		  }
		
		  List<FolderInfoVO> resultFolderList  = new ArrayList<FolderInfoVO>(); // 树状结构
		  Map<String, List<FolderInfoVO>> folderMap = new HashMap<String, List<FolderInfoVO>>();// 父id和同一个父id下的所有直接后代(子文件夹 和子文件夹下的文件)
		  for (FolderInfo folderInfo : folderInfoComponentList)
		  {
			  List<FileInfo> fileInfoList = getFileInfoListByFolderId(folderInfo.getId());
			  FolderInfoVO  folderInfoVO = new FolderInfoVO();
			  BeanUtils.copyProperties(folderInfo, folderInfoVO);
			  folderInfoVO.setChildFileList(fileInfoList);
			  if (folderInfo.getParentId() != null)
			  {  
			      List<FolderInfoVO> folderList = folderMap.get(folderInfo.getParentId());// 为了将同一个父id的文件夹 放入同一个list中
				  if (folderList == null)
				  {
					  folderList = new ArrayList<FolderInfoVO>();
				  }
				  folderList.add(folderInfoVO);
				  folderMap.put(folderInfo.getParentId(), folderList);
			  }
			  else
			  {
				  resultFolderList.add(folderInfoVO); 
				  
			  }
			  
		  }
		  //resultFolderList目前只存放了所有 最顶层的文件夹及其文件
		  for (FolderInfoVO folderInfoVO : resultFolderList)
		  {
			  initRoot( folderInfoVO, folderMap);
		  }
		  
		  return resultFolderList;
	  }
	  
	  
	    /**
		 *      将Map<String, List<FolderInfoVO>>对象中每一层级的文件夹list,放入直属父类的文件夹list对象属性中
		 * @param folderInfoVO
		 * @param folderMap
		 */
		public void initRoot(FolderInfoVO folderInfoVO, Map<String, List<FolderInfoVO>> folderMap)
		{
			  List<FolderInfoVO> childFolderNodes = folderMap.get(folderInfoVO.getId());
			  if (childFolderNodes != null)
			  {
				  folderInfoVO.setChildFolderNodes(childFolderNodes);
				  for (FolderInfoVO f : childFolderNodes) 
				  {
					  initRoot(f,  folderMap); // 下一层级
				  }
			  }
			  else
			  {
				  return; // 最底层级别了
			  }
			 
		}
      public static void main(String[] args) 
      {
    	  Test test = new Test();
    	  
    	  FolderInfo dFolderInfo = new FolderInfo();
    	  dFolderInfo.setName("D:");
    	  dFolderInfo = test.addFolderInfo(dFolderInfo);
    	  
    	  FolderInfo aFolderInfo = new FolderInfo();
    	  aFolderInfo.setName("a文件夹");
    	  aFolderInfo.setParentId(dFolderInfo.getId());
    	  aFolderInfo = test.addFolderInfo(aFolderInfo);
    	  
    	  
    	  FolderInfo bFolderInfo = new FolderInfo();
    	  bFolderInfo.setName("b文件夹");
    	  bFolderInfo.setParentId(aFolderInfo.getId());
    	  bFolderInfo = test.addFolderInfo(bFolderInfo);
    	  FileInfo txtFileInfo = new FileInfo();
    	  txtFileInfo.setName("test.txt");
    	  txtFileInfo.setParentId(bFolderInfo.getId());
    	  txtFileInfo.setContent("nihao");
    	  txtFileInfo = test.addFileInfo(txtFileInfo);
    	 
    	
    	  FolderInfo a1FolderInfo = new FolderInfo();
    	  a1FolderInfo.setName("a1文件夹");
    	  a1FolderInfo.setParentId(dFolderInfo.getId());
    	  a1FolderInfo = test.addFolderInfo(a1FolderInfo);
    	  
    	  FolderInfo b1FolderInfo = new FolderInfo();
    	  b1FolderInfo.setName("b1文件夹");
    	  b1FolderInfo.setParentId(a1FolderInfo.getId());
    	  b1FolderInfo = test.addFolderInfo(b1FolderInfo);

    	  
    	  
    	  FileInfo docFileInfo = new FileInfo();
    	  docFileInfo.setName("hello.doc");
    	  docFileInfo.setContent("word");
    	  docFileInfo.setParentId(b1FolderInfo.getId());
    	  docFileInfo = test.addFileInfo(docFileInfo);
    	  
    	  
    	  
    	  List<FolderInfoVO> resultList = test.getNode();
    	  
    	  System.out.println(resultList);
    	  
	  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值