观察者模式的基本定义和基础实现就不介绍了,可以参考这篇文章
我们接着这个思路来。
在上文中,最后的实现部分,简单来说,是通过两个接口来完成的。
1 Observer
2 Subject
这里面包括几个过程:
1 定义一个Subject接口
2 定义一个Observer接口
3 实现一个Subject类
4 实现一个Observer类
5 向一个Suject类注册Observer
6 当Subject法触发时,通知每个Observer
除了前两步,体现了一定的抽象性,后面的所有部分,都是在将原本简单的东西复杂化。有人说,这就是面向对象,这就是接口编程。思路也许是对的,但这种实现方式,我并不认同。
所以今天,我们换一种思路,把这些复杂,冗余,糟糕的接口全部去掉,回归到观察者模式最简单的部分,回归到用户的角度。
如果你是一个用户,你想使用观察者模式,其实只需要搞清楚两点:
1 触发的事件(Subject)
2 后续的通知事件(Observer)
换句话说,我对你观察者本身的实现方式并不关心,我只想看到这样的形式:
class MySubject {
@Subject
void event() {
.....
}
}
class MyObserver {
@Observer
void update() {
.....
}
}
其他的所有,对我来说,都是多余的。
下面来说实现思路:
1 我们借助spring来管理我们的bean
2 在spring加载bean时,我们通过注解知道一个类是否应用了观察者模式
3 如果类使用了观察者模式,则将所有的Observer注册到一个中间结构中
4 当Subject的event事件触发时,我们通过aop的方式,调用中间结构中的Observer方法
接下来是具体实现:
首先定义两个注解。
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documentedpublic
@interface Subject {
String value();
}
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Subject {
String value();
}
这两个注解就是我们的Subject和Observer。
接下来是在spring中发现Observer注解,并将其注册到一个中间结构中,我们先看中间结构的定义:
public class Subscriber {
@AllArgsConstructor
@Data
public static class MethodHolder {
Method method;
Object target;
public void execute(Object param) {
try {
method.invoke(target, param);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
static Map<String, List<MethodHolder>> observerMethodMap = new ConcurrentHashMap<String, List<MethodHolder>>();
public static void addMethod(String id, Method method, Object target) {
if (null == observerMethodMap.get(id)) {
observerMethodMap.put(id, new ArrayList<MethodHolder>());
}
observerMethodMap.get(id).add(new MethodHolder(method, target));
}
public static void notify(String id, Object param) {
List<MethodHolder> methodHolders = observerMethodMap.get(id);
if (null != methodHolders) {
for (MethodHolder methodHolder : methodHolders) {
methodHolder.execute(param);
}
}
}
}
之后是在spring中发现注解的过程,我们通过实现BeanPostProcessor接口来实现,这里不展开BeanPostProcessor接口的作用(如果希望详细了解,请自行百度)。代码如下:
@Service
public class ObserverBeanProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> clazz = bean.getClass();
List<Method> methodList = ReflectTool.getObserverMethod(clazz.getDeclaredMethods(), Observer.class);
for (Method method : methodList) {
Observer observer = method.getAnnotation(Observer.class);
String id = observer.value();
Subscriber.addMethod(id, method, bean);
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
最后我们需要对Subject的事件做切面,代码如下:
@Service
@Aspect
public class SubjectAspect {
@Pointcut("@annotation(com.littlersmall.observer.annotation.Subject)")
public void pointcut() {
}
@Around("pointcut()")
public Object doAfter(final ProceedingJoinPoint proceedingJoinPoint) {
Object res = null;
String id = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod().getAnnotation(Subject.class).value();
try {
res = proceedingJoinPoint.proceed();
Subscriber.notify(id, res);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return res;
}
}
当这一切都准备好之后。
你就可以方便的使用观察者模式了。
请忘记那些恶心而复杂的接口吧,回归到最本质的调用,还是以上文的事例为例子,只是这一次,简洁清晰了很多:
1 WeatherData
@Data
public class WeatherDataModel {
float temperature;
float humidity;
float pressure;
List<Float> forecastTemperatures;
}
2 Weather主题定义(subject)
@Service
public class Weather {
@Subject("weatherChanged")
public WeatherDataModel measurementChanged(WeatherDataModel weatherDataModel) {
System.out.println("weather changed: ");
return weatherDataModel;
}
}
3 显示当前天气的公告牌CurrentConditionsDisplay(observer1)
@Service
public class CurrentConditionsDisplay {
@Observer("weatherChanged")
public void currentConditions(WeatherDataModel weatherDataModel) {
System.out.println("温度: " + weatherDataModel.getTemperature());
System.out.println("湿度: " + weatherDataModel.getHumidity());
System.out.println("气压: " + weatherDataModel.getPressure());
}
}
4 显示未来几天天气的公告牌ForecastDisplay(observer2)
@Service
public class ForecastDisplay {
@Observer("weatherChanged")
public void futureConditions(WeatherDataModel weatherDataModel) {
for (int i = 0; i < weatherDataModel.getForecastTemperatures().size(); i++) {
System.out.println("day: " + i + " " + weatherDataModel.getForecastTemperatures().get(i) + "℃");
}
}
}
5 main函数
public class ObserverTest {
public static void main(String[] args) {
WeatherDataModel weatherDataModel = new WeatherDataModel();
weatherDataModel.setTemperature(22f);
weatherDataModel.setHumidity(0.8f);
weatherDataModel.setPressure(1.2f);
weatherDataModel.setForecastTemperatures(new ArrayList<Float>());
weatherDataModel.getForecastTemperatures().add(22f);
weatherDataModel.getForecastTemperatures().add(23f);
weatherDataModel.getForecastTemperatures().add(27f);
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Weather weather = ac.getBean(Weather.class);
weather.measurementChanged(weatherDataModel);
}
}
github地址
总结
设计模式的本质思想,是将不变的框架固化,而将变化的部分抽象出来。
在以前,我们只能通过一层层的接口嵌套,把变化的东西剥离,集中,再抽象。这种方式,往往会将原本的简单代码,过度设计。换句话说,我们牺牲了程序的简洁性,来换取逻辑的清晰性。
现在,有了aop这种利器,终于可以鱼和熊掌兼得了。
有机会,把java的设计模式,用aop一个一个实现一遍。
简洁的,才是美好的。