java项目框架设计模式


转载: https://blog.csdn.net/javaxuexi123/article/details/79076356

说起来设计模式,大家应该都耳熟能详,设计模式代表了软件设计的最佳实践,是经过不断总结提炼出来的代码设计经验的分类总结,这些模式或者可以简化代码,或者可以是代码逻辑开起来清晰,或者对功能扩展很方便…。

设计模式按照使用场景可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。

    • 创建型模式(Creational Patterns)
      对对象的实例化过程进行抽象,这使得一个系统可以不用关心这些对象是如何创建,组合,呈现的,对于类创建模式来说通过使用继承改变实例化的类,对于对象创建模式来说通过使用代理来实例化所需要的对象。

    • 结构型模式(Structural Patterns)
      通过对多个类和对象进行组合得到复杂结构的类,一般使用继承继承或者成员变量引用形式来实现。

    • 行为型模式(Behavioral Patterns)
      行为模式不仅表达了对象和类,还表达了他们之间的交互,涉及到了对象和算法的分配。

 

一、责任链设计模式(Chain of Responsibility Pattern)

1.1 介绍

责任链模式是把多个对象串联起来形成一个链状结构,让每个对象都有机会对事件发送者的请求进行处理。责任链模式是设计模式中的行为模式,设计意图是为了使事件发送者和事件接受者之间解耦。通常责任链链中的每个对象都有下一个对象的引入(例如tomcat 里面StandardPipeline用来管理valve),或者有个同一个链管理工厂里面使用数组存放了所有的对象(例如tomcat里面ApplicationFilterChain用来关系filter)。

1.2 Tomcat中Valve链

Tomcat中StandardEngine,StandardHost,StandardContext里面都有自己StandardPipeline,下面以StandardEngine里面StandardPipeline为例讲解

 
从上面类图可知道每个Valve都要继承ValveBase类,该类里面有一个Valve的引用,实际是链中下一个节点对象,Valve就是通过每个Valve里面的next串联为链的。
 

每个valve的invoke方法里面调用next.invoke激活链中下一个节点,并且StandardEngine,StandardHost,StandardContext都有一个basic valve这个valve在链的末尾用来激活子容器的valve链。

1.3 Tomcat中Filter链

Tomcat中Filter链是使用ApplicationFilterChain来管理的,具体结构如下图:

可知Filter链不是像Valve一样在内部维护下个节点的引用,而是在ApplicationFilterChain中搞了个数组存放所有的Filter,并通过n统计Filter总个数,pos是当前filter的下标。

ApplicationFilterChain的doFilter代码如下:

public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {
        ...
        internalDoFilter(request,response);
        ...
    }
 
private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {
 
        // Call the next filter if there is one
        if (pos < n) {
 
            //获取filter链中下标为pos的filter
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
                filter = filterConfig.getFilter();
                support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response);
 
                if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
 
                ...
 
                //调用自定义filter的dofilter方法
                filter.doFilter(request, response, this);
 
                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,filter, request, response);
            } 
            ....
 
    }
   .....
}
 

注:这两种方式的区别是啥,就是说那些场景下使用2.2,什么情况下使用2.3这个目前还没有搞清楚有

 

1.4 使用场景

  • 当一个请求需要根据请求参数的不同由不同对象来处理时候。
  • 当一个请求需要固定对象顺序处理,并且可扩展性的在固定顺序里面插入新的对象进行处理时候。

二、 策略模式(Strategy Pattern)

2.1 介绍

策略模式属于行为性模式,它定义一系列的算法对象,使用时候可以使它们相互替换。

2.2 Spring中bean实例化策略

首先看下类图:

从图知道:接口InstantiationStrategy是实例化策略接口类,它定义了三个实例化接口,然后SimpleInstantiationStrategy实现了该策略,它主要做一些简单的根据构造函数实例号bean的工作,然后CglibSubclassingInstantiationStrategy又继承了SimpleInstantiationStrategy新增了方法注入方式根据cglib生成代理类实例化方法。

在AbstractAutowireCapableBeanFactory中管理了该策略的一个对象,默认是CglibSubclassingInstantiationStrategy策略,运行时候可以通过setInstantiationStrategy改变实例化策略,如果你自己写个个策略的话。

2.3 Spring中Aop代理策略

首先看AopProxyFactory接口类提供了createAopProxy接口,这个是策略模式的接口方法。然后DefaultAopProxyFactory实现了该接口作为策略的实现者。然后ProxyCreatorSupport里面引用了AopProxyFactory,并且提供了get,set方法用来运行时改变策略,这里Spring只实现了DefaultAopProxyFactory这一个策略,如果需要自己也可以写个。

DefaultAopProxyFactory里面的createAopProxy的逻辑如下,可以在运行时根据参数决定用Cglib策略还是JDK动态代理策略生成代理类:

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
 
        //如果XML打开了优化开关,或者设置为了代理目标类,或者目前类没有接口
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
 
            //如果有接口,或者通过Proxy.newProxyInstance生成的,则使用jdk动态代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
 
            //使用cglib
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            //使用jdk动态代理
            return new JdkDynamicAopProxy(config);
        }
    }
 

另外AopProxy也是一个策略接口类,具体实现的策略为JdkDynamicAopProxy,CglibAopProxy,ObjenesisCglibAopProxy

2.4 Tomcat中Digester解析server.xml

tomcat中的Digester是为了解析server.xml的,其中每个元素都有一个解析规则就是Rule ,类图如下:
DigestER一开始先指定不同的解析策略(Rule),然后在具体解析Server.xml时候根据节点不同使用不同解析策略来解析节点。

如图在解析每个节点时候会先找到该节点对应的解析策略,然后循环去调用所有解析策略的方法去处理。

2.5 使用场景

  • 运行时根据条件的不同使用不同的策略处理一个事情,与责任链不同在于,责任链是一个链条,一个事情可以被责任链里面所有节点处理,而 策略模式则是只有有一个对象来处理。

三、 门面模式(Facade Pattern)

3.1 介绍

门面模式是一种结构性模式,它通过新增一个门面类对外暴露系统提供的一部分功能,或者屏蔽了内部系统的复杂性,对外部仅仅暴露一个简单的接口,或者通过调用不同的服务对外提供统一的接口,让使用者对这些内部服务透明化。

3.2 模板引擎Velocity中门面模式使用

Velocity里面的VelocityEngine和Velocity类都是RuntimeInstance类的门面,后者提供了模板渲染的所有功能,前两者则是内部维护RuntimeInstance的实例,具体工作还是委托给RuntimeInstance来实现。
关于Veloctiy可以参考:https://www.atatech.org/articles/78435

如图 RuntimeInstance提供了Velocity引擎的所用功能,VelocityEngine内部直接引用了RuntimeInstance的一个实例,VelocityEngine对外暴露的服务都是委托RuntimeInstance实现,并且每次new一个VelocityEngine内部都会有RuntimeInstance的一个实例被创建。而Velocity类调用了单例模式类RuntimeSingleton里面的方法,RuntimeSingleton又是RuntimeInstance的一个单例模式。

3.3 使用场景

  • 当需要对外屏蔽一个系统的复杂性时候可以考虑使用门面模式对外提供简单可读性高的接口类
  • 当需要对外部暴露系统一部分权限的接口时候可以考虑使用门面模式减少系统权限。
  • 当系统需要调用不同服务汇总后在对外提供服务时候可以考虑使用门面模式对外屏蔽细节,之暴露一个接口。

四、装饰器模式(Decorator Pattern)

4.1 介绍

装饰器模式是一种结构性模式,它作用是对对象已有功能进行增强,但是不改变原有对象结构。这避免了通过继承方式进行功能扩充导致的类体系臃肿。

4.2 Spring中BeanDefinitionDecorator

先看下类图:

如图ScopedProxyBeanDefinitionDecorator实现了decorate方法用来对scope作用域为request的bean定义进行包装。
具体时序图为:

class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator {
 
    private static final String PROXY_TARGET_CLASS = "proxy-target-class";
 
    @Override
    public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
        boolean proxyTargetClass = true;
        if (node instanceof Element) {
            Element ele = (Element) node;
            if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
                proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
            }
        }
 
        // 创建scoped的代理类,并注册到容器
        BeanDefinitionHolder holder =
                ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
        String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
        parserContext.getReaderContext().fireComponentRegistered(
                new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
        return holder;
    }
 
}
 

 

关于ScopedProxyBeanDefinitionDecorator干啥用的那:

  <bean id="lavaPvgInfo" class="com.alibaba.lava.privilege.PrivilegeInfo"
        scope="request">
        <property name="aesKey" value="666" />
        <aop:scoped-proxy />
    </bean> 

 

其实就是处理<aop:scoped-proxy />的,具体作用是包装lavaPvgInfo的bean定义为ScopedProxyFactoryBean,作用是实现request作用域bean.

4.3 commons-collections包中ListUtils

 

ListUtils中的四个方法分别依赖list的四种装饰器类对List功能进行扩充和限制。
其中FixedSizeList类通过禁止add/remove操作保证list的大小固定,但是可以修改元素内容
其中UnmodifiableList类通过禁用add,clear,remove,set,保证list的内容不被修改
其中SynchronizedList类通过使用Lock 来保证add,set,get,remove等的同步安全
其中LazyList类则当调用get方法发现list里面不存在对象时候,自动使用factory创建对象.

4.4 使用场景

  • 在不改变原有类结构基础上,新增或者限制或者改造功能时候。

五、模板设计模式(Template Pattern)

5.1 前言

模板设计模式是一种行为设计模式,它使用一个抽象类定义了一个模板,这个模板里面定义了一系列的接口,子类则只需要继承该抽象类并且根据需要重写一部分接口。

5.2 ibatis2中AbstractDAOTemplate

如图AbstractDAOTemplate是抽象模板类,里面定义了configure方法,configure方法里面定义了好多protected方法,其中就有些是抽象方法。类SpringDAOTemplate,IbatisDAOTemplate,GenericCIDAOTemplate,GenericSIDAOTemplate则继承了AbstractDAOTemplate类并重写了一部分方法。

5.3 Tomcat中Digester里面的Rule

tomcat中的Digester是为了解析server.xml的,其中每个元素都有一个解析规则就是Rule ,类图如下:

如图:Rule是抽象类,对于每个解析的节点来说Rule提供了解析所需所有的方法,而他的子类则根据自己的特殊性重写一部分方法来支持自己的特性。

5.4 Tomcat中Endpoint

如图AbstractEndpoint是个抽象类,定义了Endpoint的所有接口,然后JIoEndpoint继承了该类并且重写了一部分重要的方法实现了BIO方式endpoint,NioEndpoint则重写了方法实现了NIO的endpoint.

5.5使用场景

  • 当多个子类具有共同的操作流程逻辑,并且其中某些流程节点操作需要自己定制化时候。

六、 建造者模式(Builder Pattern)

6.1 前言

建造者模式是一种创建型模式,将一个复制对象的创建屏蔽到接口内部,用户使用时候只需要传递固定的参数,内部就会执行复杂逻辑后返回会用户需要的对象,用户不需要知道创建的细节。

6.2 Mybatis中的SqlSessionFactoryBuilder

如图mybaits中的SqlSessionFactoryBuilder就是典型的创建者模式,他内部有多个build方法,根据参数的不同创建出SqlSessionFactory对象,使用者只需要传递具体参数而不用关系内部是如何创建出需要的对象的。SqlSessionFactoryBean大家应该很熟悉,在xml里面配置的。

6.3 使用场景

  • 当一个对象比较复杂并且容易出错时候,可以考虑这种模式去屏蔽创造细节。

 

七、命令模式(Command Pattern)

7.1 介绍

命令模式是一种行为模式,通过把命令封装为一个对象,命令发送者把命令对象发出后,就不去管是谁来接受处理这个命令,命令接受者接受到命令对象后进行处理,也不用管命令是谁发出的,所以命令模式实现了发送者与接受者之间的解耦,而具体把命令发送给谁还需要一个控制器。

7.2 Tomcat中命令模式

tomcat作为一个服务器本身会接受外部大量请求,当一个请求过来后tomcat根据域名去找对应的host,找到host后会根据应用名去找具体的context(应用),然后具体应用处理请求。对于具体host来说他不关心这个请求是谁给的,对应请求来说他不必关心谁来处理,但是两者是通过request封装请求对象进行关联起来。

tomcat中Connector作为命令发出者,Connector接受到请求后把请求内容封装为request对象(命令对象),然后使用CoyoteAdapter作为分发器把请求具体发配到具体的host,host在根据request对象找到具体的context,至此找到了具体的应用,交给具体应用处理。

另外对于使用springmvc的应用来说,上面找到具体应用,但是具体交给那个controller来处理那,这是不是也是命令模式的使用那。

7.3 使用场景

    • 当事件发送者和接受者直接需要完全解耦(直接并不存在引用关系)时候。

 

  

总结

设计模式中每一个模式都描述了在我们工作中不断重复发生的问题,以及问题的解决方案,所以真正掌握设计模式可以避免我们做不必要的重复劳动。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM