组合模式
组合模式也叫部分-整体模式,我先用通俗的方式来讲它是什么,组合模式的另一种叫法很好的解释了组合模式的含义,就是其即是部分又是整体,那么什么叫即是部分又是整体呢?大家应该都知道数据结构中的树,树是由结点所组成的,结点可分为根节点、树枝结点和叶子结点,但是其本质都是一种结点,只是叫法不同便以我们理解。树的最顶层是根节点,那么我也可以理解为这个根节点就是这棵树,其他的每个树枝结点和叶子结点也都可以表示成以其本身为根节点的子树,而上面又说到这三种结点本质都是一种结点,那么结论就有结点 = 树,其即是部分又是整体,完美的解释了组合模式的含义。
相信你看完上段内容后再来看比较官方的介绍会更容易理解一些,组合模式是指通过将单个对象和组合对象用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性。
组合模式是结构型模式。
使用场景
系统对象层次具备整体和部分,呈树形结构,且要求具备统一行为(如树形菜单,操作系统目录结构,公司组织架构等)
应用实例
假设现在需要实现一个可以输出学校下属层级信息的功能,如学校下属有学院,学院下属有系别,我们需要把学校、学院和系别的名称和描述信息打印出来。
1、一般实现
如果不使用设计模式来实现这个功能,通常我们会对学校、学院、系别分别创建对应的类,再用学校套学院,学院套系别的组合形式来实现,但是这种方式欠缺灵活,不方便扩展,如果我要再对系别下增加专业的时候,我们不仅需要在对应专业创建对应的类,还需要修改系别对应类的代码,违反了开闭原则。
2、组合模式实现
那么使用组合模式呢?即是部分又是整体,我们可以把学校、学院、系别抽象成一种形式,它们都是组织,并且它们在功能和行为上具有一致性,所以只需创建组织对应的类即可,并在其内部持有一个存放自己的集合。
上代码
/**
* 组织抽象类,可表示学校、学院、系别等形式的组织
*/
public class Organization {
private String name;
private String desc;
//存放下属组织的集合
private ArrayList<Organization> subOrganization;
public Organization(String name, String desc) {
this.name = name;
this.desc = desc;
subOrganization = new ArrayList<>();
}
/**
* 添加下属组织
* @param component
*/
public void add(Organization component){
subOrganization.add(component);
}
/**
* 删除下属组织
* @param component
*/
public void del(Organization component){
subOrganization.remove(component);
}
/**
* 打印包括本身及以下的所有组织信息
*/
public void print() {
System.out.println(getName()+"("+getDesc()+")");
if (subOrganization.size()!=0)
for (Organization organization : subOrganization) {
organization.print();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public class Demo {
public static void main(String[] args) {
//学校
Organization university = new Organization("桂林电子科技大学","广西小清华");
//学院
Organization college = new Organization("--海洋信息工程学院","北海校区东校区");
Organization college2 = new Organization("--信息职业技术学院","北海校区西校区");
//系别
Organization department = new Organization("----计算机科学与技术系","修电脑的");
Organization department2 = new Organization("----物流管理系","送快递的");
Organization department3 = new Organization("----电子信息工程系","装宽带的");
Organization department4 = new Organization("----经济与管理系","管账的");
university.add(college);
university.add(college2);
college.add(department);
college.add(department2);
college2 .add(department3);
college2.add(department4);
university.print();
}
}
打印输出
桂林电子科技大学(广西小清华)
--海洋信息工程学院(北海校区东校区)
----计算机科学与技术系(修电脑的)
----物流管理系(送快递的)
--信息职业技术学院(北海校区西校区)
----电子信息工程系(装宽带的)
----经济与管理系(管账的)
注意,如果这时我要对系别下增加专业,我不需要额外再创建对应专业的类,我只需要对department继续add表示专业的Organization对象即可,是不是很方便扩展。
改进组合模式(透明式)
看到这里,可能有人会有疑问控制权限的问题,比如假定系别的下面没有东西了(当然现实中这是不可能的),它就是最后一层,我怎么能控制它不可以继续add了呢?遇到这种问题,我们可以对上面的组合模式进行一些改造,记得上面讲的树吗,这次我把结点抽象出来,再将其具体表现为叶子结点和非叶子结点,非叶子结点可以进行添加结点,而叶子结点则不可以。
它们之间的关系是这样的,引用网上的一张图。
Component表示抽象结点,Composite表示非叶子结点,Leaf表示叶子结点,叶子结点和非叶子结点继承自抽象结点,非叶子结点内组合了抽象结点。
/**
* 表示抽象结点的组织类
*/
public abstract class OrganizationComponent {
private String name;
private String desc;
protected ArrayList<OrganizationComponent> subOrganization;
public OrganizationComponent(String name, String desc) {
this.name = name;
this.desc = desc;
}
public void add(OrganizationComponent component){
//默认实现
throw new UnsupportedOperationException();
}
public void del(OrganizationComponent component){
//默认实现
throw new UnsupportedOperationException();
}
protected abstract void print();
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
/**
*表示非叶子结点的组织类
*/
public class OrganizationComposite extends OrganizationComponent {
public OrganizationComposite(String name, String desc) {
super(name, desc);
subOrganization = new ArrayList<>();
}
@Override
public void add(OrganizationComponent component) {
subOrganization.add(component);
}
@Override
public void del(OrganizationComponent component) {
subOrganization.remove(component);
}
@Override
protected void print() {
System.out.println(getName()+"("+getDesc()+")");
if (subOrganization.size()!=0)
for (OrganizationComponent organization : subOrganization) {
organization.print();
}
}
}
/**
* 表示叶子结点的组织类
*/
public class OrganizationLeaf extends OrganizationComponent {
public OrganizationLeaf(String name, String desc) {
super(name, desc);
}
@Override
protected void print() {
System.out.println(getName()+"("+getDesc()+")");
}
}
public class Demo {
public static void main(String[] args) {
OrganizationComponent university= new OrganizationComposite("桂林电子科技大学","广西小清华");
OrganizationComponent college = new OrganizationComposite("--海洋信息工程学院","北海校区东校区");
OrganizationComponent college2 = new OrganizationComposite("--信息职业技术学院","北海校区西校区");
OrganizationComponent department = new OrganizationLeaf("----计算机科学与技术系","修电脑的");
OrganizationComponent department2 = new OrganizationLeaf("----物流管理系","送快递的");
OrganizationComponent department3 = new OrganizationLeaf("----电子信息工程系","装宽带的");
OrganizationComponent department4 = new OrganizationLeaf("----经济与管理系","管账的");
university.add(college);
university.add(college2);
college.add(department);
college.add(department2);
college2 .add(department3);
college2.add(department4);
university.print();
}
}
打印结果是一样的,就不放了,可以看到,改进版的组合模式很好的解决了控制权限的问题,表示叶子结点的组织类没有权限使用add方法,使用就回抛异常,这种改进版叫透明式也是因为这个,因为抽象结点的方法对所有他的子类可见。
那么如果我不想让抽象的方法对子类可见,从而避免因无权使用抛异常呢?好解决,因为还有一种安全式改进。
改进组合模式(安全式)
安全式很好理解,它只是将抽象结点里的方法下沉到非叶子结点中,抽象结点中只保留共有方法。
具体变动主要在抽象结点和非叶子结点中
public abstract class OrganizationComponent {
private String name;
private String desc;
protected ArrayList<OrganizationComponent> subOrganization;
public OrganizationComponent(String name, String desc) {
this.name = name;
this.desc = desc;
}
protected abstract void print();
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
public class OrganizationComposite extends OrganizationComponent {
public OrganizationComposite(String name, String desc) {
super(name, desc);
subOrganization = new ArrayList<>();
}
public void add(OrganizationComponent component) {
subOrganization.add(component);
}
public void del(OrganizationComponent component) {
subOrganization.remove(component);
}
@Override
protected void print() {
System.out.println(getName()+"("+getDesc()+")");
if (subOrganization.size()!=0)
for (OrganizationComponent organization : subOrganization) {
organization.print();
}
}
}
public class Demo {
public static void main(String[] args) {
OrganizationComposite university= new OrganizationComposite("桂林电子科技大学","广西小清华");
OrganizationComposite college = new OrganizationComposite("--海洋信息工程学院","北海校区东校区");
OrganizationComposite college2 = new OrganizationComposite("--信息职业技术学院","北海校区西校区");
OrganizationComponent department = new OrganizationLeaf("----计算机科学与技术系","修电脑的");
OrganizationComponent department2 = new OrganizationLeaf("----物流管理系","送快递的");
OrganizationComponent department3 = new OrganizationLeaf("----电子信息工程系","装宽带的");
OrganizationComponent department4 = new OrganizationLeaf("----经济与管理系","管账的");
university.add(college);
university.add(college2);
college.add(department);
college.add(department2);
college2 .add(department3);
college2.add(department4);
university.print();
}
}
这样一来,表示叶子结点的组织类连add方法都没有了,就不会因为没权限抛异常了。
注意,这两种改进版组合模式不会因为增加了权限就丢失了扩展性,还是以对系别下增加专业为例,此时只需将专业用表示叶子结点的组织创建对象,用非叶子结点表示的组织创建系别在对专业添加即可,还是很灵活滴~
码了这么多字和代码,可能一遍看下来会有点懵逼,具体怎么理解还是需要看你自己怎么去想,我写的这些不是绝对,你可以参考我的解释我的例子,然后通过你的理解去实现一个自己的例子,形成一套你自己对于组合模式的看法,毕竟实践出真知嘛~