案例原型图说明及预览:
在工作中,开发(学生PAD)个人中心的时候,有一个效果图如下图1-1,在主界面中包含了5个子标签页,每个标签页是相互独立,但是有时需要进行通信。
以下有几点功能:1、5个标签页都存在两种状态: ① 浏览状态 ② 编辑状态
2、点击某个标签页:
① 标签移至界面中间
② 被点击的标签页需要进入编辑状态, 如果此时存在其他标签页也处于编辑状态,则处于编辑状态的标签页需要变成浏览状态。
当其它标签页由编辑状态进入浏览状态时,如果该标签页中数据有进行修改,需要提示用户是否进行保存数据后,再进入浏览状态。
功能方案实现及思路:
1、WHAT (什么是中介者模式):
通俗的说就是一个我去租房,我和房东基于中介进行通信和商量。此中介使得其与对象不需要显示地相互作用。
专业点说明就是:定义一个对象封装一系列多个对象如何相互作用,使得对象间不需要显示地相互引用,从而使其耦合更加松散,并且还让我们可以独立变化多个对象相互作用。
以下两张对比图很直观的说明了中介者模式的概念:
图1-2 不存在中介者模式的示例图
图1-3 存在中介的示例图
通过以上两图的对比,我们可以知道中介者模式它可以避免同事类之间的过度耦合,使得各同事类之间相互独立的使用。
2、WHY (为什么采用中介者模式)
在开发这版个人中心时,就在思考5个标签页间应该采用什么技术进行通信,第一想法是采用观察者模式,5个标签页统一监听管理它们的Fragment,消息由该Fragment进行分发。
但是使用观察者模式,监听的类需要继承Observer类,可惜Java没有多继承的特性。所以观察者模式这里无法采用。 如果界面采用VIew来进行管理,那么观察者模式就可以发挥作用了。
之后实在没辙,就采用了拙计的办法,广播通信。每个标签页监听通信的广播,需要发送通知的对象只需要在5个标签页注册了该广播之后发送即可。当时就采用了广播,很好的实现了5个标签页状态的完美切换。但是后面这种通信方式和领导交流了一下,发现广播的作用主要是用于不同进程间的通信,相同进程中不应该使用广播通信,因为广播的内部机制就是采用中介者模式,如果相同进程间采用广播等于我们程序多绕了一圈。
领导阐述了一下中介者模式的机制和原理,之后回去查了资料,自己写了一个小DEMO,有了一定了解之后,发现中介者模式可以很完美的解决在不用的对象之间存在很多关系的问题。恰巧个人中心5个标签页需要的效果就是,一个标签页发送通知,其外的4个标签页需要接收并且做一些相应的操作。
3、HOW (如何采用中介者模式实现)
PS:列举一下相关类名说明:
UserCenterDetailLayoutFragment | 管理5个标签页的Fragment |
UserCenterBasicInfoFragment | 基本信息标签页 |
UserCenterStudyInfoFragment | 学习信息标签页 |
UserCenterPersonalInfoFragment | 个性信息标签页 |
UserCenterContactInfoFragment | 联系信息标签页 |
UserCenterFamilyInfoFragment | 家庭信息标签页 |
既然叫做中介者模式,那么肯定要有一个中介者来负责5个标签页间的通信,所以定义了一个中介者业务类, 此处需要主要的是,每个加入的对象都要给自己设定一个标识ID,中介者可以通过这个ID进行消息分发的过滤等操作。
/**
* 采用中介者模式,对个人中心首页5个标签进行交互通信。
*/
public interface DetailLayoutFragmentMediator {
/**
* 当存在标签页进行编辑状态时,发送此消息通知中转站中的其它标签页,让他们进行相应的操作.
*/
public void sendEntryEditStatusMessage( int fragmentId,BaseFragment fragment);
/**
* 将需要通信的标签页加入中转站中
* @param fragment 标签页
*/
public void attach(BaseFragment fragment);
/**
* 不需要通信的标签页从中转站分离出来
* @param fragment 标签页
*/
public void detach(BaseFragment fragment);
}
中介者业务的具体实现类:
/**
* 个人中心首页,5个标签页通信中转站的具体实现类
*/
public class DetailLayoutFragmentMediatorImpl implements DetailLayoutFragmentMediator {
private List<BaseFragment> fragmentList ;
private static DetailLayoutFragmentMediatorImpl mediator ;
public static DetailLayoutFragmentMediatorImpl getInstance(){
if (mediator == null){
mediator = new DetailLayoutFragmentMediatorImpl();
}
return mediator ;
}
private DetailLayoutFragmentMediatorImpl() {
// 目前是5个标签页间通信
fragmentList = new ArrayList<BaseFragment>();
}
@Override
public void sendEntryEditStatusMessage( int fragmentId,BaseFragment fragment) {
for (BaseFragment bf: fragmentList ){
// if(fragmentList.contains(fragment)) 正常情况下 需要进行 是否存在判断,但是本场景 标签页肯定存在就不进行判断
if (bf != fragment){
// 调用标签页接收通知的方法
bf.receiveFragmentEntryEditStatusMessage(fragmentId);
}
}
}
@Override
public void attach(BaseFragment fragment) {
if (fragmentList != null){
fragmentList .add(fragment);
}
}
@Override
public void detach(BaseFragment fragment) {
if (fragmentList != null){
fragmentList .remove(fragment);
}
}
}
通过以上代码,就实现了一个中介者最简单的功能, 1、加入接收消息团体。 2、离开消息团体。 3、发送相关消息
那么5个标签页如何关联该中介者, 需要定义出我们之前定义的中介者对象,在定义出5个标签页的时候,将它们加入中介者分发消息的集合中。
以下代码是在UserCenterDetailLayoutFragment.java类中, 管理5个标签页的Fragment
这个类是ViewPager的适配器,用于创建5个标签页
。。。。
public class UserCenterPagerAdapter extends FragmentStatePagerAdapter {
public UserCenterPagerAdapter(FragmentManager fm, ArrayList<Fragment> pages) {
super (fm);
}
@Override
public int getCount() {
return 5;
}
@Override
public Fragment getItem( int position) {
int realPosition = position % 5;
switch (position % NUM_ITEMS) {
case BASIC_INFO_TAG_INDEX :
Log. i( "AA", "基本信息标签页构造" );
UserCenterBasicInfoFragment basicInstance = UserCenterBasicInfoFragment.getInstance( fragmentMediator );
fragmentMediator .attach(basicInstance);// 将基本信息标签页加入中介者分发消息的集合中。
return basicInstance;
case STUDY_INFO_TAG_INDEX :
Log. i( "AA", "学习信息标签页构造" );
UserCenterStudyInfoFragment studyInstance = UserCenterStudyInfoFragment.getInstance( fragmentMediator );
fragmentMediator .attach(studyInstance);
return studyInstance;
case PERSONAL_INFO_TAG_INDEX :
Log. i( "AA", "个性信息标签页构造" );
UserCenterPersonalInfoFragment personalInstance = UserCenterPersonalInfoFragment.getInstance( fragmentMediator );
fragmentMediator .attach(personalInstance);
return personalInstance;
case CONTACT_INFO_TAG_INDEX :
Log. i( "AA", "联系信息标签页构造" );
UserCenterContactInfoFragment contactInstance = UserCenterContactInfoFragment.getInstance( fragmentMediator );
fragmentMediator .attach(contactInstance);
return contactInstance;
case FAMILY_INFO_TAG_INDEX :
Log. i( "AA", "家庭信息标签页构造" );
UserCenterFamilyInfoFragment familyInstance = UserCenterFamilyInfoFragment.getInstance( fragmentMediator );
fragmentMediator .attach(familyInstance);
return familyInstance;
default :
break ;
}
return UserCenterBasicInfoFragment.getInstance( fragmentMediator );
}
@Override
public int getItemPosition(Object object) {
return super .getItemPosition(object);
}
}
.......
经过以上步骤,就将标签页都加入了中介者分发消息的集合中,当有标签页通知中介者需要发送消息时,中介者会将该消息都发送给消息集合中的所以对象。
那么如何发送消息:
fragmentMediator .sendEntryEditStatusMessage(getFragmentId(), this );
sendEntryEditStatusMessage是我们自己定义的发送某标签页需要进入编辑状态消息,并将要进入编辑状态的标签页id,以及自己的引用传入。 之后我们参见DetailLayoutFragmentMediatorImpl 中的sendEntryEditStatusMessage 实现,我们就会知道该中介者会发送消息到在我们基类BaseFragment定义的方法
public abstract class BaseFragment extends Fragment implements OnClickListener{
......
/**
* 当有中转站中标签页进入编辑状态,中转站会通知绑定的所以成员
* @param fragmentMediator
*/
public abstract void receiveFragmentEntryEditStatusMessage( int fragmentId);
......
}
那么5个标签页如基本信息页来说明:
会通过receiveFragmentEntryEditStatusMessage方法接收到中介者发送的消息,我们就在这个方法中做一些我们想要的操作即可。
public final class UserCenterBasicInfoFragment extends BaseFragment {
......
@Override
public void receiveFragmentEntryEditStatusMessage(int fragmentId) {
// TODO Auto-generated method stub
Log. i( "AA", "Basic收到 Edit消息" );
showDialogToSynchData( mContext);
}
......
}
通过以上这些步骤,我们就可以实现个人中心标签页间通信这样的需求。
总结
使用中介者模式的场合和优缺点
使用终结者模式的场合
2.定制一个分布在多个类中的行为,而又不想生成太多的子类。
可以看出,中介对象主要是用来封装行为的,行为的参与者就是那些对象,但是通过中介者,这些对象不用相互知道。
使用中介者模式的优点:
2.提高系统的灵活性,使得系统易于扩展和维护。
使用中介者模式的缺点:
中介者模式的缺点是显而易见的,因为这个“中介“承担了较多的责任,所以一旦这个中介对象出现了问题,那么整个系统就会受到重大的影响。经历了这个案例,也深刻的体会了思想的重要性,以后应该着重锻炼,学习编程设计思想层面,不应该盲目的海量编码。