程序员首页投稿(暂停使用,暂停投稿)技术干货

基于切面实现观察者模式

2016-03-01  本文已影响958人  littlersmall

观察者模式的基本定义和基础实现就不介绍了,可以参考这篇文章

http://www.jianshu.com/p/d55ee6e83d66

我们接着这个思路来。
在上文中,最后的实现部分,简单来说,是通过两个接口来完成的。

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地址

https://github.com/littlersmall/observer-pattern

总结
设计模式的本质思想,是将不变的框架固化,而将变化的部分抽象出来。
在以前,我们只能通过一层层的接口嵌套,把变化的东西剥离,集中,再抽象。这种方式,往往会将原本的简单代码,过度设计。换句话说,我们牺牲了程序的简洁性,来换取逻辑的清晰性。

现在,有了aop这种利器,终于可以鱼和熊掌兼得了。

有机会,把java的设计模式,用aop一个一个实现一遍。
简洁的,才是美好的。

上一篇 下一篇

猜你喜欢

热点阅读