自大学课程初识设计模式以来,就越发觉得有必要系统学习一下设计模式。
刚好在实习前准备期间课比较少,抽出一点时间整理一下记一些笔记,复制粘贴比较多。
笔记比较适合学习过设计模式的同学。
另外感谢刘伟博士,学习设计模式可以看刘伟博士的博客,很详尽。
(1) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
(2) 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
(3) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
Java SE 中的 AWT 和 Swing 包的设计就基于组合模式,在这些界面包中为用户提供了大量的容器构件(如 Container)和成员构件(如 Checkbox、Button 和 TextComponent等)
它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
但是这样的结构会出现一些问题,就是不够安全,因为叶子节点继承抽象类时也需要实现add、remove方法,但是大家也知道叶子节点是不可能使用这些方法的,所以需要在实现中使用抛出错误。
解决办法有下:
1.将叶子构件的 add()、remove() 等方法的实现代码移至 AbstractFile 类中,由 AbstractFile 提供统一的默认实现。我觉得这是比较好的方法,但是不知道为什么不经常用。
2.还有一种解决方法是在抽象构件 AbstractFile 中不声明任何用于访问和管理成员构件的方法,将访问方法都给容器节点,这样就会使得容器节点与客户端出现了耦合,不够透明。在实际应用中,安全组合模式的使用频率也非常高,在 Java AWT 中使用的组合模式就是安全组合模式。
(1) 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
(2) 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
(3) 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
(4) 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
Sunny 软件公司欲开发一个界面控件库,界面控件分为两大类,一类是单元控件,例如按钮、文本框等,一类是容器控件,例如窗体、中间面板等,试用组合模式设计该界面控件库。
题目还是比较明确的,在设计方面我多加了两个类作为单元控件的集合和容器控件的集合。
package com.joy;
public abstract class Controls {
public abstract void add(Controls child);
public abstract void remove(Controls child);
public abstract Controls getChild(int i);
public abstract void useControl();
}
package com.joy;
public class UnitControls extends Controls {
@Override
public void add(Controls child) {
try {
throw new Exception("没有该方法……");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void remove(Controls child) {
try {
throw new Exception("没有该方法……");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Controls getChild(int i) {
try {
throw new Exception("没有该方法……");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void useControl() {
System.out.println("使用了单元控件……");
}
}
package com.joy;
public class ButtonControl extends UnitControls {
@Override
public void useControl() {
System.out.println("使用了按钮控件……");
}
}
package com.joy;
public class TextViewControl extends UnitControls {
@Override
public void useControl() {
System.out.println("使用了文本框控件……");
}
}
package com.joy;
import java.util.ArrayList;
public class ContainerControls extends Controls {
ArrayList childList = new ArrayList();
@Override
public void add(Controls child) {
childList.add(child);
}
@Override
public void remove(Controls child) {
childList.remove(child);
}
@Override
public Controls getChild(int i) {
return childList.get(i);
}
@Override
public void useControl() {
for(Controls obj : childList)
{
obj.useControl();
}
}
}
package com.joy;
public class FormControl extends ContainerControls {
@Override
public void useControl() {
System.out.println("使用了窗体控件……");
super.useControl();
}
}
package com.joy;
public class MiddlewareControl extends ContainerControls {
@Override
public void useControl() {
System.out.println("使用了中间件控件……");
super.useControl();
}
}
package com.joy;
public class Client {
public static void main(String[] args) {
Controls b,tv,f,mw;
f=new FormControl();
mw=new MiddlewareControl();
b=new ButtonControl();
tv = new TextViewControl();
mw.add(b);
mw.add(tv);
f.add(mw);
f.useControl();
}
}
组合模式还是蛮典型的,因为它主要体现了面向抽象的原则,使得简化很多重复的工作,这让我想起了学操作系统的时候,设计文件系统的时候有思考建一个抽象的父类,但是当时并不知道自己使用了组合模式。