中介者模式,这个模式用来解耦类与类之间的复杂关系。
我在项目中使用中介者用来处理音乐播放器的多入口控制问题。首先是需求:带屏智能音箱在播放音乐的时候控制的入口有两个,界面和语音,播放器只有一个,为了解耦多个控制者和一个执行者,所以采用了中介者模式。这样控制者与中介者交互、中介者与执行者交互,解决了多个控制者之间的控制冲突。
下面来看看代码的实现:
首先是中介者基类:
/**
* 中介者模式基类,只有一个方法notice用来通知
*/
public interface BaseMediaEventMediator {
void notice(MediaEventUser mediaEventUser, Message message);
}
然后是所有中介者下用户的基类:
public abstract class MediaEventUser {
protected BaseMediaEventMediator baseMediaEventMediator;
public MediaEventUser(BaseMediaEventMediator baseMediaEventMediator) {
this.baseMediaEventMediator = baseMediaEventMediator;
}
public boolean handleMessage(Message message) {
return false;
}
}
用户 -> 控制者,也就是界面和语音控制:
/**
* 音乐的命令者,通过baseMediaEventMediator的notice方法来通知执行者,然后通过回调来获取执行者当前的状态
*/
public class MediaEventCommander extends MediaEventUser {
private Message message;
private MediaEventCommanderCallback mediaEventCommanderCallback;
public MediaEventCommander(BaseMediaEventMediator baseMediaEventMediator, MediaEventCommanderCallback mediaEventCommanderCallback) {
super(baseMediaEventMediator);
this.mediaEventCommanderCallback = mediaEventCommanderCallback;
}
public void commandStartPlay() {
message = new Message();
message.what = MediaEvent.COMMAND_START_PLAY;
noticeExecutor(message);
}
public void commandStopPlay() {
message = new Message();
message.what = MediaEvent.COMMAND_STOP_PLAY;
noticeExecutor(message);
}
public void commandPausePlay() {
message = new Message();
message.what = MediaEvent.COMMAND_PAUSE_PLAY;
noticeExecutor(message);
}
public void commandResumePlay() {
message = new Message();
message.what = MediaEvent.COMMAND_RESUME_PLAY;
noticeExecutor(message);
}
public void commandFastForward(Integer value) {
message = new Message();
message.what = MediaEvent.COMMAND_FAST_FORWARD;
message.obj = value;
noticeExecutor(message);
}
public void commandFastBackward(Integer value) {
message = new Message();
message.what = MediaEvent.COMMAND_FAST_BACKWARD;
message.obj = value;
noticeExecutor(message);
}
public void commandChangePlayModel(PlayModel model) {
message = new Message();
message.what = MediaEvent.COMMAND_CHANGE_PLAYMODEL;
message.obj = model;
noticeExecutor(message);
}
public void commandNext() {
message = new Message();
message.what = MediaEvent.COMMAND_NEXT;
noticeExecutor(message);
}
public void commandPrevious() {
message = new Message();
message.what = MediaEvent.COMMAND_PREVIOUS;
noticeExecutor(message);
}
public void commandIndex(Integer index) {
message = new Message();
message.what = MediaEvent.COMMAND_INDEX;
message.obj = index;
noticeExecutor(message);
}
public void commandChangeProcess(Integer process) {
message = new Message();
message.what = MediaEvent.COMMAND_CHANGE_PROCESS;
message.obj = process;
noticeExecutor(message);
}
public void requestPlayingAudio() {
message = new Message();
message.what = MediaEvent.REQUEST_PLAYING_AUDIO;
noticeExecutor(message);
}
public void requestAudioList() {
message = new Message();
message.what = MediaEvent.REQUEST_AUDIOLIST;
noticeExecutor(message);
}
private void noticeExecutor(Message message) {
baseMediaEventMediator.notice(this, message);
}
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MediaEvent.EXECUTOR_START_PALY:
mediaEventCommanderCallback.onMediaStart((AudioBean) message.obj);
break;
case MediaEvent.EXECUTOR_STOP_PALY:
mediaEventCommanderCallback.onMediaPause();
break;
case MediaEvent.COMMAND_PAUSE_PLAY:
mediaEventCommanderCallback.onMediaPause();
break;
case MediaEvent.EXECUTOR_ERROR_PALY:
mediaEventCommanderCallback.onMediaError((ExoPlayerManager.MediaPlayerError) message.obj);
break;
case MediaEvent.EXECUTOR_CHANGE_PLAYMODEL:
mediaEventCommanderCallback.onMediaChangePlayModel((PlayModel) message.obj);
break;
case MediaEvent.RETURN_PALYING_AUDIO:
mediaEventCommanderCallback.returnPlayingMediaAudio((AudioBean) message.obj);
break;
case MediaEvent.RETURN_AUDIOLIST:
mediaEventCommanderCallback.returnMediaAudioList((List<AudioBean>) message.obj);
break;
case MediaEvent.RETURN_LYRIC:
mediaEventCommanderCallback.returnLyric((File) message.obj);
break;
}
return super.handleMessage(message);
}
public interface MediaEventCommanderCallback {
void onMediaStart(AudioBean audioBean);
void onMediaPause();
void onMediaError(ExoPlayerManager.MediaPlayerError mediaPlayerError);
void onMediaChangePlayModel(PlayModel model);
void returnPlayingMediaAudio(AudioBean audioBean);
void returnMediaAudioList(List<AudioBean> audioBeans);
void returnLyric(File lyricFile);
}
}
用户 -> 执行者,也就是音乐播放器:
/**
* 音乐的执行者,通过baseMediaEventMediator的notice方法来通知命令者,然后通过回调来获取命令者的指令
*/
public class MediaEventExecutor extends MediaEventUser {
private Message message;
private MediaEventExecutorCallback mediaEventExecutorCallback;
public MediaEventExecutor(BaseMediaEventMediator baseMediaEventMediator, @NonNull MediaEventExecutorCallback mediaEventExecutorCallback) {
super(baseMediaEventMediator);
this.mediaEventExecutorCallback = mediaEventExecutorCallback;
}
public void executorStartPlay(AudioBean audioBean) {
message = new Message();
message.what = MediaEvent.EXECUTOR_START_PALY;
message.obj = audioBean;
noticeCommander(message);
}
public void executorPausePlay() {
message = new Message();
message.what = MediaEvent.EXECUTOR_STOP_PALY;
noticeCommander(message);
}
public void executorError(ExoPlayerManager.MediaPlayerError mediaPlayerError) {
message = new Message();
message.what = MediaEvent.EXECUTOR_ERROR_PALY;
message.obj = mediaPlayerError;
noticeCommander(message);
}
public void executorChangePlayModel(PlayModel playModel) {
message = new Message();
message.what = MediaEvent.EXECUTOR_CHANGE_PLAYMODEL;
message.obj = playModel;
noticeCommander(message);
}
public void returnAudio(AudioBean audioBean) {
message = new Message();
message.what = MediaEvent.RETURN_PALYING_AUDIO;
message.obj = audioBean;
noticeCommander(message);
}
public void returnAudioList(List<AudioBean> audioBeans) {
message = new Message();
message.what = MediaEvent.RETURN_AUDIOLIST;
message.obj = audioBeans;
noticeCommander(message);
}
public void returnLyric(File file) {
message = new Message();
message.what = MediaEvent.RETURN_LYRIC;
message.obj = file;
noticeCommander(message);
}
private void noticeCommander(Message message) {
baseMediaEventMediator.notice(this, message);
}
@Override
public final boolean handleMessage(Message message) {
switch (message.what) {
case MediaEvent.COMMAND_START_PLAY:
return mediaEventExecutorCallback.startPlay();
case MediaEvent.COMMAND_STOP_PLAY:
return mediaEventExecutorCallback.stopPlay();
case MediaEvent.COMMAND_PAUSE_PLAY:
return mediaEventExecutorCallback.pausePlay();
case MediaEvent.COMMAND_RESUME_PLAY:
return mediaEventExecutorCallback.resumePlay();
case MediaEvent.COMMAND_FAST_FORWARD:
return mediaEventExecutorCallback.fastForward((Integer) message.obj);
case MediaEvent.COMMAND_FAST_BACKWARD:
return mediaEventExecutorCallback.fastBackward((Integer) message.obj);
case MediaEvent.COMMAND_CHANGE_PLAYMODEL:
return mediaEventExecutorCallback.changePlayModel((PlayModel) message.obj);
case MediaEvent.COMMAND_NEXT:
return mediaEventExecutorCallback.next();
case MediaEvent.COMMAND_PREVIOUS:
return mediaEventExecutorCallback.previous();
case MediaEvent.COMMAND_INDEX:
return mediaEventExecutorCallback.index((Integer) message.obj);
case MediaEvent.COMMAND_CHANGE_PROCESS:
return mediaEventExecutorCallback.changeProcess((Integer) message.obj);
case MediaEvent.REQUEST_PLAYING_AUDIO:
return mediaEventExecutorCallback.getPlayingAudio();
case MediaEvent.REQUEST_AUDIOLIST:
return mediaEventExecutorCallback.getAudioBeans();
default:
break;
}
return super.handleMessage(message);
}
public interface MediaEventExecutorCallback {
boolean startPlay();
boolean stopPlay();
boolean pausePlay();
boolean resumePlay();
boolean fastForward(Integer value);
boolean fastBackward(Integer value);
boolean changePlayModel(PlayModel model);
boolean next();
boolean previous();
boolean index(Integer index);
boolean changeProcess(Integer process);
boolean getPlayingAudio();
boolean getAudioBeans();
}
}
最后是中介者的实现:
/**
* 中介者模式实现
*/
public class MediaEventMediator implements BaseMediaEventMediator {
private static final String TAG = "MediaEventMediator";
private int mediaType;
private MediaEventMediator() {
mediaType = Constant.MEDIA_TYPE_MUSIC;
}
private static MediaEventMediator instance = new MediaEventMediator();
public static MediaEventMediator getInstance() {
return instance;
}
//音乐的中介者允许有多个命令者Commander(Activity、Service)
private List<MediaEventCommander> commanders = new ArrayList<>();
//音乐的中介者模式只允许一个执行者Executor
private MediaEventExecutor executor;
public void addCommander(MediaEventCommander commander) {
commanders.add(commander);
}
public void removeCommander(MediaEventCommander commander) {
commanders.remove(commander);
}
public void setExecutor(MediaEventExecutor executor) {
this.executor = executor;
}
//实现基类的notice方法,通过Message来携带参数
@Override
public void notice(MediaEventUser mediaEventUser, Message message) {
LogUtil.d(TAG, "notice: 中介者收到消息:mediaEventUser.getClass() = " + mediaEventUser.getClass().getName()
+ ";message.what = " + message.what
+ ";message.obj = " + message.obj);
if ("com.soundai.music.mediators.MediaEventCommander".equals(mediaEventUser.getClass().getName())) {
if (executor != null) {
boolean flag = executor.handleMessage(message);
LogUtil.d(TAG, "notice: 处理结果:flag = " + flag);
}
} else if ("com.soundai.music.mediators.MediaEventExecutor".equals(mediaEventUser.getClass().getName())) {
for (MediaEventCommander commander : commanders) {
commander.handleMessage(message);
}
}
}
}
代码部分就到此为止了,从我的感觉上来说,中介者模式的好处在于:把对象和对象之间的耦合降低了,比如上面的命令者和执行者,在加入了中介者之后,执行者就只需要关心自己的业务逻辑然后跟中介者交互,命令者同理。这样的缺点也很明显:所有的交互相关的逻辑都在中介者中处理,中介者可能后续的逻辑会越来越复杂,而且一旦中介者出了问题,那么整条链路可能都会出现问题。