组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
优点:
1、可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。
2、客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。
3、定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
4、更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。
缺点: 1、使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联
2、在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
Employee.java
<pre class="prettyprint">
import java.util.ArrayList;
import java.util.List;
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
//构造函数
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}
//步骤 2
//使用 Employee 类来创建和打印员工的层次结构。
CompositePatternDemo.java
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
}
另一个:文件夹
首先是文件类:File.java
public abstract class File {
String name;
public File(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void display();
}
然后是文件夹类:Folder.java,该类包含对文件的增加、删除和浏览三个方法
public class Folder extends File{
private List<File> files;
public Folder(String name){
super(name);
files = new ArrayList<File>();
}
/**
* 浏览文件夹中的文件
*/
public void display() {
for(File file : files){
file.display();
}
}
/**
* @desc 向文件夹中添加文件
* @param file
* @return void
*/
public void add(File file){
files.add(file);
}
/**
* @desc 从文件夹中删除文件
* @param file
* @return void
*/
public void remove(File file){
files.remove(file);
}
}
然后是三个文件类:TextFile.java、ImageFile.java、VideoFile.java
TextFile.java
public class TextFile extends File{
public TextFile(String name) {
super(name);
}
public void display() {
System.out.println("这是文本文件,文件名:" + super.getName());
}
}
ImageFile.java
public class ImagerFile extends File{
public ImagerFile(String name) {
super(name);
}
public void display() {
System.out.println("这是图像文件,文件名:" + super.getName());
}
}
VideoFile.java
复制代码
public class VideoFile extends File{
public VideoFile(String name) {
super(name);
}
public void display() {
System.out.println("这是影像文件,文件名:" + super.getName());
}
}
最后是客户端
public class Client {
public static void main(String[] args) {
/**
* 我们先建立一个这样的文件系统
* 总文件
*
* a.txt b.jpg c文件夹
* c_1.text c_1.rmvb c_1.jpg
*
*/
//总文件夹
Folder zwjj = new Folder("总文件夹");
//向总文件夹中放入三个文件:1.txt、2.jpg、1文件夹
TextFile aText= new TextFile("a.txt");
ImagerFile bImager = new ImagerFile("b.jpg");
Folder cFolder = new Folder("C文件夹");
zwjj.add(aText);
zwjj.add(bImager);
zwjj.add(cFolder);
//向C文件夹中添加文件:c_1.txt、c_1.rmvb、c_1.jpg
TextFile cText = new TextFile("c_1.txt");
ImagerFile cImage = new ImagerFile("c_1.jpg");
VideoFile cVideo = new VideoFile("c_1.rmvb");
cFolder.add(cText);
cFolder.add(cImage);
cFolder.add(cVideo);
//遍历C文件夹
cFolder.display();
//将c_1.txt删除
cFolder.remove(cText);
System.out.println("-----------------------");
cFolder.display();
}
}
组合模式最重要的一点就是存在包含与被包含的关系,并且可以忽略掉包含被包含者之间的区别。强调两者的相同点,忽略两者的不同点。
比如ceo和职员,强调的是他们都是员工,而不是他们的职位差异,工作差异,文件夹和文本文件,强调的是都是文件而非其他。