轉載: 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的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 使用場景
- 當事件發送者和接受者直接需要完全解耦(直接並不存在引用關系)時候。
總結
設計模式中每一個模式都描述了在我們工作中不斷重復發生的問題,以及問題的解決方案,所以真正掌握設計模式可以避免我們做不必要的重復勞動。