相关阅读:
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(六)事件通知模式解耦过程
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
HikariPool源码(二)设计思想借鉴
【极客源码】JetCache源码(一)开篇
【极客源码】JetCache源码(二)顶层视图
人在职场(一)IT大厂生存法则
1. 原始需求和目标
2. 迭代1
需求看起来比较简单,监控下网络变化,如果从非wifi变化到wifi,则调用安装器进行版本检测和安装。其类结构如下:
2.1. 实现代码
public class NetworkListener {
public void networkChanged(NetworkType from, NetworkType to) {
if (from != null && to != null) {
notifyInstaller(from, to);
}
}
private void notifyInstaller(NetworkType from, NetworkType to) {
if (to == NetworkType.WIFI && from != NetworkType.WIFI) {
// 代码耦合,如果有新的逻辑依赖网络罗变化,则要则这里修改代码
Installer installer = new Installer();
if (installer.hasNewVersion()) {
installer.downloadApk();
installer.upgrade();
}
}
}
}
class Installer {
public boolean hasNewVersion() {
System.out.println("has new version: true");
return true;
}
public void downloadApk() {
System.out.println("downloadAPk");
}
public void upgrade() {
System.out.println("upgrade new version apk.");
}
}
enum NetworkType {
NONE,
MOBILE,
WIFI
}
2.2. 验证代码
public class EntryDemo {
public static void main(String[] args) {
NetworkListener listener = new NetworkListener();
listener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
}
}
2.3. 输出
has new version: true
downloadAPk
upgrade new version apk.
至此,需求实现,没几分钟就搞定,但这种实现方式将Installer和NetworkListener耦合,如果将来有新的业务也需要根据网络变化进行处理,则要在NetworkListener内部修改代码,因此需要对这点解耦。
3. 迭代2
迭代2类结构如下:
1.新增NetworkChangedEventSubscriber接口,可订阅NetworkChangedEvent
2.NetworkListener可添加NetworkChangedEventSubscriber,当发生NetworkChangedEvent时则通知它。
3.1. 实现代码
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @ClassName NetworkListener
* @Description
* @Author 铿然一叶
* @Date 2020/5/31 15:29
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class NetworkListener {
private Set<NetworkChangedEventSubscriber> subscribers;
public NetworkListener() {
subscribers = new CopyOnWriteArraySet<>();
}
public void networkChanged(NetworkType from, NetworkType to) {
if (subscribers != null) {
NetworkChangedEvent event = new NetworkChangedEvent(from, to);
for (NetworkChangedEventSubscriber subscriber: subscribers) {
subscriber.notifyNetworkChanged(event);
}
}
}
// 可以添加多个Subscriber
public void addNetworkChangedEventSubscriber(NetworkChangedEventSubscriber subscriber) {
subscribers.add(subscriber);
}
// 封装网络变化事件
public static class NetworkChangedEvent {
NetworkType from;
NetworkType to;
public NetworkChangedEvent(NetworkType from, NetworkType to) {
this.from = from;
this.to = to;
}
}
// 定义Subscriber接口
public interface NetworkChangedEventSubscriber {
void notifyNetworkChanged(NetworkChangedEvent event);
}
}
// 实现Subscriber接口
class Installer implements NetworkListener.NetworkChangedEventSubscriber {
@Override
public void notifyNetworkChanged(NetworkListener.NetworkChangedEvent event) {
if (event != null && event.from != null && event.to != null) {
if (event.to == NetworkType.WIFI && event.from != NetworkType.WIFI) {
if (hasNewVersion()) {
downloadApk();
upgrade();
}
}
}
}
public boolean hasNewVersion() {
System.out.println("has new version: true");
return true;
}
public void downloadApk() {
System.out.println("downloadAPk");
}
public void upgrade() {
System.out.println("upgrade new version apk.");
}
}
enum NetworkType {
NONE,
MOBILE,
WIFI
}
3.2. 验证代码
public class EntryDemo {
public static void main(String[] args) {
NetworkListener networkListener = new NetworkListener();
// 已经解耦,只要添加新的subscriber则可
networkListener.addNetworkChangedEventSubscriber(new Installer());
networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
}
}
这种方式的缺点是必须获得networkListener实例才能添加listener,导致每次添加新的listener都要在创建NetworkListener的地方修改代码。
4. 迭代3
迭代3类结构如下: 新增NetworkChangedEventRegistry来负责注册和获取NetworkChangedEventSubscriber,使得注册Subscriber和获取Subscriber解耦。
4.1. 实现代码
4.1.1. NetworkListener.java
import java.util.Set;
/**
* @ClassName NetworkListener
* @Description
* @Author 铿然一叶
* @Date 2020/5/31 15:55
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class NetworkListener {
private Set<NetworkChangedEventSubscriber> subscribers;
public void networkChanged(NetworkType from, NetworkType to) {
NetworkChangedEvent event = new NetworkChangedEvent(from, to);
// 和subscriber解耦
Set<NetworkChangedEventSubscriber> subscribers = NetworkChangedEventRegistry.getInstance().getSubscribers();
if (subscribers != null) {
for (NetworkChangedEventSubscriber subscriber : subscribers) {
subscriber.notifyNetworkChanged(event);
}
}
}
public static class NetworkChangedEvent {
NetworkType from;
NetworkType to;
public NetworkChangedEvent(NetworkType from, NetworkType to) {
this.from = from;
this.to = to;
}
}
public interface NetworkChangedEventSubscriber {
void notifyNetworkChanged(NetworkChangedEvent event);
}
}
enum NetworkType {
NONE,
MOBILE,
WIFI
}
4.1.2. Installer.java
public class Installer implements NetworkListener.NetworkChangedEventSubscriber {
@Override
public void notifyNetworkChanged(NetworkListener.NetworkChangedEvent event) {
if (event != null && event.from != null && event.to != null) {
if (event.to == NetworkType.WIFI && event.from != NetworkType.WIFI) {
if (hasNewVersion()) {
downloadApk();
upgrade();
}
}
}
}
public boolean hasNewVersion() {
System.out.println("has new version: true");
return true;
}
public void downloadApk() {
System.out.println("downloadAPk");
}
public void upgrade() {
System.out.println("upgrade new version apk.");
}
}
4.1.3. NetworkChangedEventRegistry.java
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @ClassName NetworkChangedEventRegistry
* @Description
* @Author 铿然一叶
* @Date 2020/5/31 15:58
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class NetworkChangedEventRegistry {
// 单例模式写法
private volatile static NetworkChangedEventRegistry registry;
private Set<NetworkListener.NetworkChangedEventSubscriber> subscribers;
public NetworkChangedEventRegistry() {
subscribers = new CopyOnWriteArraySet<>();
}
// 单例模式
public static NetworkChangedEventRegistry getInstance() {
if (registry == null) {
synchronized (NetworkChangedEventRegistry.class) {
if (registry == null) {
registry = new NetworkChangedEventRegistry();
}
}
}
return registry;
}
public void register(NetworkListener.NetworkChangedEventSubscriber subscriber) {
if (subscriber != null) {
subscribers.add(subscriber);
}
}
public void unregister(NetworkListener.NetworkChangedEventSubscriber subscriber) {
if (subscriber != null) {
subscribers.remove(subscriber);
}
}
public Set<NetworkListener.NetworkChangedEventSubscriber> getSubscribers() {
return subscribers;
}
}
4.2. 验证代码
public class EntryDemo {
public static void main(String[] args) {
// 通过两个独立方法来说明subscriber的注册和listenr已经解耦
register();
networkChanged();
}
private static void register() {
NetworkChangedEventRegistry.getInstance().register(new Installer());
}
private static void networkChanged() {
NetworkListener networkListener = new NetworkListener();
networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
}
}
到这里,实现已经几近完美,从解耦角度来看没有什么可挑剔的,但软件的质量属性不仅仅是解耦,还有可靠性等等。
而在当前实现中,通知subscriber是同步模式,如果有多个subscriber,其中一个subscriber的执行时间很长,排在它后面的subscriber收到通知就不及时,可能出现收到通知时网络再次发生变化,已经不再是WIFI网络。
因此为了及时通知每个subscriber,要使用异步模式,但可能有的可需要同步,有的需要异步,于是我们引入Dispatcher来解决这个问题。
5. 迭代4
迭代4类结构如下:
新增Dispatcher来派发event给Subscriber,通过不同的实现类可决定是同步还是异步方式。
5.1. 实现代码
5.1.1. NetworkListener.java
import java.util.Set;
/**
* @ClassName NetworkListener
* @Description
* @Author 铿然一叶
* @Date 2020/5/31 16:26
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class NetworkListener {
private Dispatcher dispatcher;
// 通过依赖注入解耦,由调用者传入具体实现类
public NetworkListener(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public void networkChanged(NetworkType from, NetworkType to) {
NetworkChangedEvent event = new NetworkChangedEvent(from, to);
Set<NetworkChangedEventSubscriber> subscribers = NetworkChangedEventRegistry.getInstance().getSubscribers();
if (subscribers != null) {
for (NetworkChangedEventSubscriber subscriber : subscribers) {
// 由Dispatcher来派发事件
dispatcher.distribute(subscriber, event);
}
}
}
public static class NetworkChangedEvent {
NetworkType from;
NetworkType to;
public NetworkChangedEvent(NetworkType from, NetworkType to) {
this.from = from;
this.to = to;
}
}
public interface NetworkChangedEventSubscriber {
void notifyNetworkChanged(NetworkChangedEvent event);
}
}
enum NetworkType {
NONE,
MOBILE,
WIFI
}
5.1.2. Dispatcher
public interface Dispatcher {
void distribute(NetworkListener.NetworkChangedEventSubscriber subscriber, NetworkListener.NetworkChangedEvent event);
}
/**
* @ClassName SyncDispatcher
* @Description 同步Dispatcher
* @Author 铿然一叶
* @Date 2020/5/31 16:37
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class SyncDispatcher implements Dispatcher {
@Override
public void distribute(NetworkListener.NetworkChangedEventSubscriber subscriber, NetworkListener.NetworkChangedEvent event) {
if (subscriber != null && event != null) {
System.out.println("sync distribute start.");
subscriber.notifyNetworkChanged(event);
System.out.println("sync distribute end.");
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName AsyncDispatcher
* @Description 异步Dispatcher
* @Author 铿然一叶
* @Date 2020/5/31 16:38
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class AsyncDispatcher implements Dispatcher {
private ExecutorService executorService;
public AsyncDispatcher() {
// 通过线程池提高性能,异步派发的线程处理方式可以自行根据需要修改
executorService = Executors.newSingleThreadExecutor();
}
@Override
public void distribute(NetworkListener.NetworkChangedEventSubscriber subscriber, NetworkListener.NetworkChangedEvent event) {
if (subscriber != null && event != null) {
System.out.println("async distribute start.");
executorService.execute(()->subscriber.notifyNetworkChanged(event));
System.out.println("async distribute end.");
}
}
}
5.2. 验证代码
/**
* @ClassName EntryDemo
* @Description
* @Author 铿然一叶
* @Date 2020/5/31 16:43
* @Version 1.0
* 掘金:https://juejin.im/user/3544481219739870
**/
public class EntryDemo {
public static void main(String[] args) {
// 通过两个独立方法来说明subscriber的注册和listenr已经解耦
register();
syncDistribute();
asyncDistribute();
}
private static void register() {
NetworkChangedEventRegistry.getInstance().register(new Installer());
}
private static void syncDistribute() {
Dispatcher dispatcher = new SyncDispatcher();
NetworkListener networkListener = new NetworkListener(dispatcher);
networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
}
private static void asyncDistribute() {
Dispatcher dispatcher = new AsyncDispatcher();
NetworkListener networkListener = new NetworkListener(dispatcher);
networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
}
}
5.3. 输出
sync distribute start.
has new version: true
downloadAPk
upgrade new version apk.
sync distribute end.
async distribute start.
async distribute end.
has new version: true
downloadAPk
upgrade new version apk.
可以看到同步模式和异步模式的差别,异步模式派发后不需要等待subscriber执行,派发立即就结束了,subscriber通过线程执行。
6. 总结
最终事件通知模式抽象后的类结构为:
通过本章我们掌握了如下知识点:
1. 事件通知模式实现时需要考虑的几个要点,以及核心类。
2. 通过依赖注入实现解耦是一种经典解耦模式,要善用。(参考Dispatcher的注入)
3. 软件质量属性不仅仅是解耦,可扩展,其他属性也要考虑,例如可靠性。
end.