中介者模式包装了一系列对象相互作用的方式,使得对象不必相互明显作用。使它们可以松散耦合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立变化。中介者模式将多对多的相互作用转化为一对多的相互作用。中介者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
Mediator:抽闲中介者角色,定义了同事对象到中介者对象的接口,一般以抽象类的方式实现。
ConcreteMediator:具体中介者角色,继承于抽象中介者,实现了父类定义的方法,它从具体的同事对象接收消息,向具体同事对象发出命令。
Colleague:抽象同事角色,定义了中介者对象的接口,它知道中介者而不知道其他同事对象。
ConcreteColeagueA/B:具体同事类角色,继承于抽象同事类,每个具体同事类都知道本身在小范围内的行为,而不知道他在大范围内的目的。
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\Mediator.java
//抽象中介者
public abstract class Mediator {
protected ConcreteColleagueA colleagueA;//具体同事A
protected ConcreteColleagueB colleagueB;//具体同事B
/**
* 抽象中介方法、子类实现
*/
public abstract void method();
public void setColleagueA(ConcreteColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(ConcreteColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
}
//具体中介者
package mediatorpattern.gome.com.mediatordemo;
/**
* Created by ying.zhang on 2018/8/15.
*/
public class ConcreteMediator extends Mediator {
@Override
public void method() {
colleagueA.action();
colleagueB.action();
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\Colleague.java
//抽象同事
public abstract class Colleague {
protected Mediator mediator;//中介者对象
public Colleague (Mediator mediator) {
this.mediator = mediator;
}
/**
* 同事角色的具体行为,由子类去实现
*/
public abstract void action();
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\ConcreteColleagueA.java
/**
* Created by user on 2018/8/15.
* 具体同事A
*/
public class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
@Override
public void action() {
Log.v(MainActivity.TAG, "ColleagueA 将信息递交给中介者处理");
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\ConcreteColleagueB.java
/**
* Created by ying.zhang on 2018/8/15.
* ConcreteColleagueB
*/
public class ConcreteColleagueB extends Colleague {
public ConcreteColleagueB(Mediator mediator) {
super(mediator);
}
@Override
public void action() {
Log.v(MainActivity.TAG, "ColleagueB 将想你想递交给中介者处理");
}
}
中介者模式的例子很多,计算机就是一个中介者模式的例子,以计算机主机为例,计算机主机部分构成主要分为几块:CPU、内存、显卡、IO设备,一般来说一台计算机有了前两块:CPU、内存、显卡、IO设备,一般来说一台计算机有前面两块就可以运行启动了,当然,如果要连接显示器显示画面,那么还需要加上显卡,如果需要存储数据,那还需要加上IO设备,但是,在本节中这些部分都不重要,因为它们分割开来也是一个普通零部件而已,我们需要 将这些零部件整合起来,那就是主板。
package mediatorpattern.gome.com.mediatordemo;
/**
* Created by ying.zhang on 2018/8/15.
* 抽象中介者
* 抽象中介者只是定义了一个抽象接口方法,具体的同事类通过该方法通知中介者自身的状态改变。
*/
public abstract class ZMeditor {
/**
* 同事对象改变时通知中介者的方法
* 在同事对象改变时由中介者通知其他对象
*
* @param c 同事对象
*/
public abstract void changed(ZColleague c);
}
package mediatorpattern.gome.com.mediatordemo;
/**
* Created by ying.zhang on 2018/8/15.
* 具体的中介者是主板,它负责联络各个同事类,也就是CPU、内存、显卡、IO设备等
*/
public class MainBoard extends ZMeditor {
private CDDevice cdDevice;//光驱设备
private CPU cpu;//CPU
private SoudCard soudCard;//声卡设备
private GraphicsCard graphicsCard;//显卡设备
@Override
public void changed(ZColleague c) {
if (c == cdDevice) {
handleCD((CDDevice) c);
} else if (c == cpu) {
handleCPU((CPU) c);
}
}
/**
* 处理光驱读取数据后其他设备的交互
* @param cdDevice
*/
private void handleCD(CDDevice cdDevice) {
cpu.decodeData(cdDevice.read());
}
/**
* 处理CPU读取数据后与其他设备的交互
* @param cpu
*/
private void handleCPU(CPU cpu) {
soudCard.soudPlay(cpu.getDataSound());
graphicsCard.videoPlay(cpu.getDataVideo());
}
/**
* 设置CD设备
* @param cdDevice
*/
public void setCDDevice(CDDevice cdDevice) {
this.cdDevice = cdDevice;
}
/**
* 设置CPU
*
* @param cpu
*/
public void setCPU(CPU cpu) {
this.cpu = cpu;
}
/**
* 设置声卡设备
*/
public void setSoundCard(SoudCard soundCard) {
this.soudCard = soundCard;
}
/**
* 设置显卡设备
* @param graphicsCard
*/
public void setGraphicsCard(GraphicsCard graphicsCard) {
this.graphicsCard = graphicsCard;
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\ZColleague.java
package mediatorpattern.gome.com.mediatordemo;
/**
* Created by ying.zhang on 2018/8/15.
* 抽象同事
*/
public abstract class ZColleague {
protected ZMeditor mediator;//每个Colleague都应该知道中介者
public ZColleague(ZMeditor mediator) {
this.mediator = mediator;
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\CDDevice.java
package mediatorpattern.gome.com.mediatordemo;
/**
* Created by ying.zhang on 2018/8/15.
* CD 设备负责读取光盘的数据并将数据提供给主板
*/
public class CDDevice extends ZColleague {
private String data;//视频数据
public CDDevice(ZMeditor mediator) {
super(mediator);
}
/**
* 读取视频数据
* @return 视频数据
*/
public String read() {
return data;
}
/**
* 加载视频数据
*/
public void load() {
//实际情况中视频数据和音频数据是在一个数据流中的
data = "视频数据,音频数据";
//通知中介者,也就是主板数据改变
mediator.changed(this);
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\CPU.java
package mediatorpattern.gome.com.mediatordemo;
/**
* Created by ying.zhang on 2018/8/15.
* 首先是CPU,其负责对主板传递过来的音、视频数据解码
*/
public class CPU extends ZColleague {
private String dataVideo, dataSound;//视频和音频数据
public CPU(ZMeditor mediator) {
super(mediator);
}
/**
* 获取视频数据
*
* @return 视频数据
*/
public String getDataVideo() {
return dataVideo;
}
/**
* 获取音频数据
* @return 音频数据
*/
public String getDataSound() {
return dataSound;
}
/**
* 解码数据
* @param data
*
*/
public void decodeData(String data) {
//分割音、视频数据
String[] tmp = data.split(",");
//解析音、视频数据
dataVideo = tmp[0];
dataSound = tmp[1];
//告诉中介者自身状态改变
mediator.changed(this);
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\GraphicsCard.java
package mediatorpattern.gome.com.mediatordemo;
import android.util.Log;
/**
* Created by ying.zhang on 2018/8/15.
* 显卡和声卡分别用来播放视频和音频
*/
public class GraphicsCard extends ZColleague {
public GraphicsCard(ZMeditor mediator) {
super(mediator);
}
/**
* 播放视频
* @param data 视频数据
*/
public void videoPlay(String data) {
Log.v(MainActivity.TAG, "视频:" + data);
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\SoudCard.java
package mediatorpattern.gome.com.mediatordemo;
import android.util.Log;
/**
* Created by ying.zhang on 2018/8/15.
*
*/
public class SoudCard extends ZColleague {
public SoudCard(ZMeditor mediator) {
super(mediator);
}
/**
* 播放视频
*
* @param data 音频数据
*/
public void soudPlay(String data) {
Log.v(MainActivity.TAG,"音频:" + data);
}
}
D:\Users\user\AndroidStudioProjects\MediatorDemo\app\src\main\java\mediatorpattern\gome\com\mediatordemo\MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final String TAG = "Mediator_zy";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构造主板对象
MainBoard mainBoard = new MainBoard();
//分别构造各个零部件
CDDevice cd = new CDDevice(mainBoard);
CPU cpu = new CPU(mainBoard);
GraphicsCard graphicsCard = new GraphicsCard(mainBoard);
SoudCard sc = new SoudCard(mainBoard);
//将各个零部件安装到主板
mainBoard.setCDDevice(cd);
mainBoard.setCPU(cpu);
mainBoard.setGraphicsCard(graphicsCard);
mainBoard.setSoundCard(sc);
//完成之后就可以开始播放视频了
cd.load();
}
}
Log分析:
08-15 13:46:17.599 8088-8088/mediatorpattern.gome.com.mediatordemo V/Mediator_zy: 音频:音频数据
08-15 13:46:17.600 8088-8088/mediatorpattern.gome.com.mediatordemo V/Mediator_zy: 视频:视频数据
中介者模式在Android源码中的一个比较好的例子是Keyguard锁屏的功能实现,查找设计模式实现的小技巧。Android源码的命名相当规范,如果要找一个设计模式,以中介者模式为例,只需要在源码中搜索“Mediator”关键字,就能够找到KeyguardViewMediator这个类,顺藤摸瓜只需阅读KeyguardViewMediator的源码就可以扎到其他的同事类,由此构建一个完整的设计模式类图。
#Android8.0
public class KeyguardViewMediator extends SystemUI {
... ...
private AlarmManager mAlarmManager;
private AudioManager mAudioManager;
private StatusBarManager mStatusBarManager;
/** High level access to the power manager for WakeLocks */
private PowerManager mPM;
/**
* Used to keep the device awake while to ensure the keyguard finishes opening before
* we sleep.
*/
private PowerManager.WakeLock mShowKeyguardWakeLock;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
... ...
}
我们在KeyguardViewMediator中看到很多xxxManager管理器的成员变量,这些各种各样的管理器其实就是同事类,Android使用KeyguardViewMediator来充当中介者 协调这些管理器的状态改变,同样地,KeyguardViewMediator中定义了很多方法来处理这些管理器的状态,以解锁或锁屏时声音的播放为例,在KeyguardViewMediator中对应playSounds方法来协调音频的这一状态。
private void playSounds(boolean locked) {
playSound(locked ? mLockSoundId : mUnlockSoundId);
}
private void playSound(int soundId) {
if (soundId == 0) return;
final ContentResolver cr = mContext.getContentResolver();
if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) {
mLockSounds.stop(mLockSoundStreamId);
// Init mAudioManager
if (mAudioManager == null) {
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
if (mAudioManager == null) return;
mUiSoundsStreamType = mAudioManager.getUiSoundsStreamType();
}
mUiOffloadThread.submit(() -> {
// If the stream is muted, don't play the sound
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
int id = mLockSounds.play(soundId,
mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
synchronized (this) {
mLockSoundStreamId = id;
}
});
}
}
另一个Android中关于中介者模式的例子是Binder机制,Binder机制是Android中非常重要的一个机制,其用来绑定不同的系统级服务并进行跨进程通信。在Binder机制中,有3个非常重要的组件:Servicemanager、Binder Driver和Bp Binder,其中 Bp Binder是Binder的一个代理角色,其提供IBinder接口给各个客户端服务使用,这三者就扮演了一个中介者的角色。当手机启动后,ServiceManager会先向Binder Driver进行注册,注意,这里的ServiceManager虽然是一个特殊的服务,但毕竟还是一个服务,其特殊性在于,它在Binder Driver中是最先被注册的,其注册ID为0,当其他的服务要注册到Binder Driver时,会先通过这个0号ID获取到ServiceManager所对应的IBinder接口,该接口的实质上的实现逻辑是由BpBinder实现的,获取到对应的接口后就回调其中的transcat方法,此后就会在Binder Driver中新注册一个ID 1来对应这个服务,如果有客户端想要使用这个服务,那么,它会先像Binder Driver一样获取到ID为0的接口,也就是ServiceManager所对应的接口,并调用其transcat方法要求连接到刚才的服务,这时候Binder Driver就会将ID为1的服务回传给客户端并将相关消息反馈给ServiceManager完成连接。这里的ServiceManager和Binder Driver就相当于一个中介者,协调各个服务端与客户端。
参考《Android源码设计模式》