设计模式

设计模式实践-组合模式

2020-10-20  本文已影响0人  h2coder

什么是组合模式?什么时候用?

组合模式,也称为部分整理模式。该模式将一组相似的对象,当作一个对象处理,并将对象组合成树形结构,提供统一获取方法,以此来忽略对象和对象间的差别。简单来讲,就是将对象组合为树形结构来处理外部的调用,内部进行递归分发,最后获取结果返回(是不是很像ViewGrop和View的关系,和事件处理机制,没错他们就是用了组合模式!)。

怎么实现组合模式?

组合模式涉及以下对象:

  1. Component:抽象的节点,可以是接口,也可以是抽象类,是节点的基类。

  2. Composite,存储子节点。

  3. Leaf,没有子节点的叶子节点。

组合模式,分发onBackPressed

安卓中,系统给我提供了onBackPressed()来处理返回键,我们一般在该回调里面去拦截返回键,例如比较常用的双击退出应用。在做直播模块的时候,遇到一个问题,界面上有几个自定义View是悬浮的,当按下返回键时需要他们去隐藏,所以只能在onBackPressed()中将所有需要的View在这里判断,如果没有隐藏,先隐藏,然后拦截,最后都隐藏完了,再调用super.onBackPressed()进行finish掉Activity。

    @Override
    public void onBackPressed() {
        //公告正在显示,必须先关闭,拦截
        if (vNoticeFlowView != null && vNoticeFlowView.isShow()) {
            vNoticeFlowView.hide();
            return;
        }
        //工具箱正在显示,必须先关闭,拦截
        if (vToolBoxDrawer != null && vToolBoxDrawer.isOpen()) {
            vToolBoxDrawer.close();
            return;
        }
        //分享布局正在显示,必须先关闭,拦截
        if (isShowShareLayout) {
            toggleShareLayout(false);
            return;
        }
        super.onBackPressed();
    }
@Override
    public void onBackPressed() {
        //...
        ToggleAreaMapRouteListEvent toggleRouteListEvent = EventBusUtil.getStickyEvent(ToggleAreaMapRouteListEvent.class);
        if (toggleRouteListEvent != null && toggleRouteListEvent.isShow()) {
            ToggleAreaMapRouteListEvent.send(false);
            return;
        }
        ToggleAreaMapSubScenicListEvent toggleSubScenicListEvent = EventBusUtil.getStickyEvent(ToggleAreaMapSubScenicListEvent.class);
        if (toggleSubScenicListEvent != null && toggleSubScenicListEvent.isShow()) {
            ToggleAreaMapSubScenicListEvent.send(false);
            return;
        }
        ToggleAreaMapVoiceTypeListEvent toggleVoiceTypeListEvent = EventBusUtil.getStickyEvent(ToggleAreaMapVoiceTypeListEvent.class);
        if (toggleVoiceTypeListEvent != null && toggleVoiceTypeListEvent.isShow()) {
            ToggleAreaMapVoiceTypeListEvent.send(false);
            return;
        }
        ToggleAreaMapAreaServiceEvent toggleAreaMapAreaServiceEvent = EventBusUtil.getStickyEvent(ToggleAreaMapAreaServiceEvent.class);
        if (toggleAreaMapAreaServiceEvent != null && toggleAreaMapAreaServiceEvent.isShow()) {
            ToggleAreaMapAreaServiceEvent.send(false);
            return;
        }
        if (ToggleScenicMapOfflineEvent.getEvent().isShow()) {
            ToggleScenicMapOfflineEvent.getEvent().send(false, OfflineHolder.TYPE_AREA);
            return;
        }
        if (ToggleScenicAuthEvent.getEvent().isShow()) {
            ToggleScenicAuthEvent.getEvent().send(false, ScenicMapAuthHolder.AUTH_TYPE_AREA);
            return;
        }
        //...
        super.onBackPressed();
    }

实现步骤

  1. Component组件接口,叶子接口,定义组件事件入口方法和添加子组件(没错组件是可以嵌套的!),而我们的onBackPressed()是带返回值的,返回值代表是否已经被处理。
public interface Component {
    /**
     * 注册子组件
     */
    void registerComponent(Class<?> componentClazz, Component component);

    /**
     * 解注册子组件
     */
    void unRegisterComponent(Component component);

    /**
     * 获取子组件
     *
     * @param componentClazz 子组件的接口
     */
    <T extends Component> T getComponentInterface(Class<T> componentClazz);

    /**
     * 返回键处理
     */
    boolean onBackPressed();
}
  1. 一般组件还会有基类,定义通用流程逻辑和方法。子组件怎么嵌套呢,其实就是内部有一个List集合保存,在调用时,先遍历子组件调用对应的事件方法,返回值为true则已处理,那么我就不处理了,返回值为false则没有处理,那么父组件可以做处理。(好熟悉,这不就是事件分发机制中的dispatchTouchEvent()吗)
public abstract class BaseComponent implements Component, LifecycleObserver {
    /**
     * 子组件集合
     */
    private Map<Class<?>, Component> mChildComponents = new LinkedHashMap<>();
    /**
     * 生命周期
     */
    private LifecycleOwnerExt mLifecycleOwner;

    public BaseComponent(LifecycleOwnerExt owner) {
        mLifecycleOwner = owner;
        owner.getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    protected void onLifecycleCreate() {
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    protected void onLifecycleResume() {
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    protected void onLifecyclePause() {
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    protected void onLifecycleDestroy() {
    }

    public LifecycleOwnerExt getLifecycleOwner() {
        return mLifecycleOwner;
    }

    public Context getContext() {
        return mLifecycleOwner.getActivity();
    }

    public BaseLingJiActivity getActivity() {
        return (BaseLingJiActivity) mLifecycleOwner.getActivity();
    }

    public String getString(@ArrayRes int resId, int index) {
        return getActivity().getResources().getStringArray(resId)[index];
    }

    public String getString(@StringRes int resId) {
        return getActivity().getString(resId);
    }

    public String getString(@StringRes int resId, Object... formatArgs) {
        return getActivity().getString(resId, formatArgs);
    }

    @Override
    public void registerComponent(Class<?> componentClazz, Component component) {
        if (component == null) {
            return;
        }
        if (mChildComponents.containsKey(componentClazz)) {
            throw new IllegalArgumentException("Component已添加,请勿重复添加");
        }
        mChildComponents.put(componentClazz, component);
    }

    @Override
    public void unRegisterComponent(Component component) {
        Class<?> clazz = null;
        for (Map.Entry<Class<?>, Component> entry : mChildComponents.entrySet()) {
            if (entry.getValue() == component) {
                clazz = entry.getKey();
            }
        }
        if (clazz != null) {
            mChildComponents.remove(clazz);
        }
    }

    @Override
    public <T extends Component> T getComponentInterface(Class<T> componentClazz) {
        return (T) mChildComponents.get(componentClazz);
    }

    @CallSuper
    @Override
    public boolean onBackPressed() {
        boolean isHandle;
        for (Map.Entry<Class<?>, Component> entry : mChildComponents.entrySet()) {
            isHandle = entry.getValue().onBackPressed();
            if (isHandle) {
                return true;
            }
        }
        return false;
    }
}
  1. 组件实现类,也就是树形结构中的树叶,例如公告组件,一个悬浮公告View,需要返回键进行拦截,先掉隐藏自己。
//公告组件接口,继承Component接口。
public interface INoticeComponent extends Component {
    //其他无关Api...
}

public class NoticeComponent extends BaseComponent implements INoticeComponent {
    @Override
    public boolean onBackPressed() {
        //内嵌子组件处理了,那么我就不处理了
        boolean isHandle = super.onBackPressed();
        if (isHandle) {
            return true;
        }
        //公告正在显示,则先关闭
        if (vNoticeFlowView != null && vNoticeFlowView.isShow()) {
            vNoticeFlowView.hide();
            return true;
        } else {
            return false;
        }
    }
}
  1. 树叶有了,那么轮到树干。RoomComponentProvider,它实现了IComponentProvider接口,IComponentProvider接口则是继承Component接口,所以其实它也是一个组件,只是它是最根层的,可以说就是RootComponent。

其实组合模式有个缺点:就是获取子组件类型只能使用通用接口类型,如果需要获取具体类型就只能单独定义获取方法,方法内部强转后返回。

//组件提供者
public interface IComponentProvider extends Component {
    //定义一些其他获取组件的Api
}

public class RoomComponentProvider implements IComponentProvider {
    //方法实现,其他的和普通的组件没有区别
}
  1. 有了树干和树叶,还需要事件源头进行事件分发,即为我们的Activity。
public abstract class AudioLiveRoomActivity extends AppCompatActivity {
    //组件提供者
    private RoomComponentProvider mComponentProvider;

    @Override
    public void onCreate() {
        //...省略其他常规设置
        mComponentProvider = new RoomComponentProvider();
    }

    @Override
    public void onBackPressed() {
        if (mComponentProvider != null) {
            //组件树中组件所有都没有处理,则调用Activity父类处理
            if (!mComponentProvider.onBackPressed()) {
               super.onBackPressedSupport();
            }
        } else {
            super.onBackPressedSupport();
        }
    }
}

分发onActivityResult

如果onBackPressed的分发还不够,那就来处理onActivityResult吧!Activity间跳转自然少不了交互,例如:

  1. 调用Pay组件库进行支付,例如支付信息是通过onActivityResult返回。

  2. 调用发送图片消息,调用系统选择图片功能,图片信息通过onActivityResult返回。

  3. 其他我们的自定义界面,也需要使用onActivityResult返回数据。

等等,如果一个界面的startActivityForResult()非常多,onActivityResult的处理过程就非常多的if-else,那么onActivityResult()方法体就会很大,而且耦合在了Activity。

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PAY_REQUEST_CODE) {
            //支付
        } else if (requestCode == IMAGE_PICK_REQUEST_CODE) {
            //选择图片
        } else if (requestCode == XXX_REQUEST_CODE) {
            //自定义
        }
        //...只要够复杂,这里就会满屏幕都是if-else
}

分发套路

其实有了上文的分发onBackPressed,那么分发onActivityResult就是照葫芦画瓢了。

  1. 在Component接口中添加onActivityResult方法。

  2. BaseComponent组件基类中,复写onActivityResult方法,遍历内部组件进行分发。

  3. 需要处理onActivityResult的子类组件,复写onActivityResult方法进行处理。

没错,只要这3步,即可分发事件,后续再需要分发事件,则也是按照以上3步即可!

实现步骤

  1. Component提供onActivityResult
public interface Component {
    //省略其他方法...

    /**
     * onActivityResult()处理,返回true为已处理,false为未处理
     */
    boolean onActivityResult(int requestCode, int resultCode, Intent data);
    
    //省略其他方法...
}
  1. BaseComponent中分发给子组件
public abstract class BaseComponent implements Component {
    @CallSuper
    @Override
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        boolean isHandle;
        for (Map.Entry<String, Component> entry : mChildComponents.entrySet()) {
            isHandle = entry.getValue().onActivityResult(requestCode, resultCode, data);
            if (isHandle) {
                return true;
            }
        }
        return false;
    }
}
  1. 例如支付组件,需要接收支付结果
public class PayComponent extends BaseComponent implements IPayComponent {
    @Override
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        //先调用子组件处理,没有处理则自己处理
        boolean isHandle = super.onActivityResult(requestCode, resultCode, data);
        if (isHandle) {
            return true;
        }
        //...拿取支付信息
    }
}

总结

组合模式的优缺点

优点:

  1. 可以清晰的定义类的层次结构,对外接收处理时,外部并不需要关心内部的复杂情况,外部并不清楚接收方是单个对象还是一组有结构的对象。

  2. 添加新的枝干和节点时,增加新的分支类即可。内部采用递归组合进行事件分发,可以很好将类职责分拆。

缺点:

  1. 获取组件只能返回基类类型,使用时需要必要的类型检查。(类似Context的getSystemService())
上一篇下一篇

猜你喜欢

热点阅读