學習完整課程請移步 互聯網 Java 全棧工程師
一、前言
設計模式代表了軟件設計的最佳實踐,是經過不斷總結提煉出來的代碼設計經驗的分類總結,這些模式或者可以簡化代碼,或者可以是代碼邏輯開起來清晰,或者對功能擴展很方便
設計模式按照使用場景可以分為三大類:
-
創建型模式(Creational Patterns):對對象的實例化過程進行抽象,這使得一個系統可以不用關心這些對象是如何創建,組合,呈現的,對于類創建模式來說通過使用繼承改變實例化的類,對于對象創建模式來說通過使用代理來實例化所需要的對象。
- 工廠模式
- 原型模式
- 構建者模式
- 單例模式
-
結構型模式(Structural Patterns):通過對多個類和對象進行組合得到復雜結構的類,一般使用繼承繼承或者成員變量引用形式來實現。
- 適配器模式
- 橋接模式
- 過濾器模式
- 組合模式
- 裝飾者模式
- 門面模式
- 享元模式
- 代理模式
-
行為型模式(Behavioral Patterns):行為模式不僅表達了對象和類,還表達了他們之間的交互,涉及到了對象和算法的分配。
- 責任鏈模式
- 命令模式
- 解釋器模式
- 迭代器模式
- 中介者模式
- 備忘錄模式
- 觀察者模式
- 狀態模式
- 空對象模式
- 策略模式
- 模板方法模式
- 訪問者模式
二、責任鏈設計模式(Chain of Responsibility Pattern)
介紹
責任鏈模式是把多個對象串聯起來形成一個鏈狀結構,讓每個對象都有機會對事件發送者的請求進行處理。責任鏈模式是設計模式中的行為模式,設計意圖是為了使事件發送者和事件接受者之間解耦。通常責任鏈鏈中的每個對象都有下一個對象的引入(例如 Tomcat 里面 StandardPipeline
用來管理 valve
),或者有個同一個鏈管理工廠里面使用數組存放了所有的對象(例如 Tomcat 里面 ApplicationFilterChain
用來關系 filter)。
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
鏈。
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);
}
....
}
.....
}
使用場景
- 當一個請求需要根據請求參數的不同由不同對象來處理時候。
- 當一個請求需要固定對象順序處理,并且可擴展性的在固定順序里面插入新的對象進行處理時候。
三、工廠模式(Factory Pattern)
介紹
工廠模式是創建型模式,他封裝了對象的創建過程,調用者使用具體的工廠方法根據參數就可以獲取對應的對象。
Spring 框架中 BeanFactory
如圖 BeanFactory
接口提供了 getBean
方法,在 AbstractBeanFactory
中實現了該方法,經過層層繼承,實現,最后 DefaultListableBeanFactory
實現了 BeanDefinitionRegistry
接口用來保存 bean
定義,繼承了 AbstractAutowireCapableBeanFactory
用來支撐 autowired
。
舉個例子
@Test
public void testBeanFactoy() throws NamingException, SQLException, ParseException, IOException {
// 創建 Bean 工廠
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
// 給 Bean 工廠添加 Bean 定義, 解析 xml 里面的 Bean 放入 Bean 工廠
loadBeanDefinitions(bf);
// 根據名字從 Bean 工廠獲取 Bean
Hello hello = (Hello) bf.getBean("hello");
hello.sayHello();
Hello2 hello2 = (Hello2) bf.getBean("hello2");
hello2.sayHello();
}
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String[] configLocations = new String[] { "beans2.xml" };
if (configLocations != null) {
beanDefinitionReader.loadBeanDefinitions(configLocations);
}
}
使用場景
- 不同條件下創建不同實例,用于統一管理 Bean
- 不同條件下調用不同工廠方法獲取不同場景下的 Bean
四、單例設計模式(Singleton Pattern)
介紹
單例模式是一種創建型模式,單例模式提供一個創建對象的接口,但是多次調用該接口返回的是同一個實例的引用,目的是為了保證只有一個實例,并且提供一個訪問這個實例的統一接口。
Spring 中單例 Bean 的創建
Spring 中默認配置的 bean
的 scope
為 singleton
,也就是單例作用域。那么看看它是如何做到的。在 AbstractBeanFactory
類里面的 doGetBean
方法:
protected Object doGetBean(
final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean = null;
// 解決set循環依賴
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
}
else {
...
// 創建單件bean.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
...
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//創建原型bean
else if (mbd.isPrototype()) {
...
}
//創建request作用域bean
else {
...
}
}
...
return bean;
}
getSingleton 代碼:
public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
...
beforeSingletonCreation(beanName);
...
try {
singletonObject = singletonFactory.getObject();
}
catch (BeanCreationException ex) {
...
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);
可知 Spring 內部四通過一個 ConcurrentMap
來管理單件 bean
的。獲取 bean
時候會先看看 singletonObjects
中是否有,有則直接返回,沒有則創建后放入。
Spring 的 bean
工廠管理的單例模式管理的是多個 bean
實例的單例,是工廠模式管理所有的 bean
,而每個 bean
的創建又使用了單例模式。
使用場景
- 同一個 JVM 應用的不同模塊需要使用同一個對象實例進行信息共享。
- 需要同一個實例來生成全局統一的序列號
五、原型設計模式(Prototype Pattern)
介紹
相比單例設計模式,原型模式是每次創建一個對象,下面看下 Spring 是如何使用原型模式的
Spring 中原型 bean 的創建
創建原型 bean
需要在 xml
特別說明:
<bean id="hello" class="com.lusifer.demo.Hello" scope="prototype"/>
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
}
else {
...
try {
...
// Create bean instance.
if (mbd.isSingleton()) {
...
}
//創建原型bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
...
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
...
return (T) bean;
}
createBean
函數里面則是根據 bean
定義創建新 bean
,感興趣的可以看看。
使用場景
當有業務場景使用某個 bean
時候需要使用自己的一個拷貝的時候使用。
六、策略模式(Strategy Pattern)
介紹
策略模式屬于行為性模式,它定義一系列的算法對象,使用時候可以使它們相互替換。
Spring 中 bean 實例化策略
從圖知道:接口 InstantiationStrategy
是實例化策略接口類,它定義了三個實例化接口,然后 SimpleInstantiationStrategy
實現了該策略,它主要做一些簡單的根據構造函數實例號 bean
的工作,然后 CglibSubclassingInstantiationStrategy
又繼承了 SimpleInstantiationStrategy
新增了方法注入方式根據 cglib
生成代理類實例化方法。
在 AbstractAutowireCapableBeanFactory
中管理了該策略的一個對象,默認是 CglibSubclassingInstantiationStrategy
策略,運行時候可以通過 setInstantiationStrategy
改變實例化策略,如果你自己寫個個策略的話。
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
Tomcat 中 Digester 解析 server.xml
Tomcat 中的 Digester
是為了解析 server.xml
的,其中每個元素都有一個解析規則就是 Rule
,DigestER 一開始先指定不同的解析策略(Rule),然后在具體解析 server.xml 時候根據節點不同使用不同解析策略來解析節點。
如圖在解析每個節點時候會先找到該節點對應的解析策略,然后循環去調用所有解析策略的方法去處理。
使用場景
運行時根據條件的不同使用不同的策略處理一個事情,與責任鏈不同在于,責任鏈是一個鏈條,一個事情可以被責任鏈里面所有節點處理,而 策略模式則是只有有一個對象來處理。
七、門面模式(Facade Pattern)
介紹
門面模式是一種結構性模式,它通過新增一個門面類對外暴露系統提供的一部分功能,或者屏蔽了內部系統的復雜性,對外部僅僅暴露一個簡單的接口,或者通過調用不同的服務對外提供統一的接口,讓使用者對這些內部服務透明化。
模板引擎 Velocity 中門面模式使用
Velocity 里面的 VelocityEngine
和 Velocity
類都是 RuntimeInstance
類的門面,后者提供了模板渲染的所有功能,前兩者則是內部維護 RuntimeInstance
的實例,具體工作還是委托給 RuntimeInstance
來實現。
如圖 RuntimeInstance
提供了 Velocity
引擎的所用功能,VelocityEngine
內部直接引用了 RuntimeInstance
的一個實例,VelocityEngine
對外暴露的服務都是委托 RuntimeInstance
實現,并且每次 new
一個 VelocityEngine
內部都會有 RuntimeInstance
的一個實例被創建。而 Velocity
類調用了單例模式類 RuntimeSingleton
里面的方法,RuntimeSingleton
又是 RuntimeInstance
的一個單例模式。
使用場景
- 當需要對外屏蔽一個系統的復雜性時候可以考慮使用門面模式對外提供簡單可讀性高的接口類
- 當需要對外部暴露系統一部分權限的接口時候可以考慮使用門面模式減少系統權限。
- 當系統需要調用不同服務匯總后在對外提供服務時候可以考慮使用門面模式對外屏蔽細節,之暴露一個接口。
八、裝飾器模式(Decorator Pattern)
介紹
裝飾器模式是一種結構性模式,它作用是對對象已有功能進行增強,但是不改變原有對象結構。這避免了通過繼承方式進行功能擴充導致的類體系臃腫。
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
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
創建對象.
使用場景
在不改變原有類結構基礎上,新增或者限制或者改造功能時候。
九、適配器模式(Adapter Pattern)
介紹
適配器模式屬于結構性模式,它為兩個不同接口之間互通提供了一種手段。
Spring 中 MethodInterceptor 適配器
在 Spring Aop 框架中,MethodInterceptor
接口被用來攔截指定的方法,對方法進行增強。
大家都知道在 AOP 中每個 advistor
里面會有一個 advice
具體做切面動作,Spring 提供了 AspectJAfterReturningAdvice
,AspectJMethodBeforeAdvice
,AspectJAroundAdvice
,AspectJAfterAdvice
這幾個 advice
,在 XML 配置 AOP 時候會指定 <aop:after-returning/>
, <aop:before/>
, <aop:around/>
, <aop:after/>
, 其實內部就是創建上面對應的這些 advice
。
從圖知道 AspectJAfterReturningAdvice
和 AspectJMethodBeforeAdvice
沒有實現 MethodInterceptor
接口,其他兩者則實現了該接口。而 Spring Aop 的方法攔截器卻必須是實現了 MethodInterceptor
的,所以 Spring 提供了對應的適配器來適配這個問題,分別是 MethodBeforeAdviceAdapter
和 AfterReturningAdviceAdapter
和 ThrowsAdviceAdapter
。
看下 DefaultAdvisorAdapterRegistry
的 getInterceptors
方法:
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
//從advistor中獲取advice
Advice advice = advisor.getAdvice();
//如果實現了MethodInterceptor則直接加入,比如AspectJAroundAdvice,AspectJAfterAdvice
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//否者看是否有當前advice的適配器,首先檢驗是否支持,支持則返回對應的適配器
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
以 MethodBeforeAdviceAdapter
為例子看下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
可知 MethodBeforeAdviceInterceptor
繼承了 MethodInterceptor
作為了一個適配器內部委托請求給 MethodBeforeAdvice
。
使用場景
- 兩個系統交互時候由于接口參數不一樣沒辦法直接對接,則可以搞個適配器接口做參數轉換。
- 適配器模式經常是在一個系統或者設計已經定型時候用的,而不是在初始設計時候。一般是因為不影響現在業務情況下,通過適配方式統一接口
十、模板方法模式(Template Pattern)
介紹
模板設計模式是一種行為設計模式,它使用一個抽象類定義了一個模板,這個模板里面定義了一系列的接口,子類則只需要繼承該抽象類并且根據需要重寫一部分接口。
ibatis2 中 AbstractDAOTemplate
如圖 AbstractDAOTemplate
是抽象模板類,里面定義了 configure
方法,configure
方法里面定義了好多 protected
方法,其中就有些是抽象方法。類 SpringDAOTemplate
,IbatisDAOTemplate
,GenericCIDAOTemplate
,GenericSIDAOTemplate
則繼承了 AbstractDAOTemplate
類并重寫了一部分方法。
Tomcat 中 Digester 里面的 Rule
Tomcat 中的 Digester
是為了解析 server.xml
的,其中每個元素都有一個解析規則就是 Rule
,類圖如下:
如圖:Rule 是抽象類,對于每個解析的節點來說 Rule 提供了解析所需所有的方法,而他的子類則根據自己的特殊性重寫一部分方法來支持自己的特性。
Tomcat 中 Endpoint
如圖 AbstractEndpoint
是個抽象類,定義了 Endpoint
的所有接口,然后 JIoEndpoint
繼承了該類并且重寫了一部分重要的方法實現了 BIO
方式 endpoint
, NioEndpoint
則重寫了方法實現了 NIO
的 endpoint
.
使用場景
當多個子類具有共同的操作流程邏輯,并且其中某些流程節點操作需要自己定制化時候。
十一、建造者模式(Builder Pattern)
介紹
建造者模式是一種創建型模式,將一個復制對象的創建屏蔽到接口內部,用戶使用時候只需要傳遞固定的參數,內部就會執行復雜邏輯后返回會用戶需要的對象,用戶不需要知道創建的細節。
Mybatis 中的 SqlSessionFactoryBuilder
如圖 MyBaits 中的 SqlSessionFactoryBuilder
就是典型的創建者模式,他內部有多個 build
方法,根據參數的不同創建出 SqlSessionFactory
對象,使用者只需要傳遞具體參數而不用關系內部是如何創建出需要的對象的。SqlSessionFactoryBean
大家應該很熟悉,在 xml 里面配置的。
使用場景
當一個對象比較復雜并且容易出錯時候,可以考慮這種模式去屏蔽創造細節。
十二、觀察者模式(Observer Pattern)
介紹
觀察者模式是一種行為模式,它定義了當一個對象的狀態或者屬性發生變化時候,通通知其他對這些狀態感興趣的對象。觀察者模式也叫發布-訂閱模式,就是說當你訂閱了摸一個主體時候,如果發布者改變了主題內容,那么所有訂閱這個主體者都會受到通知。
Spring 中 ApplicationListener
如圖 黃色部分的 listener
們可以認為是訂閱者,紅色的 context
是發布者,context
在 IOC
不同狀態會給這些訂閱者發布不同的消息通知訂閱者容器狀態。藍色的為具體的事件(這里為容器不同狀態),其中 ContextRefreshedEvent
是 IOC
刷新完成(也就是 bean
解析完成,創建完畢并且 autowired
完成)后的事件這個經常用。
。這里 context
并不是直接來管理黃色的 listener
訂閱者的,而是委托給了綠色的部分,該部分是可以增加刪除訂閱者,并且發布事件給訂閱者。
其實 Tomcat 中的 Lifecycle
也是這種機制
使用場景
滿足發布-訂閱條件的,當一個對象狀態或者屬性變化,需要把這種變化通知到訂閱者時候。
十三、命令模式(Command Pattern)
介紹
命令模式是一種行為模式,通過把命令封裝為一個對象,命令發送者把命令對象發出后,就不去管是誰來接受處理這個命令,命令接受者接受到命令對象后進行處理,也不用管命令是誰發出的,所以命令模式實現了發送者與接受者之間的解耦,而具體把命令發送給誰還需要一個控制器。
Tomcat 中命令模式
Tomcat 作為一個服務器本身會接受外部大量請求,當一個請求過來后 Tomcat 根據域名去找對應的 host,找到 host 后會根據應用名去找具體的 context(應用),然后具體應用處理請求。對于具體 host 來說他不關心這個請求是誰給的,對應請求來說他不必關心誰來處理,但是兩者是通過 request 封裝請求對象進行關聯起來。
Tomcat 中 Connector
作為命令發出者,Connector
接受到請求后把請求內容封裝為 request
對象(命令對象),然后使用 CoyoteAdapter
作為分發器把請求具體發配到具體的 host
, host
在根據 request
對象找到具體的 context
,至此找到了具體的應用,交給具體應用處理。
另外對于使用 SpringMVC 的應用來說,上面找到具體應用,但是具體交給那個 controller 來處理那,這是不是也是命令模式的使用那。
使用場景
當事件發送者和接受者直接需要完全解耦(直接并不存在引用關系)時候。
總結
設計模式中每一個模式都描述了在我們工作中不斷重復發生的問題,以及問題的解決方案,所以真正掌握設計模式可以避免我們做不必要的重復勞動。