通俗理解组合模式(Composite)-设计模式-结构型

 

目录

1、通过代码体会组合模式

(1)结构图: 

(2)代码

2、理解与定义

(1)定义

(2)优点:

(3)缺点:

(4) 适用场景

3、抽象组件的实现讨论(结构图)

(1)两种实现方式

方式一:

方式二:

4、源码学习


 

1、通过代码体会组合模式

我们一定了解文件夹和文件的关系,文件夹可以包含多个文件,而文件不能再包含文件。组合模式相对比较简单,我们用一个课程目录结构的实现代码,体会组合模式。场景是课程包里包含子课程包和课程。比如是下面这样的一个层级结构。

技术进阶课程
 Course name:Linux课程, price:11.0
 Course name:Windows课程, price:9.0
 Java课程目录
  Course name:Java课程1, price:23.0
  Course name:Java课程2, price:23.0

(1)结构图: 

(2)代码

首先是通用的抽象类CatalogComponent,不管是课程包还是具体课程,都需要继承自它。但是继承的时候可以选择性的对抽象类的方法进行重写。我们知道,目录要能够包含文件,而文件不能再有子节点。

package structural.composite;

public abstract class CatalogComponent {

	public void add(CatalogComponent catalogComponent) {
		throw new UnsupportedOperationException("不支持添加操作");
	}
	
	public void remove(CatalogComponent catalogComponent) {
		throw new UnsupportedOperationException("不支持删除操作");
	}
	
	public String getName(CatalogComponent catalogComponent) {
		throw new UnsupportedOperationException("不支持获取名称操作");
	}
	
	public double getPrice(CatalogComponent catalogComponent) {
		throw new UnsupportedOperationException("不支持获取价格操作");
	}
	
	public void print() {
		throw new UnsupportedOperationException("不支持打印操作");
	}
	
	
}

然后,课程包类:

package structural.composite;

import java.util.ArrayList;
import java.util.List;


public class CourseCatalog extends CatalogComponent{

	private List<CatalogComponent> items=new ArrayList<CatalogComponent>();
	private String name;
	private Integer level;
	
	
	public CourseCatalog(String name,Integer level) {
		this.name=name;
		this.level=level;
	}
	
	@Override
	public void add(CatalogComponent catalogComponent) {
		items.add(catalogComponent);
	}

	@Override
	public void remove(CatalogComponent catalogComponent) {
		items.remove(catalogComponent);
	}

	@Override
	public String getName(CatalogComponent catalogComponent) {
		return this.name;
	}

	@Override
	public void print() {
		System.out.println(this.name);
		for (CatalogComponent catalogComponent : items) {
			if (this.level!=null) {
				for (int i = 0; i < level; i++) {
					System.out.print(" ");
				}
				
			}
			catalogComponent.print();
		}
	}

	
}

然后,是课程类:

package structural.composite;

public class Course extends CatalogComponent{
	
	private String name;
	private double price;
	
	public Course(String name,double price) {
		this.name=name;
		this.price=price;
	}

	@Override
	public String getName(CatalogComponent catalogComponent) {
		return this.name;
	}

	@Override
	public double getPrice(CatalogComponent catalogComponent) {
		return this.price;
	}

	@Override
	public void print() {
		System.out.println("Course name:"+name+", price:"+price);
	}
	
	

	
}

最后是测试类:

package structural.composite;

public class Test {
	public static void main(String[] args) {
		CatalogComponent linuxCourse=new Course("Linux课程", 11);
		CatalogComponent windowsCourse=new Course("Windows课程", 9);
		CatalogComponent javaCatalog = new CourseCatalog("Java课程目录",2);
		
		CatalogComponent javaCourse1=new Course("Java课程1", 23);
		CatalogComponent javaCourse2=new Course("Java课程2", 23);
		javaCatalog.add(javaCourse1);
		javaCatalog.add(javaCourse2);
		
		CatalogComponent mainCatalog=new CourseCatalog("技术进阶课程",1);
		mainCatalog.add(linuxCourse);
		mainCatalog.add(windowsCourse);
		mainCatalog.add(javaCatalog);
		
		mainCatalog.print();
				

		
	}
}

完整代码在这里:

https://github.com/phs999/DesignPatterns/tree/9f96419d299ca3a9d55df4fa0b4f3b4e25e79fdb/design_pattern/src/structural/composite

2、理解与定义

(1)定义

通过以上的课程包与课程的关系,我们可以大概理解组合模式是个什么:

 组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。使客户端对单个对象和组合对象保持一致的方式处理。 

(2)优点:

  • 可以实现清楚的定义分层次的复杂对象,表示对象的全部或部分层次。
  • 让客户端忽略了层次的差异,方便对整个层次结构进行控制。从而简化了客户端代码。
  • 符合开闭原则。当扩展叶子节点或容器节点,不会对原来的代码造成影响。
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

(3)缺点:

  • 限制类型时会较复杂。比如上面的例子中我Java课程包里只想限制容纳Java类的课程,而其他类型课程不能出现在这个目录包下。这种需求在当前设计模式下就不好实现。

(4) 适用场景

      在以下情况下可以考虑使用组合模式:

      (1) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。

      (2) 在一个使用面向对象语言开发的系统中需要处理一个树形结构。

      (3) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。

3、抽象组件的实现讨论(结构图)

(1)两种实现方式

方式一:

就像文章一开始的例子那样,我们在抽象类中定义容器节点和叶子节点所需的所有方法,由容器节点和叶子节点自己定义需要重写哪些方法。但是,这样就需要在抽象类中对定义的方法进行处理,防止客户端使用多态的方式调用到节点对象未定义的方法上,比如我们代码中默认方法都是抛出异常。

 

方式二:

只在抽象类中定义 容器节点和叶子节点通用的方法,节点操作的方法放在容器节点来定义实现。

 

4、源码学习

(1)java.awt.Container是典型的容器类,Component就是那个抽象类。

public class Container extends Component {  
    public Component add(Component comp) {
        addImpl(comp, null, -1);
        return comp;
    }

}

(2)ArrayList是典型的容器类,Collection就是那个抽象类。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

      public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

}


public interface List<E> extends Collection<E> {
}

(3)Mybatis中的MixedSqlNode是典型的容器对象,SqlNode是抽象的那个接口。

package org.apache.ibatis.scripting.xmltags;

import java.util.Iterator;
import java.util.List;

public class MixedSqlNode implements SqlNode {
    private List<SqlNode> contents;

    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }

    public boolean apply(DynamicContext context) {
        Iterator i$ = this.contents.iterator();

        while(i$.hasNext()) {
            SqlNode sqlNode = (SqlNode)i$.next();
            sqlNode.apply(context);
        }

        return true;
    }
}

 

参考:

https://blog.csdn.net/LoveLion/article/details/7956937

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值