设计模式实践 外观模式

208 阅读5分钟

什么是外观模式?什么时候用?

外观模式,也称为门面模式。其实日常生活中,有很多外观模式的影子,例如我们日常工作中,领导层,决定要开发一个直播模块,领导层给中层(部门老大)发出命令,中层则分配到特定的执行层(我们开发)。领导层无需知道下面的人是怎么分配的,谁和谁配合,只要发出命令,验收结果即可。

场景

  • 例如刚完成的直播间模块,用户角色的改变,UI界面上很多部件的状态、样式、点击事件等都需要切换,这篇文章就不一一列举了,不是本文重点,避免跑题。

  • 直播间总共有以下角色:

    1. 普通用户
    2. 上座用户
    3. 房管
    4. 老师(主播)
    5. 禁言用户
    6. 游客

直播间样式.png

  • 可以看到直播间的角色是非常多的,也不排除以后会增加其他角色,例如其他pk、助场老师等。这就涉及到了角色切换,切换的地方又不止一处,有的是由接口调用回调时切换,有的是IM触发而切换等等...那么需要切换的组件大于3个以上,每处都需要写相同的代码,而后续再加组件也需要切换时,就需要每处都要修改,这时就可以使用外观模式来统一分发切换,外部调用方无需知道哪些组件需要进行切换,只需要做要发起切换的动作行为即可。

怎么实现外观模式?

  • Facade,门面接口,对外提供的Api接口。
  • 子系统接口,每个子系统都需要实现。

外观模式实践,统一处理直播间角色改变

如果正常逻辑去编写我们的代码,就会有非常多的if-else,所以每个UI组件我都用状态模式封装了一遍,外部只需要调用切换角色API即可,无需关注内部是怎么切换的,是显示、隐藏还是Remove、Add。而角色改变的时机并不是只有一处,那么调用各个组件的切换方法就会到处都是,为了解决这种情况我们使用外观模式,统一调用外观,外观再分发给每个需要角色切换的组件。

实现步骤

  1. IRoleSwitcher接口,角色切换接口,定义了每种角色的切换方法,这里由于我们的门面和其他组件的操作是一样的,所以合并给一个接口。

  2. Component接口,子系统接口,定义通用组件方法,每个子系统都会自己有一个接口继承它,并提供子系统的特定Api。

  3. IBottomBarComponent,底部栏组件接口,实现了Component接口和IRoleSwitcher接口。

  4. IApplyForSeatComponent,上座按钮组件接口,实现了Component接口和IRoleSwitcher接口。

  5. IToolBoxComponent,辅助工具栏组件接口,实现了Component接口和IRoleSwitcher接口。

  6. IRoleComponent,角色组件,就是我们外观,Facade,负责分发切换事件给上面的3个组件,由于我们门面Api和子系统Api是一致的,所以本身也实现了IRoleSwitcher操作接口。同样实现了Component作为组件使用。(非必须,只是我们的场景比较简单,基本就是分发切换事件,而其他需要不同子系统配合的场景,门面Api一般和子系统是不一样的)

别废话,上代码~

  • Component组件接口和IRoleSwitcher接口。
public interface Component {
}

public interface IRoleSwitcher {
    /**
     * 设置为普通用户角色
     */
    void setNormalUserRole();

    /**
     * 设置为管理员角色
     */
    void setManagerRole();

    /**
     * 设置为上座用户角色
     */
    void setSeatUpRole();

    /**
     * 设置为老师角色
     */
    void setTeacherRole();

    /**
     * 设置为游客角色
     */
    void setVisitorRole();

    /**
     * 设置为游客禁言角色
     */
    void setForbidSpeakRole();
}
  • IBottomBarComponent,底部栏组件接口和实现类
public interface IBottomBarComponent extends Component, IRoleSwitcher {
	//...其他Api方法,不是重点
}

public class BottomBarComponent extends BaseRoomComponent implements IBottomBarComponent {
	@Override
    public void setNormalUserRole() {
        //底部栏切换到普通用户角色样式
        ...
    }

    @Override
    public void setManagerRole() {
        //底部栏切换到普通用户角色样式
        ...
    }

    @Override
    public void setSeatUpRole() {
        //底部栏切换到上座用户角色样式
        ...
    }

    @Override
    public void setTeacherRole() {
        //底部栏切换到老师用户角色样式
        ...
    }

    @Override
    public void setVisitorRole() {
        //底部栏切换到游客角色样式
        ...
    }

    @Override
    public void setForbidSpeakRole() {
        //底部栏切换到禁言用户角色样式
        ...
    }
}
  • IApplyForSeatComponent,上座按钮组件
public interface IApplyForSeatComponent extends Component, IRoleSwitcher {
		//...省略其他Api
}

public class ApplyForSeatComponent extends BaseRoomComponent implements IApplyForSeatComponent {
		//...实现方法和BottomBarComponent是一致的,所以不再列举
}
  • IToolBoxComponent,辅助工具栏组件
public interface IToolBoxComponent extends Component, IRoleSwitcher {
		//...省略其他Api
}

public class ToolBoxComponent extends BaseRoomComponent implements IToolBoxComponent {
		//...实现方法和BottomBarComponent是一致的,所以不再列举
}
  • IRoleComponent,角色组件,中介类
public class RoleComponent extends BaseRoomComponent implements IRoleComponent {
@Override
    public void setNormalUserRole() {
        getComponentProvider().getBottomBarComponent().setNormalUserRole();
        getComponentProvider().getApplyForSeatComponent().setNormalUserRole();
        getComponentProvider().getToolBoxComponent().setNormalUserRole();
    }

    @Override
    public void setManagerRole() {
        getComponentProvider().getBottomBarComponent().setManagerRole();
        getComponentProvider().getApplyForSeatComponent().setManagerRole();
        getComponentProvider().getToolBoxComponent().setManagerRole();
    }

    @Override
    public void setSeatUpRole() {
        getComponentProvider().getBottomBarComponent().setSeatUpRole();
        getComponentProvider().getApplyForSeatComponent().setSeatUpRole();
        getComponentProvider().getToolBoxComponent().setSeatUpRole();
    }

    @Override
    public void setTeacherRole() {
        getComponentProvider().getBottomBarComponent().setTeacherRole();
        getComponentProvider().getApplyForSeatComponent().setTeacherRole();
        getComponentProvider().getToolBoxComponent().setTeacherRole();
    }

    @Override
    public void setVisitorRole() {
        getComponentProvider().getBottomBarComponent().setVisitorRole();
        getComponentProvider().getApplyForSeatComponent().setVisitorRole();
        getComponentProvider().getToolBoxComponent().setVisitorRole();
    }

    @Override
    public void setForbidSpeakRole() {
        getComponentProvider().getBottomBarComponent().setForbidSpeakRole();
        getComponentProvider().getApplyForSeatComponent().setForbidSpeakRole();
        getComponentProvider().getToolBoxComponent().setForbidSpeakRole();
    }
}
  • 切换调用处调用
mDataStore = getDataStore();
	//监听房间信息改变切换角色。
	mDataStore.getLiveRoomViewModel().getRoomInformationLiveData().observe(getLifecycleOwner(), new Observer<RoomInformationModel>() {
	@Override
	public void onChanged(@Nullable RoomInformationModel roomInformationModel) {
	    if (roomInformationModel == null) {
	        return;
	    }
	    IMRoomInfoModel roomInfoModel = roomInformationModel.getRoomInfo();
	    IMSendUserModel currentUser = mDataStore.getLiveRoomViewModel().getCurrentUserLiveData().getValue();
	    if (currentUser == null) {
	        return;
	    }
	    //这里简单期间,省略了决定角色的生成,决定后通知其他组件切换样式
	    IRoleComponent roleComponent = getComponentProvider().getRoleComponent();
	    //老师角色
	    if (currentUser.isTeacher()) {
	        roleComponent.setTeacherRole();
	    } else if (currentUser.isNormalUser()) {
	        //普通用户角色
	        roleComponent.setNormalUserRole();
	    } else if (currentUser.isSeatUpUser()) {
	        //上座用户角色
	        roleComponent.setSeatUpRole();
	    } else if (currentUser.VisitorRole()) {
	        //游客角色
	        roleComponent.setVisitorRole();
	    } else if (currentUser.isForbidSpeakUser()) {
	        //禁言用户角色
	        roleComponent.setForbidSpeakRole();
	    }
	}
});

总结

  • 外观模式的优点:提供统一入口方法给外部,隐藏内部子系统的细节。

  • 外观模式的缺点,外观类没有遵循开闭原则,当我们的业务变更时,可能就需要修改外观类,增加新的逻辑和子系统类。