海口网站推广公司,水墨画风格网站,网站开发软硬件配置,网站备案承诺书填写案例引入
学校院系展示
编写程序展示一个学校院系结构: 需求是这样#xff0c;要在一个页面中展示出学校的院系组成#xff0c;一个学校有多个学院#xff0c;一个学院有多个系 【传统方式】
将学院看做是学校的子类#xff0c;系是学院的子类#xff0c;小的组织继承大…案例引入
学校院系展示
编写程序展示一个学校院系结构: 需求是这样要在一个页面中展示出学校的院系组成一个学校有多个学院一个学院有多个系 【传统方式】
将学院看做是学校的子类系是学院的子类小的组织继承大的组织
分析 在一个页面中展示出学校的院系组成一个学校有多个学院一个学院有多个系 因此这种方案不能很好实现管理操作比如对学院、系的添加删除遍历
【组合模式】
把学校、院、系都看做是组织结构他们之间没有继承的关系而是一个树形结构可以更好的实现管理操作
介绍
基本介绍
组合模式又叫部分整体模式描述部分和整体的关系它创建了对象组的树形结构将对象组合成树状结构以表示“整体-部分”的层次关系组合模式依据树形结构容器中可以放入内容也可以放入小容器小容器中又可以放入内容或者更小的容器来组合对象用来表示部分以及整体层次。组合模式可以使容器与内容具有一致性创造出递归结构组合模式属于结构型模式组合模式使得用户对单个对象和组合对象的访问具有一致性组合能让客户以一致的方式处理个别对象以及组合对象
使用场景
组合模式解决这样的问题当我们的要处理的对象可以生成一个树形结构而我们要对树上的节点和叶子进行操作时它能够提供一致的方式而不用考虑它是节点还是叶子 登场角色
Leaf(树叶)表示“内容”的角色里面不能放人其他对象即没有孩子其定义组合内元素的行为Composite(复合物)表示容器的角色可以在其中放入Leaf和Composite有一些对子部件的相关操作如增加、删除可能不具有叶子的某种行为Component使Leaf和Composite具有一致性的角色Composite是 Leaf和Composite的父类。Compnet是组合中对象声明接口在适当情况下实现所有类共有的接口默认行为用于访问和管理Component子部件Component 可以是抽象类或者接口Client使用Composite模式的角色 案例实现
案例1
类图 代码实现
【Component组织】
package com.test.composite;/**
* 组织如论是系、学院还是学校都属于组织
*/
public abstract class OrganizationComponent {/**
* 名字
*/
private String name;
/**
* 说明
*/
private String des;/**
* 为什么需要默认实现而不是写成抽象方法呢
* 因为叶子节点不需要实现add方法如果是抽象方法的话就要实现了有点多余
* param organizationComponent
*/
protected void add(OrganizationComponent organizationComponent) {
//默认实现抛出不支持操作异常
throw new UnsupportedOperationException();
}protected void remove(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}/**
* 构造器
* param name
* param des
*/
public OrganizationComponent(String name, String des) {
super();
this.name name;
this.des des;
}public String getName() {
return name;
}public void setName(String name) {
this.name name;
}public String getDes() {
return des;
}public void setDes(String des) {
this.des des;
}/**
* 打印方法, 做成抽象的, 子类都需要实现
*/
protected abstract void print();}【Composite大学】
package com.test.composite;import java.util.ArrayList;
import java.util.List;/**
* University 就是 Composite角色 , 可以管理College
*/
public class University extends OrganizationComponent {ListOrganizationComponent organizationComponents new ArrayListOrganizationComponent();/**
* 构造器
* param name
* param des
*/
public University(String name, String des) {
super(name, des);
}/**
* 重写add
* param organizationComponent
*/
Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}/**
* 重写remove
* param organizationComponent
*/
Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}Override
public String getName() {
return super.getName();
}Override
public String getDes() {
return super.getDes();
}/**
* print方法就是输出 University 包含的学院
*/
Override
protected void print() {
// 先输出学校的名字
System.out.println(-------------- getName() --------------);
// 遍历 organizationComponents其实就是遍历出学校的学院
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}}【Composite学院】
package com.test.composite;import java.util.ArrayList;
import java.util.List;public class College extends OrganizationComponent {/**
* 存储系
*/
ListOrganizationComponent organizationComponents new ArrayListOrganizationComponent();/**
* 构造器
* param name
* param des
*/
public College(String name, String des) {
super(name, des);
}Override
protected void add(OrganizationComponent organizationComponent) {
// 将来实际业务中Colleage 的 add 和 University add 不一定完全一样
organizationComponents.add(organizationComponent);
}Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}Override
public String getName() {
return super.getName();
}Override
public String getDes() {
return super.getDes();
}/**
* print方法就是输出学院包含的系
*/
Override
protected void print() {
System.out.println(-------------- getName() --------------);
// 遍历 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}}【Composite系】
package com.test.composite;public class Department extends OrganizationComponent {//没有子节点所以不用声明集合public Department(String name, String des) {
super(name, des);
}//add , remove 就不用写了因为他是叶子节点Override
public String getName() {
return super.getName();
}Override
public String getDes() {
return super.getDes();
}Override
protected void print() {
// 没有子节点不需要输入其他东西
System.out.println(getName());
}}【Client】
package com.test.composite;public class Client {public static void main(String[] args) {
//创建大学
OrganizationComponent university new University(清华大学, 中国顶级大学 );//创建大学的各个学院
OrganizationComponent computerCollege new College(计算机学院, 计算机学院 );
OrganizationComponent infoEngineerCollege new College(信息工程学院, 信息工程学院 );//创建各个学院下面的系(专业)
computerCollege.add(new Department(软件工程, 软件工程不错 ));
computerCollege.add(new Department(网络工程, 网络工程不错 ));
computerCollege.add(new Department(计算机科学与技术, 计算机科学与技术是老牌的专业 ));
infoEngineerCollege.add(new Department(通信工程, 通信工程不好学 ));
infoEngineerCollege.add(new Department(信息工程, 信息工程好学 ));//将学院加入到 学校
university.add(computerCollege);
university.add(infoEngineerCollege);//输出大学的各个组织
university.print();
}}【运行】
--------------清华大学--------------
--------------计算机学院--------------
软件工程
网络工程
计算机科学与技术
--------------信息工程学院--------------
通信工程
信息工程Process finished with exit code 0【只打印某个学院的组织结构】
computerCollege.print();【运行】
--------------计算机学院--------------
软件工程
网络工程
计算机科学与技术Process finished with exit code 0案例2
类图 代码实现
【ComponentEntry类】
package com.test.composite.Sample;/**
* 目录条目类 用来实现 File类 和 Directory类 的一致性
*/
public abstract class Entry {/**
* 获取名字
*
* return
*/
public abstract String getName();/**
* 获取大小
*
* return
*/
public abstract int getSize();/**
* 加入目录条目向文件夹中放入文件或者文件夹Directory类来具体实现
*
* param entry
* return
* throws FileTreatmentException
*/
public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}/**
* 为一览加上前缀并显示目录条目一览
*/
public void printList() {
printList();
}/**
* 为一览加上前缀
* protected修饰只能被子类调用
* param prefix
*/
protected abstract void printList(String prefix);/**
* 显示代表类的文字
*
* return
*/
public String toString() {
// 将文件名和文件大小一起显示出来
return getName() ( getSize() );
}
}方法的默认实现是抛异常一般都是这样做这样如果子类没有重写该方法的话就会抛异常
【Composite文件类】
package com.test.composite.Sample;/**
* 文件类
*/
public class File extends Entry {
private String name;
private int size;/**
* 构造方法 创建文件
*
* param name
* param size
*/
public File(String name, int size) {
this.name name;
this.size size;
}Override
public String getName() {
return name;
}Override
public int getSize() {
return size;
}Override
protected void printList(String prefix) {
// 直接写this会自动调用该类的toString()方法的
System.out.println(prefix / this);
}
}【Composite目录类】
package com.test.composite.Sample;import java.util.ArrayList;
import java.util.Iterator;public class Directory extends Entry {
/**
* 文件夹的名字
*/
private String name;
/**
* 文件夹中目录条目的集合
*/
private ArrayList directory new ArrayList();public Directory(String name) {
this.name name;
}Override
public String getName() {
return name;
}/**
* 获取大小计算子文件或文件夹的大小总和
* return
*/
Override
public int getSize() {
int size 0;
Iterator it directory.iterator();
while (it.hasNext()) {
Entry entry (Entry) it.next();
// 无论子条目是文件夹还是文件都可以直接调用其getSize()方法这就是“容器与内容一致性”的好处
// 如果entry是目录就会形成递归调用
size entry.getSize();
}
return size;
}/**
* 增加目录条目
* param entry
* return
*/
Override
public Entry add(Entry entry) {
directory.add(entry);
return this;
}/**
* 显示目录条目一览
* param prefix
*/
Override
protected void printList(String prefix) {
System.out.println(prefix / this);
Iterator it directory.iterator();
while (it.hasNext()) {
Entry entry (Entry) it.next();
// 也是递归调用
entry.printList(prefix / name);
}
}
}【自定义异常类】
package com.test.composite.Sample;/**
* 自定义异常类
*/
public class FileTreatmentException extends RuntimeException {
public FileTreatmentException() {
}public FileTreatmentException(String msg) {
super(msg);
}
}【主类】
package com.test.composite.Sample;public class Main {
public static void main(String[] args) {
try {
System.out.println(Making root entries...);
Directory rootdir new Directory(root);
Directory bindir new Directory(bin);
Directory tmpdir new Directory(tmp);
Directory usrdir new Directory(usr);
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File(vi, 10000));
bindir.add(new File(latex, 20000));
rootdir.printList();System.out.println();
System.out.println(Making user entries...);
Directory yuki new Directory(yuki);
Directory hanako new Directory(hanako);
Directory tomura new Directory(tomura);
usrdir.add(yuki);
usrdir.add(hanako);
usrdir.add(tomura);
yuki.add(new File(diary.html, 100));
yuki.add(new File(Composite.java, 200));
hanako.add(new File(memo.tex, 300));
tomura.add(new File(game.doc, 400));
tomura.add(new File(junk.mail, 500));
rootdir.printList();
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}【运行】
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)Process finished with exit code 0拓展
如何通过修改或者补充上面的代码来增加一个为 文件/目录 获取完整路径的功能如/root/usr/yuki/Composite.java
【Component】
添加一个记录父条目的变量和一个公共方法该方法不需要子类去重写因为实现逻辑都一样如果不一样的话就需要写成抽象方法
package com.test.composite.A1;public abstract class Entry {
protected Entry parent;public abstract String getName();public abstract int getSize();public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}public void printList() {
printList();
}protected abstract void printList(String prefix);public String toString() {
return getName() ( getSize() );
}/**
* 获取条目的完整路径
*
* return
*/
public String getFullName() {
StringBuffer fullname new StringBuffer();
Entry entry this;
do {
//需要将父条目的名字插到前面而不是append到后面
fullname.insert(0, / entry.getName());
entry entry.parent;
} while (entry ! null);
return fullname.toString();
}
}【Composite目录类】
当给目录加入元素时需要指定元素的父元素使用entry.parent this; 来实现
package com.test.composite.A1;import java.util.ArrayList;
import java.util.Iterator;public class Directory extends Entry {
private String name;
private ArrayList directory new ArrayList();
public Directory(String name) {
this.name name;
}
public String getName() {
return name;
}
public int getSize() {
int size 0;
Iterator it directory.iterator();
while (it.hasNext()) {
Entry entry (Entry)it.next();
size entry.getSize();
}
return size;
}
public Entry add(Entry entry) {
directory.add(entry);
entry.parent this;
return this;
}
protected void printList(String prefix) {
System.out.println(prefix / this);
Iterator it directory.iterator();
while (it.hasNext()) {
Entry entry (Entry)it.next();
entry.printList(prefix / name);
}
}
}组合模式在JDK的HashMap源码中的应用 Map 就是一个抽象的构建 (类似我们的Component)HashMap是一个中间的构建(Composite), 实现/继承了相关方法put, putAllNode 是 HashMap的静态内部类类似Leaf叶子节点, 该类没有put, putAll这些方法 static class NodeK,V implements Map.EntryK,V
组合模式总结
简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题具有较强的扩展性。当我们要更改组合对象时我们只需要调整内部的层次关系客户端不用做出任何改动方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节容易添加节点或者叶子从而创建出复杂的树形结构需要遍历组织机构或者处理的对象具有树形结构时非常适合使用组合模式要求较高的抽象性如果节点和叶子有很多差异性的话比如很多方法和属性都不一样不适合使用组合模式