在日常生产开发中,在面对不同的业务场景时,使用合适的开发设计模式会大幅提高代码效率。所以熟练使用常用设计模式对我们开发效率与代码质量有着较大提升。
此次为大家介绍发布订阅者模式,由于在实际实现中也用到了缺省适配器与单例,所以也会有相应的介绍实现。
发布订阅者模式
这是一种适用于一对多业务场景的模式,包含消息发布者、消息订阅者、以及消息广播器三个模块。
此模式流程为发布者和订阅者都会有消息类型属性。当发布者触发自身事件,消息广播器会将消息广播给订阅对应消息类型的订阅者,订阅者去执行相应的逻辑方法。
单例模式
单例模式是一种多对一场景的模式。不同类型的对象调用特定实例时,此实例保持唯一,这就是单例模式的特点。比如买票。
上面提到消息广播器中包含了消息类型对应的订阅者,实际存储使用map来实现的。显而易见这个map的实现方式就是使用单例模式。
缺省适配器模式
缺省适配器模式时面向接口的一种模式。在我们定义某种接口的时候,实现类往往不会完全满足此接口,只会选择性的实现某些方法,这时候就要用到缺省适配器模式。
实际实现大致就是使用抽象类去实现接口的特殊方法,再由具体业务类继承抽象方法。不过现在Java版本有了接口可以实现default方法后显得抽象类比较鸡肋。但是抽象类还是有他的用武之地,比如实现了构造方法之后,他的继承类的构造方法只能调用阀类的构造方法,这也是很有用处的。
简单的介绍完这三种设计模式之后,让我们来看看他的具体实现吧。
首先是消息发布者接口
public interface IRelease {
String getMessageType();
String getName();
//同步发布事件
default public void eventSyns(Object message){
ReleaseSubscription.getInstance().eventSyns(this,message);
};
}
发布者实现类
public class ReleaseImpl implements IRelease {
protected String name;//实例名称
protected String messageType;//消息类型
public ReleaseImpl(String messageType){
this.messageType = messageType;
}
public String getName() {
return name;
}
public ReleaseImpl(String name, String messageType){
this.name = name;
this.messageType = messageType;
}
@Override
public String getMessageType() {
return this.messageType;
}
}
订阅者接口
public interface ISubscription {
String getMessageType();
//订阅
default public void register(){
ReleaseSubscription.getInstance().register(this);
}
//取消订阅
default public void destory(){
ReleaseSubscription.getInstance().destory(this);
}
//实际逻辑方法
public void receiveMessage (IRelease release,Object message);
}
订阅者适配器
public abstract class SubAdapter implements ISubscription {
protected String name;
protected String messageType;
public String getName() {
return name;
}
@Override
public abstract void receiveMessage(IRelease release, Object message);
@Override
public String getMessageType() {
return this.messageType;
}
public SubAdapter(String name, String messageType) {
this.name = name;
this.messageType = messageType;
register();
}
public SubAdapter(String messageType) {
this.messageType = messageType;
register();
}
}
订阅者实现类
public class ExampleSubInstance extends SubAdapter{
/**
*此类的构造方法受到了父类构造方法的限制,这就是适配器的优势所在,在实现具体累的时候不会影响整体的代码逻辑。
*
*/
public ExampleSubInstance(String name, String messageType) {
super(name, messageType);
}
public ExampleSubInstance(String messageType) {
super(messageType);
}
//具体业务逻辑
@Override
public void receiveMessage(IRelease release, Object message) {
System.out.println(this.name+" 实例收到 "+release.getName()+" 实例消息:"+message.toString()+",消息类型为"+this.messageType+"。线程名:"+Thread.currentThread().getName());
}
}
消息广播器
public class ReleaseSubscription {
//每种消息类型对应订阅者
public static Map<String,ArrayList<ISubscription>> subMap = null;
//发布器实例化对象
public static ReleaseSubscription releaseSubscription = null;
/**
* 触发广播
* @param release 消息发布者实例
* @param message 触发消息
*/
public void eventSyns(IRelease release, Object message){
for(ISubscription sub:subMap.get(release.getMessageType())){
sub.receiveMessage(release,message);
}
}
/**
* 取消订阅
* @param sub 取消订阅实例
* @return 是否成功
*/
public boolean destory(ISubscription sub){
if(subMap.get(sub.getMessageType()) == null||subMap.get(sub.getMessageType()).contains(sub)){return false;}
subMap.get(sub.getMessageType()).remove(sub);
return true;
}
/**
* 将订阅者加入相应列表
* @param sub 订阅者实例
*/
public void register(ISubscription sub){
if(sub.getMessageType() == null)throw new IllegalArgumentException("订阅失败,订阅者消息类型不能为空!");
if(subMap.get(sub.getMessageType()) == null){
subMap.put(sub.getMessageType(),new ArrayList<ISubscription>());
}
subMap.get(sub.getMessageType()).add(sub);
}
/**
* 实例化对象时,初始化订阅者map
* 此处便是标准单例模式的完整实现,它能够保证整个调用中,订阅列表总是唯一。
* 并且由于加了锁,所以在多线程调用时也不会影响它的唯一性。
*/
private ReleaseSubscription(){
if(subMap == null){
synchronized (ReleaseSubscription.class){
if(subMap == null){
subMap = new HashMap<>();
}
}
}
}
/**
* 获取发布器实例
* @return 发布器实例
*/
public static ReleaseSubscription getInstance(){
if (releaseSubscription == null){
releaseSubscription = new ReleaseSubscription();
}
return releaseSubscription;
}
}
让我们来执行一下吧
我们注册了两个发布类型,三个不同的订阅者订阅了相应的消息类型。
当我们只触发第一种类型时,订阅Type01的订阅者将会触发他们的实际业务逻辑。
public class ExampleMain {
/**
* 发布订阅者模式
* 此模式适用于一对多的业务场景。有效的降低了不同逻辑方法之间的耦合度,并且便于扩展。
* 总体流程为,实例化相关对象之后,发布者触发事件,消息广播器通知到订阅此消息类型的所有订阅者,订阅者会执行实际逻辑receiveMessage()方法。
* @param args
*/
public static void main(String[] args) {
//实例化不同消息发布者
IRelease release_01 = new ReleaseImpl("发布者01","Type01");
IRelease release_02 = new ReleaseImpl("发布者01","Type02");
//实例化订阅者
new ExampleSubInstance("订阅者01","Type01");
new ExampleSubInstance("订阅者02","Type01");
new ExampleSubInstance("订阅者03","Type02");
//消息发布者触发事件
release_01.eventSyns("执行!");
}
}
执行结果
订阅者01 实例收到 发布者01 实例消息:执行!,消息类型为Type01。线程名:main
订阅者02 实例收到 发布者01 实例消息:执行!,消息类型为Type01。线程名:main
到此我们就完整的实现了发布订阅者模式,并且其中还交叉使用了单例模式与缺省适配器模式,以往以上的分享可以对你有所帮助。
以上。