组合模式在JDK以及MyBatis源码中的应用

结构型模式                 ————顺口溜:适装桥组享代外

目录

1、组合模式(Composite Pattern)

1.1 组合模式UML图

1.2 日常生活中看组合模式

1.3 应用实例

1.4 Java代码实现

1.4.1 透明方式

1.4.2 安全方式

2、组合模式在源码中的应用

2.1 JDK源码中组合模式体现

2.2 MyBatis源码中组合模式体现

3、组合模式的优缺点

3.1 优点

3.2 缺点

3.3 使用场景

3.4 注意事项

4、组合模式与装饰器模式


1、组合模式(Composite Pattern)

又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。——它创建了对象组的树形结构

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

  • 意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
  • 主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
  • 何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
  • 如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
  • 关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

1.1 组合模式UML图

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

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

在这里插入图片描述

图一 透明方式

在这里插入图片描述

图二 安全方式

1.2 日常生活中看组合模式

  • 组合模式常用于和树状结构有关的场景中,其他场景使用的还是比较少的。
  • 总部与分公司之间的树形关系。

1.3 应用实例

  1. 算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作数、操作符和另一个操作数。
  2. 在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。

1.4 Java代码实现

1.4.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));
    	
	}
}

1.4.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);
    	  
	  }
}

2、组合模式在源码中的应用

2.1 JDK源码中组合模式体现

HashMap中有一个putAll方法,参数是一个Map,这就是一种组合模式的体现:  

在这里插入图片描述
另外还有ArrayList中的addAll方法也是一样。

2.2 MyBatis源码中组合模式体现

MyBatis中有一个SqlNode接口,下面很多一级标签:

在这里插入图片描述

然后一级标签下面又有二级标签(这就是组合模式的体现):

在这里插入图片描述

3、组合模式的优缺点

3.1 优点

  1. 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
  2. 让客户端忽略了层次的差异,方便对整个层次结构进行控制
  3. 简化客户端代码
  4. 符合开闭原则

3.2 缺点

  1. 限制类型时较为复杂
  2. 使设计变得更加抽象
  3. 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则

3.3 使用场景

  1. 希望客户端可以忽略组合对象与单个对象的差异时
  2. 处理一个树型结构
  3. 文件、文件夹的管理

3.4 注意事项

注意事项:定义时为具体类

4、组合模式与装饰器模式

装饰者模式、组合模式对比:
1、结构型模式——组合模式,用于把众多子对象组织为一个整体,及此程序员与大批对象打交道时可以将他们当作一个对象来对待,并将它们组织为层次性的树。通常它并不修改方法调用,
而只是将其沿组合对象与子对象的链向下传递,直到到达并落实在叶对象上。
2、结构型模式——装饰器模式,但它并非用于组织对象,而是用于在不修改现有对象或从其派生子类的前提下为其增添职责。在一些较简单的例子中,装饰者模式会透明而不加修改地传递所有方法调用,
不过,创建装饰者模式的目的就在于对方法进行修改。
尽管简单的组合对象与简单的装饰者对象是相同的,但二者却有着不同的焦点。组合对象并不修改方法调用,其着眼在点于组织子对象。而装饰者模式存在的唯一目的就是修改方法调用而不是组织子对象,因为子对象只有一个。

 

参考文章:

https://blog.csdn.net/atu1111/article/details/105506191

https://www.runoob.com/design-pattern/composite-pattern.html

https://blog.csdn.net/zwx900102/article/details/108554255

https://blog.csdn.net/qq_37909508/article/details/93964968

https://blog.csdn.net/itpinpai/article/details/51567511

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值