浅谈springMVC中的设计模式(3)——策略模式

在springMVC中,我们常常能看到策略模式的身影,其实策略模式在我们日常开发中也是十分常见的设计模式,先来看看它的定义:策略模式是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
使用策略模式有时候可以让我们的编码从繁琐难维护的if-else中解放出来。

getDefaultStrategies
例如在DispatchServlet中的初始化组件中,用到了getDefaultStrategies方法,来决定不同组件的默认类型以实现组件的初始化操作。我们来看一下这个方法:

// 传入ApplicationContext上下文和策略接口的Class类型
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        // 相应组件的类名
        String key = strategyInterface.getName();
        // 从property中获取当前策略接口实现类的类名集合
        String value = defaultStrategies.getProperty(key);
        if (value != null) {
            // 获取策略接口所有实现类的类名
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<T>(classNames.length);
            for (String className : classNames) {
                try {
                    // 创建相应实现类的bean,并放入集合中
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    Object strategy = createDefaultStrategy(context, clazz);
                    strategies.add((T) strategy);
                }
                catch (ClassNotFoundException ex) {
                    throw new BeanInitializationException(
                            "Could not find DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]", ex);
                }
                catch (LinkageError err) {
                    throw new BeanInitializationException(
                            "Error loading DispatcherServlet's default strategy class [" + className +
                                    "] for interface [" + key + "]: problem with class file or dependent class", err);
                }
            }
            // 返回策略接口实现类的集合
            return strategies;
        }
        else {
            return new LinkedList<T>();
        }
    }
// 初始化真正调用的重载方法,默认返回策略实现类的第一个
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
        List<T> strategies = getDefaultStrategies(context, strategyInterface);
        if (strategies.size() != 1) {
            throw new BeanInitializationException(
                    "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
        }
        return strategies.get(0);
    }

我们可以看到,DispatchServlet在初始化组件时,会传入相应组件的接口,获取到该组件的实现类集合,并将第一个实现类作为默认的组件使用,例如我们来看initLocaleResolver方法,它初始化了一个默认的本地化处理组件。

private void initLocaleResolver(ApplicationContext context) {
        try {
            this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            // We need to use the default.
            this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
                        "': using default [" + this.localeResolver + "]");
            }
        }
    }

它传入了LocaleResolver.class,这个类有多个实现类,包括AcceptHeaderLocaleResolver、CookieLocaleResolver、FixedLocaleResolver等,对应了多种不同的处理方式,你可以决定用哪一种处理方式(绑定对应的组件就好了)。但试想一下,如果用if-else来决定用那种处理方式,光一个LocaleResolver,代码就将变得又长又臭,更何况springMVC还要初始化这么多其他组件。策略模式就用了面向对象的思想,用接口、继承、多态来代替if-else,增加了代码的可读性和可维护性。

其他使用到策略模式的地方
springMVC在决定request的media types时也用到了策略模式。其中的ContentNegotiationManager是最核心的一个类,其中有一个方法我们可以重点来看一下:

@Override
    // 处理请求的mediaTypes
    public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
        // 遍历绑定的strategy来处理请求,其中ContentNegotiationStrategy是一个接口,它的实现类包含了多种决定mediaTypes的策略
        for (ContentNegotiationStrategy strategy : this.strategies) {
            // 如果某一策略实现类识别的请求的mediaTypes,则返回,未识别则继续遍历
            List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
            if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
                continue;
            }
            return mediaTypes;
        }
        return Collections.emptyList();
    }

ContentNegotiationStrategy接口只有一个方法:

public interface ContentNegotiationStrategy {

    // 返回请求的mediaTypes集合,如果无法识别则抛出异常
    List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
            throws HttpMediaTypeNotAcceptableException;

}

跟上面的LocaleResolver接口一样,ContentNegotiationStrategy也有多种不同的实现类,这些实现类大都对应了不同的处理方式。策略模式在此情形下,使得在处理request的mediaTypes时,省去了大量if-else判断,实现了策略算法与调用逻辑上的解耦。

总结
从上面两个简单的例子中我们可以看到,使用策略模式的实现方式是:
定义策略接口->实现不同的策略类->利用多态或其他方式调用策略
从springMVC处理request的media types中,我们又可以学到:
当我们遇到的问题时,如果无法事先知道哪种处理方式合适,可以使用策略模式。当某一种策略模式匹配时,返回正确结果,以此解决问题
此外,策略模式使用的场景,实现的方式还有很多,这里就不一一赘述了。总之,使用策略模式对我们处理和解决问题、算法(解决方式)和使用解耦、代码的可读性和可维护性方面都有极大的好处。

猜你喜欢

转载自blog.csdn.net/ljw761123096/article/details/79686534