行为型模式(中)
- 行为型模式共11种,如下所示,本文介绍中间3种:观察者模式、迭代子模式和命令模式。
一、观察者模式
1.1 定义
- 观察者模式定义了一种对象之间的依赖关系,比如有很多对象a,b,c… 依赖于对象V,对象V变化后需要通知到前面的a,b,c等对象。并且又想让他们之间的耦合度尽量低,此时就可以考虑使用观察者模式。
1.2 优点和使用场景
- 优点:使用观察者模式可以让存在依赖关系之间的类尽量解耦。
- 场景:平时使用不多,估计在框架中用的多
1.3 实现
- 观察者模式中会定义一个被观察的角色(Subject)和若干个观察角色(Observer)。只要被观察者改变,就会通知到观察者,在被观察者中持有全部观察者的引用以便必要时给予通知。
- 假设我们的场景是这样的,被观察者就是学校的通知,通知会发布假期信息,一旦放假,所有的观察者(学生,老师,家长就会被通知)
1.3.1 被观察者Notic
public interface Notice {
void notice();
void addObserver(Observer observer);
}
public class SchoolNotice implements Notice {
ArrayList<Observer> observers = new ArrayList();
@Override
public void notice() {
for (Observer observer : observers) {
observer.getNotic();
}
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
public List<Observer> getObservers() {
if (observers != null) {
return observers;
}
return Collections.EMPTY_LIST;
}
public boolean removeObserver(Observer observer) {
if (observers != null && observers.contains(observer)) {
return observers.remove(observer);
}
return false;
}
}
1.3.2 观察者Observer
- Observer也是一个接口,定义了主体方法,不同的观察者实现类可以有不同的行为
public interface Observer {
void getNotic();
}
1.3.3 观察者实现类
public class Parent implements Observer {
@Override
public void getNotic() {
System.out.println("家长收到通知,安排假期活动...");
}
}
public class Student implements Observer {
@Override
public void getNotic() {
System.out.println("学生收到通知,很高兴...");
}
}
public class Teacher implements Observer {
@Override
public void getNotic() {
System.out.println("老师收到通知,可以休息一阵子...");
}
}
1.3.4 测试
public class ObserverTest {
public static void main(String[] args) {
Observer student = new Student();
Observer teacher = new Teacher();
Observer parent = new Parent();
Notice schoolNotice = new SchoolNotice();
schoolNotice.addObserver(student);
schoolNotice.addObserver(teacher);
schoolNotice.addObserver(parent);
schoolNotice.notice();
}
}
学生收到通知,很高兴...
老师收到通知,可以休息一阵子...
家长收到通知,安排假期活动...
- 我们看到被观察者发生变化之后,所有的观察者都会收到一个响应,并且二者之间没有太多的耦合,假如要增加观察者,也比较容易。如果在使用场景中,一个组件类的变化需要很多组件同步发生一定的变化,就可以考虑使用观察者模式。
二、迭代子模式
2.1 定义
- 参考文章[1]中有关于迭代子模式的详细介绍,写的非常好,可以直接阅读,不过看到网上很多资料都是这一篇,好像是摘自书上。因此这一节我就不对迭代子模式本身做介绍了,我们看看JDK源码中的集合框架中对迭代子模式的应用。
2.2 优点和使用场景
- 优点:解耦;几乎所有的设计模式都围绕这一点在做工作,迭代子模式可以将聚集对象和客户端之间解耦,聚集对象专注于对象的存储方式,迭代类专注于对聚集元素的遍历,后者可以有不同的实现算法,对前者和客户端都是透明的。
- 场景:JDK集合接口
2.3 实现
- 这部分不给案例了,直接看看JDK源码的设计,案例可以阅读参考文章[1]。
设计模式角色 | 作用 |
---|
抽象迭代子(Iterator)角色 | 此抽象角色定义出遍历元素所需的接口。 |
具体迭代子(ConcreteIterator)角色 | 此角色实现了Iterator接口,并保持迭代过程中的游标位置。 |
聚集(Aggregate)角色 | 此抽象角色给出创建迭代子(Iterator)对象的接口。 |
具体聚集(ConcreteAggregate)角色 | 实现了创建迭代子(Iterator)对象的接口,返回一个合适的具体迭代子实例。 |
客户端(Client)角色 | 持有对聚集及其迭代子对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。 |
2.3.1 Iterable
- Iterable是抽象迭代子角色; 定义出遍历元素所需的接口
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
2.3.2 Collection
- Collection是聚集(Aggregate)角色; 给出创建迭代子(Iterator)对象的接口。
public interface Collection<E> extends Iterable<E> {
Iterator<E> iterator();
}
2.3.3 AbstractList.Itr
- AbstractList.Itr是具体迭代子(ConcreteIterator)角色,实现了Iterable中定义的遍历元素的接口。他是AbstractList的内部类(迭代子是聚集的内部类,这属于黑箱聚集与内禀迭代子),Itr可以自由访问聚集的元素,。
private class Itr implements Iterator<E> {
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
2.3.4 ArrayList
- ArrayList是具体聚集(ConcreteAggregate)角色,实现了Collection中定义的创建迭代子(Iterator)对象的接口iterator(),返回一个合适的具体迭代子实例。
public Iterator<E> iterator() {
return new Itr();
}
2.3.5 客户端
- 客户端就是我们的调用代码,持有聚集角色和迭代子对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。
public class IteratorPatternTest {
static Random random = new Random();
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(100));
}
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer result = iterator.next();
System.out.print(result + " ");
if (result > 50 && result < 70){
iterator.remove();
}
}
System.out.println();
Iterator<Integer> iterator1 = list.iterator();
while (iterator1.hasNext()) {
System.out.print(iterator1.next() + " ");
}
}
}
输出:
95 69 59 22 62 38 97 0 72 80
95 22 38 97 0 72 80
- 不过这里尤其注意的是ConcurrentModificationException异常和Fail Fast机制,如果放开CommentA或者CommentB就会抛出该异常,不能通过非迭代器提供的方法修改聚集的内容,但是后面通过 iterator.remove()则是可以的。
- 关于:Fail Fast
如果一个算法开始之后,它的运算环境发生变化,使得算法无法进行必需的调整时,这个算法就应当立即发出故障信号。这就是FailFast的含义。如果
聚集对象的元素在一个动态迭代子的迭代过程中发生变化时,迭代过程会受到影响而变得不能自恰。这时候,迭代子就应当立即抛出一个异常。这种迭
代子就是实现了Fail Fast功能的迭代子。
在Itr类的源代码中可以看到,方法checkForComodification()会检查聚集的内容是否刚刚被外界直接修改过(不是通过迭代子提供的方法修改的)。如果
在迭代开始后,聚集的内容被外界绕过迭代子对象而直接修改的话,这个方法会立即抛出ConcurrentModificationException()异常。这就是说,
AbstractList.Itr迭代子是一个Fail Fast的迭代子。
三、命令模式
3.1 定义
- 将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。参考文章[2]中有关于命令模式的详细介绍,可以直接阅读。这一节不对命令模式本身做介绍了,我们看看JDK源码中的Executor对命令模式的应用。
3.2 优点和使用场景
- 有点:降低了系统耦合度。 新的命令可以很容易添加到系统中去。通过命令实际上对任务进行了一定程度的规范与抽象只要符合该规范即可递交命令,排除了其他的耦合
- 场景:java Concurrency Executor execute() 方法
3.3 实现
- 这部分不给案例了,直接看看JDK源码java Concurrency Executor execute() ,案例可以阅读参考文章[2]。
设计模式角色 | 作用 |
---|
Command(命令抽象类) | 定义一组可以执行的操作接口。 |
ConcreteCommand(具体命令类) | 它持有Receiver的引用,针对不同的命令执行具体的操作方法。 |
Receiver(接受者) | 定义执行者统一的接口,它可以是接口,也可以是具体实现类。 |
Invoker(调用类) | 它持有Command的引用,执行具体命令类。 |
3.3.1 Runnable接口
- Runnable接口是JDK线程对象需要实现的接口,它就是前面的Command(命令抽象类):定义一组可以执行的操作接口。
public interface Runnable {
public abstract void run();
}
3.3.2 MyRunnableImpl
- MyRunnableImpl代表具体的线程对象,他就是具体的命令类
3.3.3 Executor接口
- Executor接口是JDK线程执行框架的顶层接口,它就是前面的Invoker(调用者的角色),不过他说一个顶层接口,具体的调用者是它的各种子类
public interface Executor {
void execute(Runnable command);
}
3.3.4 ThreadPoolExecutor线程池
- ThreadPoolExecutor线程池是Executor接口实现类, 他可以提交命令,Receiver(接受者)
- ThreadPoolExecutor线程池是Executor接口实现类, 他可以执行命令,这里对应Invoker(调用类)
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
- ThreadPoolExecutor#addWorker
private boolean addWorker(Runnable firstTask, boolean core) {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
}
}
- 由此我们将命令抽象类(Command->Runnable)的一个具体实现(ConcreteCommand->MyRunnableImpl)提交给接受者(Receiver->ThreadPoolExecutor线程池),并由调用类(Invoker->ThreadPoolExecutor线程池)执行。
四、参考