今天的主题是设计模式的最后一章 - 行为型设计模式,还是围绕着开源项目来展开。由于行为型模式比较多,一共有11个,所以分成2章来写。
目录:
- 策略模式的简介和应用
- 模板方法模式的简介和应用
- 观察者模式的简介和应用
- 迭代器模式的简介和应用
- 责任链模式的简介和应用
1. 策略模式的简介和应用
- 1.1 简介
策略模式的思想是针对一组算法,将每一种算法都封装到具有共同接口的独立的类中,从而使它们可以相互替换。策略模式的最大特点是使得算法可以在不影响客户端的情况下发生变化,从而改变不同的功能。
策略模式体现了面向对象程序设计中非常重要的两个原则:
- 封装变化的概念。
- 编程中使用接口,而不是使用具体的实现类 (面向接口编程)。
- 1.2 demo
/**
* 策略接口
*/
private interface IStrategy {
int calculate(int num1, int num2);
}
/**
* 具体策略1
*/
private final class AddStrategy implements IStrategy {
@Override
public int calculate(int num1, int num2) {
return num1 + num2;
}
}
/**
* 具体策略2
*/
private final class SubtractStrategy implements IStrategy {
@Override
public int calculate(int num1, int num2) {
return num1 - num2;
}
}
/**
* 环境角色
*/
private final class Env {
private IStrategy strategy;
public Env(IStrategy strategy) {
this.strategy = strategy;
}
public int calculate(int num1, int num2) {
return strategy.calculate(num1, num2);
}
}
public static void main(String[] args) {
TestBehaviorDesignModel model = new TestBehaviorDesignModel();
Env env = model.new Env(model.new AddStrategy());
System.out.println(env.calculate(1, 2));
}
执行输出:3
是不是很简单,而且就是面向接口编程,所以不要固化在某种设计模式上,能让代码方便维护、管理、拓展的模式才是好模式。
- 1.3 TreeSet 中的策略模式
TreeSet 支持外比较器排序,TreeSet 仅仅知道它接收的是一个 Comparator 接口类型,但是具体是哪种实现类,TreeSet 并不关心。实现类在真正的传入TreeSet 之前,TreeSet 本身是不知道的,所以我们可以自己去实现 Comparator 接口,然后在实现类里面去封装好我们自己的规则 (这里的规则你可以当做是算法)。比如说我们要实现对一个集合的元素排序,但是到底是要升序排序还是降序排序,这个完全由我们来控制。我们可以把这种变化的内容封装到自己的实现类中,真正运行的时候才知道具体的实现。
关于比较器,可以看看我这篇文章:Java篇 - 一招教你使用TreeMap
TreeSet 支持传入外比较器的构造器:
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
Comparator 接口,具体由开发人员去实现:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
default <U> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
return thenComparing(comparing(keyExtractor, keyComparator));
}
// ......
}
2. 模板方法模式的简介和应用
- 2.1 简介
模版方法模式定义一个算法中的操作框架,而将一些步骤延迟到子类中。使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
模板方法模式非常简单,仅仅使用了 Java 的继承机制,但它是一个非常广泛的模式,它的方法分为两类:
- 基本方法:是由子类实现的方法,并且在模板方法被调用。
- 模板方法:可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调用,完成固定的逻辑 (一般都加上 final 关键字,防止被重写)。
- 2.2 demo
private abstract class AbstractClz {
protected abstract void method1();
protected abstract void method2();
protected final void templateMethod() {
method1();
method2();
}
}
private final class Clz1 extends AbstractClz {
@Override
protected void method1() {
System.out.println("Clz1 method1");
}
@Override
protected void method2() {
System.out.println("Clz1 method2");
}
}
private final class Clz2 extends AbstractClz {
@Override
protected void method1() {
System.out.println("Clz2 method1");
}
@Override
protected void method2() {
System.out.println("Clz2 method2");
}
}
- 2.3 Android Context 的模版方法模式
模版方法模式在每个项目中几乎都会用,这也是面向对象继承特性的一个运用。Android 中的 Context 是一个抽象类,它有几个实现类:Application, Activity, Service, ContextThemeWrapper 等。
看看 Context 类的定义:
public abstract class Context {
@ViewDebug.ExportedProperty(deepExport = true)
public abstract Resources.Theme getTheme();
public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(attrs);
}
public final TypedArray obtainStyledAttributes(
@StyleRes int resid, @StyleableRes int[] attrs) throws Resources.NotFoundException {
return getTheme().obtainStyledAttributes(resid, attrs);
}
// ......
}
再来看看它的一个实现类 ContextThemeWrapper (父类 ContextWrapper 是 Context 的装饰器类):
public class ContextThemeWrapper extends ContextWrapper {
@Override public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
// ......
}
3. 观察者模式的简介和应用
- 3.1 简介
观察者模式是在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。
- 3.2 demo
以前在 Android 中还没有 EventBus 这种事件总线的框架,所以一般监听状态变化更新数据使用的是自己写的观察者,不过性能没有那么好。
public interface ObserverListener<Param> {
/**
* 通知所有注册者
*
* @param action 注册的action
* @param params 回传的参数
*/
void notifyAll(String action, Param... params);
}
/**
* 观察者
*
* <p>实现就是一个轻量级支持并发的单例类,内部维护一个链表,通过register和unregister往链表中添加和
* 移除观察对象。注意,因为实现原理是在某个场景下通知所有的被观察者,即遍历链表,执行链表里的回调方法,
* 这个是串形过程,如果中间某个回调执行时间过长,会影响整个的观察者速度。所以对于响应速度要求过高的场景,
* 最好不要使用observer。同时observer太过于解耦,在一定程度上对于软件的设计是有破坏性的。</p>
* <p>那么为什么当初不选择EventBus等做了性能优化的事件总线呢,主要是使用场景较少,而且作为一个sdk,应该
* 尽量少的依赖第三方库,否则会造成方法数增多,同时还得维护和接入方的相同依赖库的版本统一。</p>
*
* @author kzw 17/3/20
*/
public class Observer<Listener extends ObserverListener<Param>, Param> {
private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
private Observer() {
}
/**
* <p>
* 使用延迟加载引入的同步关键字会降低系统性能,而使用内部类来维护单例的实例,
* 当MtbConfigObserver被加载时,其内部类并不会被初始化,所以可以保证MtbConfigObserver
* 类被载入JVM时,不会初始化单例类,而只有调用 @see #getInstance()方法时,才会加载
* MtbConfigObserverHolder,从而初始化instance,而且实例的建立是在类加载时完成,所以
* 天生对多线程友好。
* </p>
*/
private static final class ObserverHolder {
private static Observer instance = new Observer();
}
/** 避免反序列化 */
private Object readResolve() throws ObjectStreamException {
return getInstance();
}
public static Observer getInstance() {
return ObserverHolder.instance;
}
@SuppressWarnings("unchecked")
public final void fireUpdate(String action, final Param... param) {
if (mListeners.isEmpty()) {
return;
}
synchronized (this) {
for (Listener listener : mListeners) {
if (listener != null) {
listener.notifyAll(action, param);
}
}
}
}
public final void register(final Listener listener) {
if (listener == null) {
return;
}
synchronized (this) {
mListeners.add(listener);
}
}
public final void unregister(final Listener listener) {
if (listener == null) {
return;
}
synchronized (this) {
mListeners.remove(listener);
}
}
}
- 3.3 EventBus 的观察者模式
EventBus 是一种用于 Android 的事件发布-订阅总线,由 GreenRobot 开发。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。流程就是订阅,收到通知,取消订阅。
public class EventBus {
public void register(Object subscriber) {
// ...
}
public synchronized void unregister(Object subscriber) {
// ...
}
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
}
然后调用方在某个时机调用 post 发送通知,只要订阅过的 Activity 或者 Fragment 就可以收到通知,如:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Events.register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
Events.unregister(this);
}
@SuppressWarnings("unchecked")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(final org.web3j.protocol.core.methods.response.Transaction event) {
if (!Requires.isNull(event)) {
ensurePushExecutor();
mPushExecutor.execute(new Runnable() {
@Override
public void run() {
TransactionMessage message = new TransactionMessage();
message.transaction = event;
if (CTXCPushFactory.getPusher(PushType.PUSH_TYPE_TRANSACTION).handleMessage(message, mGlobalWallet)) {
AppUtils.runAtFrontOfQueue(new Runnable() {
@Override
public void run() {
loadData();
}
});
}
}
});
}
}
4. 迭代器模式的简介和应用
- 4.1 定义
迭代器模式 (Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
迭代器模式的优点有:
- 简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象。而是对于 hash 表来说,用户遍历起来就比较麻烦了,引入了迭代器方法后,用户用起来就简单的多了。
- 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
- 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。
迭代器模式的缺点:
- 对于比较简单的遍历 (像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像 ArrayList,我们宁可愿意使用 for 循环和 get 方法来遍历集合。
总的来说:迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像 Java 中的 Collection,List、Set、Map 等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。
- 4.2 ArrayList 中的迭代器模式
迭代器接口:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
ArrayList 的 Iterator:
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
return new ListIterator<E>() {
public boolean hasNext() {
// ...
}
@SuppressWarnings("unchecked")
public E next() {
// ...
}
public boolean hasPrevious() {
// ...
}
@SuppressWarnings("unchecked")
public E previous() {
// ...
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
// ...
}
public int nextIndex() {
// ...
}
public int previousIndex() {
// ...
}
public void remove() {
// ...
}
public void set(E e) {
// ...
}
public void add(E e) {
// ...
}
};
}
5. 责任链模式的简介和应用
- 5.1 简介
顾名思义,责任链模式 (Chain of Responsibility Pattern) 为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式,在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
- 5.2 demo
模拟一个村、镇、县的责任链关系请求。
protected abstract class Handler {
protected Handler next;
public abstract void handleRequest(String value);
public Handler next() {
return this.next;
}
public void setNext(Handler next) {
this.next = next;
}
}
private final class VillageHandler extends Handler {
@Override
public void handleRequest(String value) {
if ("village".equals(value)) {
System.out.println("VillageHandler: handled~");
} else {
System.out.println("VillageHandler: pass~");
this.next.handleRequest(value);
}
}
}
private final class TownHandler extends Handler {
@Override
public void handleRequest(String value) {
if ("town".equals(value)) {
System.out.println("VillageHandler: handled~");
} else {
System.out.println("Town: pass~");
this.next.handleRequest(value);
}
}
}
private final class CountyHandler extends Handler {
@Override
public void handleRequest(String value) {
if ("county".equals(value)) {
System.out.println("County: handled~");
} else if (this.next == null) {
System.out.println("no next Handler, this request can not be handle~");
} else {
System.out.println("County: pass~");
this.next.handleRequest(value);
}
}
}
public static void main(String[] args) {
TestBehaviorDesignModel model = new TestBehaviorDesignModel();
Handler villageHandler = model.new VillageHandler();
Handler townHandler = model.new TownHandler();
Handler countyHandler = model.new CountyHandler();
villageHandler.setNext(townHandler);
townHandler.setNext(countyHandler);
System.out.println("test county request:");
villageHandler.handleRequest("county");
System.out.println("\ntest city request:");
villageHandler.handleRequest("city");
}
执行输出:
test county request:
VillageHandler: pass~
Town: pass~
County: handled~test city request:
VillageHandler: pass~
Town: pass~
no next Handler, this request can not be handle~
是不是有点像链表的方式,一环扣一环。
- 5.3 Android 事件传递机制的责任链模式
View 的事件传递层级:
Activity –> PhoneWindow –> decorView –> ViewGroup –> … –> View.
Android 的事件分发机制比较复杂,对其感兴趣的同学可以看看这篇文章:一文读懂Android View事件分发机制
总结就是:一条责任链,事件分发,谁消费请求就停止请求。