五、行为型模式
-
**行为模式的定义:**行为型模式是对不同的对象之间划分职责和算法的抽象化。行为型模式定义了系统中对象之间的交互与通信,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责,包括对系统中较为复杂的流程的控制。
-
行为模式的分类:
类行为型模式:使用继承关系在几个类之间分配行为,主要通过多态等方式来分配父类与子类的职责;
对象行为型模式:使用对象的聚合关联关系来分配行为,主要通过对象关联等方式来分配两个或多个类的职责。
5.1 命令模式
5.1.1 命令模式的定义
(描述对象之间的调用关系)
**1.模式动机:**将请求发送者和接收者完全解耦;发送者与接收者之间没有直接引用关系;发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求
**2.模式定义:**将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
5.1.2 命令模式的结构与分析
//调用者
public class Invoker {
private AbstractCommand ac;
public Invoker(AbstractCommand ac) {
this.ac = ac;
}
public void send() {
ac.execute();
}
}
//接收者
public class Receiver {
public void action() {
}
}
//抽象命令类
public abstract class AbstractCommand {
public abstract void execute();
}
//具体命令类
public class ConcreteCommand extends AbstractCommand {
private Receiver receiver = new Receiver();
@Override
public void execute() {
receiver.action();
}
}
-
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分开
-
将请求发送者和接收者完全解耦,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。
5.1.3 命令模式的案例
电视机是请求的接收者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应电视机的不同操作。抽象命令角色由一个命令接口来扮演,有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作:打开电视机、关闭电视机和切换频道。显然,电视机遥控器就是一个典型的命令模式应用实例。
public class TV {
public void open() {
System.out.println("打开电视");
}
public void change() {
System.out.println("切换频道");
}
public void close() {
System.out.println("关掉电视");
}
}
public interface Command {
public void execute();
}
public class OpenCommand implements Command{
private TV tv = new TV();
@Override
public void execute() {
tv.open();
}
}
public class CloseCommand implements Command{
private TV tv = new TV();
@Override
public void execute() {
tv.close();
}
}
public class ChangeCommand implements Command {
private TV tv = new TV();
@Override
public void execute() {
tv.change();
}
}
public class Control {
private Command openCommand;
private Command changeCommand;
private Command closeCommand;
public Control(Command openCommand, Command changeCommand, Command closeCommand) {
this.openCommand = openCommand;
this.changeCommand = changeCommand;
this.closeCommand = closeCommand;
}
public void open() {
openCommand.execute();
}
public void change() {
changeCommand.execute();
}
public void close() {
closeCommand.execute();
}
}
public class Main {
public static void main(String[] args) {
Command openCommand = new OpenCommand();
Command changeCommandCommand = new ChangeCommand();
Command closeCommand = new CloseCommand();
Control control = new Control(openCommand, changeCommandCommand, closeCommand);
control.open();
control.change();
control.close();
}
}
5.1.4 命令模式的优缺点
优点 | 缺点 |
---|---|
1.将系统的请求调用者和请求接收者解耦 | 1.使用命令模式可能会导致某些系统有过多的具体命令类 |
2.添加命令符合开闭原则 | |
3.可以比较容易地设计一个命令队列或宏命令(组合模式) | |
4.为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案 |
5.1.5 命令模式的适用场景
-
需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
-
需要在不同的时间指定请求、将请求排队和执行请求
-
需要支持命令的撤销(Undo)操作和恢复(Redo)操作
-
需要将一组操作组合在一起形成宏命令
5.2 迭代器模式
5.2.1 迭代器模式的定义
1.模式动机:如何访问一个聚合对象(用于存储多个对象)中的元素但又不需要暴露它的内部结构,还能提供多种不同的遍历方式
2.模式定义:提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示。
5.2.2 迭代器模式的结构与分析
-
聚合对象的两个职责:存储数据,聚合对象的基本职责;遍历数据,既是可变化的,又是可分离的
-
将遍历数据的行为从聚合对象中分离出来,封装在迭代器对象中,由迭代器来提供遍历聚合对象内部数据的行为,简化聚合对象的设计,更符合单一职责原则
-
迭代器对象使用了工厂模式的设计思想,聚合类可以看作工厂模式,迭代器是产品类
实现方法一:由于集合和数组在聚合类中,迭代器需要使用它们,因此迭代器作为内部类出现
public interface Iterator {
public void first();
public void next();
public boolean isLast();
public Object currentItem();
}
public abstract class Aggregate {
public abstract Iterator createIterator();
}
public class ConcreteAggregate extends Aggregate{
private String[] strings;
public ConcreteAggregate() {
strings = new String[]{"d", "e", "f"};
}
@Override
public Iterator createIterator() {
return new ConcreteIterator();
}
private class ConcreteIterator implements Iterator{
private int index = 0;
@Override
public void first() {
index = 0;
}
@Override
public void next() {
if (strings.length > index) {
index++;
} else {
throw new RuntimeException("越界");
}
}
@Override
public boolean isLast() {
return index == strings.length;
}
@Override
public Object currentItem() {
return strings[index];
}
}
}
实现方法二:可以在聚合类创建迭代器时,将自身传给迭代器并提供集合/数组的get方法,这样也可以实现
public interface Iterator {
public void first();
public void next();
public boolean isLast();
public Object currentItem();
}
public abstract class Aggregate {
public abstract Iterator createIterator();
}
public class ConcreteAggregate extends Aggregate{
private String[] strings;
public ConcreteAggregate() {
strings = new String[]{"a", "b", "c"};
}
public String[] getStrings() {
return strings;
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
}
public class ConcreteIterator implements Iterator{
private ConcreteAggregate aggregate;
private int index = 0;
public ConcreteIterator(ConcreteAggregate aggregate) {
this.aggregate = aggregate;
}
@Override
public void first() {
index = 0;
}
@Override
public void next() {
if (aggregate.getStrings().length > index) {
index++;
} else {
throw new RuntimeException("越界");
}
}
@Override
public boolean isLast() {
return index == aggregate.getStrings().length;
}
@Override
public Object currentItem() {
return aggregate.getStrings()[index];
}
}
public class Main {
public static void main(String[] args) {
Aggregate concreteAggregate = new ConcreteAggregate();
Iterator iterator = concreteAggregate.createIterator();
while (!iterator.isLast()) {
System.out.println(iterator.currentItem());
iterator.next();
}
}
}
5.2.3 迭代器模式的案例
电视机遥控器就是一个迭代器的实例,通过它可以实现对电视机频道集合的遍历操作,本实例我们将模拟电视机遥控器的实现。
- 迭代器类
public interface TVIterator {
public void setChannel(int i);
public Object currentChannel();
public void next();
public void previous();
public boolean isLast();
public boolean isFirst();
}
public class SkyworthIterator implements TVIterator {
private SkyworthTelevision skyworthTelevision;
private int index = 0;
public SkyworthIterator(SkyworthTelevision skyworthTelevision) {
this.skyworthTelevision = skyworthTelevision;
}
@Override
public void setChannel(int i) {
this.index = i;
}
@Override
public Object currentChannel() {
return skyworthTelevision.getObj()[this.index];
}
@Override
public void next() {
if (this.index < skyworthTelevision.getObj().length) {
this.index++;
}
}
@Override
public void previous() {
if (this.index > 0) {
this.index--;
}
}
@Override
public boolean isLast() {
return this.index == skyworthTelevision.getObj().length ;
}
@Override
public boolean isFirst() {
return this.index == 0;
}
}
public class TCLIterator implements TVIterator{
private TCLTelevision tclTelevision;
private int index = 0;
public TCLIterator(TCLTelevision tclTelevision) {
this.tclTelevision = tclTelevision;
}
@Override
public void setChannel(int i) {
this.index = i;
}
@Override
public Object currentChannel() {
return tclTelevision.getObj()[this.index];
}
@Override
public void next() {
if (this.index < tclTelevision.getObj().length) {
this.index++;
}
}
@Override
public void previous() {
if (this.index > 0) {
this.index--;
}
}
@Override
public boolean isLast() {
return this.index == tclTelevision.getObj().length;
}
@Override
public boolean isFirst() {
return this.index == 0;
}
}
- 电视类
public interface Television {
public TVIterator createIterator();
public Object[] getObj();
}
public class SkyworthTelevision implements Television{
private Object[] obj = {"CCTV-1","CCTV-2","CCTV-3","CCTV-4","CCTV-5","CCTV-6","CCTV-7","CCTV-8"};
@Override
public TVIterator createIterator() {
return new SkyworthIterator(this);
}
@Override
public Object[] getObj() {
return obj;
}
}
public class TCLTelevision implements Television{
private Object[] obj = {"CCTV-1","CCTV-2","CCTV-3","CCTV-4","CCTV-5","CCTV-6","CCTV-7","CCTV-8"};
@Override
public TVIterator createIterator() {
return new TCLIterator(this);
}
@Override
public Object[] getObj() {
return obj;
}
}
- 客户端
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil {
public static Object getBean() {
try {
//获取XML
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document parse = db.parse(new File("src/main/java/com/tyut/text3/example1/tv.xml"));
String tv = parse.getElementsByTagName("tv").item(0).getFirstChild().getNodeValue();
//获取对象
Class<?> aClass = Class.forName(tv);