觀察者模式的基本定義和基礎(chǔ)實(shí)現(xiàn)就不介紹了波附,可以參考這篇文章
我們接著這個思路來贝或。
在上文中壤躲,最后的實(shí)現(xiàn)部分霞掺,簡單來說砸紊,是通過兩個接口來完成的传于。
1 Observer
2 Subject
這里面包括幾個過程:
1 定義一個Subject接口
2 定義一個Observer接口
3 實(shí)現(xiàn)一個Subject類
4 實(shí)現(xiàn)一個Observer類
5 向一個Suject類注冊O(shè)bserver
6 當(dāng)Subject法觸發(fā)時,通知每個Observer
除了前兩步醉顽,體現(xiàn)了一定的抽象性沼溜,后面的所有部分,都是在將原本簡單的東西復(fù)雜化游添。有人說系草,這就是面向?qū)ο螅@就是接口編程否淤。思路也許是對的悄但,但這種實(shí)現(xiàn)方式,我并不認(rèn)同石抡。
所以今天,我們換一種思路助泽,把這些復(fù)雜啰扛,冗余,糟糕的接口全部去掉嗡贺,回歸到觀察者模式最簡單的部分隐解,回歸到用戶的角度。
如果你是一個用戶诫睬,你想使用觀察者模式煞茫,其實(shí)只需要搞清楚兩點(diǎn):
1 觸發(fā)的事件(Subject)
2 后續(xù)的通知事件(Observer)
換句話說,我對你觀察者本身的實(shí)現(xiàn)方式并不關(guān)心,我只想看到這樣的形式:
class MySubject {
@Subject
void event() {
.....
}
}
class MyObserver {
@Observer
void update() {
.....
}
}
其他的所有续徽,對我來說蚓曼,都是多余的。
下面來說實(shí)現(xiàn)思路:
1 我們借助spring來管理我們的bean
2 在spring加載bean時钦扭,我們通過注解知道一個類是否應(yīng)用了觀察者模式
3 如果類使用了觀察者模式纫版,則將所有的Observer注冊到一個中間結(jié)構(gòu)中
4 當(dāng)Subject的event事件觸發(fā)時,我們通過aop的方式客情,調(diào)用中間結(jié)構(gòu)中的Observer方法
接下來是具體實(shí)現(xiàn):
首先定義兩個注解其弊。
@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中發(fā)現(xiàn)Observer注解膀斋,并將其注冊到一個中間結(jié)構(gòu)中梭伐,我們先看中間結(jié)構(gòu)的定義:
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中發(fā)現(xiàn)注解的過程,我們通過實(shí)現(xiàn)BeanPostProcessor接口來實(shí)現(xiàn)仰担,這里不展開BeanPostProcessor接口的作用(如果希望詳細(xì)了解籽御,請自行百度)。代碼如下:
@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;
}
}
當(dāng)這一切都準(zhǔn)備好之后技掏。
你就可以方便的使用觀察者模式了。
請忘記那些惡心而復(fù)雜的接口吧项鬼,回歸到最本質(zhì)的調(diào)用哑梳,還是以上文的事例為例子,只是這一次绘盟,簡潔清晰了很多:
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 顯示當(dāng)前天氣的公告牌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函數(shù)
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地址
總結(jié)
設(shè)計(jì)模式的本質(zhì)思想鸠真,是將不變的框架固化,而將變化的部分抽象出來龄毡。
在以前吠卷,我們只能通過一層層的接口嵌套,把變化的東西剝離沦零,集中祭隔,再抽象。這種方式路操,往往會將原本的簡單代碼疾渴,過度設(shè)計(jì)。換句話說屯仗,我們犧牲了程序的簡潔性搞坝,來換取邏輯的清晰性。
現(xiàn)在魁袜,有了aop這種利器桩撮,終于可以魚和熊掌兼得了敦第。
有機(jī)會,把java的設(shè)計(jì)模式店量,用aop一個一個實(shí)現(xiàn)一遍芜果。
簡潔的,才是美好的垫桂。