Android设计模式之责任链模式

一年逛一次设计模式,每次都有新感觉!

Posted by 夏敏的博客 on July 20, 2017

去年这时候就在写设计模式的文章了,但是没写完,有好几个没有写,现在打算继续写,争取把设计模式写完,本来这些很简单的模式我打算一篇写几个的,但是还是遵从单一职责原则,每篇文章写一个比较好.

责任链模式定义

责任链模式很简单,我们看它的英文名”Iterator Pattern”,我觉得直接叫他迭代器模式拉倒了.就是一个请求沿着一条“链”传递,直到该“链”上的某个处理者处理它为止。这种情况在编程中很常见,当我们的服务对象有多个的时候,很容易遇到.

责任链模式应用场景

一个请求可以被多个处理者处理或处理者未明确指定时,具体由哪个对象处理由运行时动态的决定.

责任链模式简单使用

针对上面的说的应用场景,可以很简单的模拟出场景,我有一个事情有3个对象有权处理,具体处理要看这三个对象是否处理了.

我正在听JJ的”末班车”,想到了个例子,我们要上班,小明家门口的站牌有三路公交车711,712,718都可以到公司,那么小明站到站台后,这三路公交车就构成了上面的情况,多个处理者,且处理情况动态决定.那这种代码的编写是什么情况呢?

我们定义小明具有上车功能,车具有接人功能.

/** 上车接口 */
interface GetOn {
	void getOn();
}

/** 人,具有上车属性 */
class Person implements GetOn {
	public void getOn() {

	}
}

/** 接人接口,接想上车的人 */
interface Pickup {
	boolean pickUp(GetOn on);
}

定义三辆车

class Car711 implements Pickup {
	public boolean pickUp(GetOn on) {
		System.out.println("711车没满,能上车");
		return true;
	}
}

class Car712 implements Pickup {
	public boolean pickUp(GetOn on) {
		System.out.println("712车满,上车失败");
		return false;
	}
}

class Car718 implements Pickup {
	public boolean pickUp(GetOn on) {
		System.out.println("718车没满,能上车");
		return false;
	}
}

这时候模拟一下运行情况,假设是712先来的.

public static void main(String[] args) {
  GetOn xiaoming = new Person();
  Car711 car711 = new Car711();
  Car712 car712 = new Car712();
  Car718 car718 = new Car718();

  if (car712.pickUp(xiaoming)) {
    System.out.println("小明上712了");
  } else if (car711.pickUp(xiaoming)) {
    System.out.println("小明上711了");
  } else if (car718.pickUp(xiaoming)) {
    System.out.println("小明上718了");
  }
}

结果为:

712车满,上车失败
711车没满,能上车
小明上711了

这就是一个简单的责任链模式demo,但事实上我们程序肯定不是这么写的,车可能有很多,然而都注册进一个表里面的.情况多了,就发现这么设计的优点真的好.

责任链模式源码应用

Android源码设计里面的触摸事件的分发就是采用的责任链模式,一层一层的分发下去,直到有人处理了,就终止了.

我们可以看看ViewGroup里面的事件分发.

public boolean onInterceptTouchEvent(MotionEvent ev) {
   return false;
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
    boolean handled = false;
    if (onFilterTouchEventForSecurity(ev)) {
        ....
        /*
         * 如果事件未被取消并未被拦截
         */
        if (!canceled && !intercepted) {
            /*
             * 如果事件为起始事件
             */
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                // 省掉部分逻辑…………

                final int childrenCount = mChildrenCount;

                /*
                 * 如果TouchTarget为空并且子元素不为0
                 */
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);

                    final View[] children = mChildren;

                    final boolean customOrder = isChildrenDrawingOrderEnabled();

                   /*
                    * 遍历子元素
                    */
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = customOrder ?
                                getChildDrawingOrder(childrenCount, i) : i;
                        final View child = children[childIndex];

                       /*
                        * 如果这个子元素无法接收Pointer Event或这个事件点压根就没有落在子元素的边界范围内
                        */
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            // 那么就跳出该次循环继续遍历
                            continue;
                        }

                        // 找到Event该由哪个子元素持有
                        newTouchTarget = getTouchTarget(child);


                        if (newTouchTarget != null) {
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }

                        resetCancelNextUpFlag(child);

                       /*
                        * 投递事件执行触摸操作
                        * 如果子元素还是一个ViewGroup则递归调用重复此过程
                        * 如果子元素是一个View那么则会调用View的dispatchTouchEvent并最终由onTouchEvent处理
                        */
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            mLastTouchDownTime = ev.getDownTime();
                            mLastTouchDownIndex = childIndex;
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                    }
                }

               /*
                * 如果发现没有子元素可以持有该次事件
                */
                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }
        ...
    }
    ...
    return handled;
}

具体分发机制在之前的浅析Android触摸事件分发机制就讲过了

ViewGroup事件投递的递归调用就类似于一条责任链,责任就是消费触摸事件.,一旦其寻找到责任者,那么将由责任者持有并消费掉该次事件,具体的体现在View的onTouchEvent方法中返回值的设置,如果onTouchEvent返回false那么意味着当前View不会是该次事件的责任人将不会对其持有,如果为true则相反,此时View会持有该事件并不再向外传递。

带来的问题

万事都有其不足,甚至完美的东西都有缺点,那就是他没有缺点.责任链模式也有其缺点: 最大的缺点是当责任人层次太多,责任人太多时,性能问题


本文作者:Anderson/Jerey_Jobs

博客地址 : http://jerey.cn/
简书地址 : Anderson大码渣
github地址 : https://github.com/Jerey-Jobs

作者:Anderson大码渣,欢迎关注我的简书: Anderson大码渣