前幾節(jié)已經(jīng)學(xué)過(guò)了設(shè)計(jì)模式中的創(chuàng)建型模式,重點(diǎn)學(xué)習(xí)了建造者模式和原型模式。此外創(chuàng)建型模式還包括單例模式和工廠模式。
- 單例模式用來(lái)創(chuàng)建全局唯一的對(duì)象。
- 工廠模式用來(lái)創(chuàng)建不同但是相關(guān)類型的對(duì)象(繼承同一父類或者接口的一組子類),由給定的參數(shù)來(lái)決定創(chuàng)建哪種類型的對(duì)象。
- 建造者模式是用來(lái)創(chuàng)建復(fù)雜對(duì)象,可以通過(guò)設(shè)置不同的可選參數(shù),“定制化”地創(chuàng)建不同的對(duì)象。
- 原型模式針對(duì)創(chuàng)建成本比較大的對(duì)象,利用對(duì)已有對(duì)象進(jìn)行復(fù)制的方式進(jìn)行創(chuàng)建,以達(dá)到節(jié)省創(chuàng)建時(shí)間的目的。
????????從今天起,我們開(kāi)始學(xué)習(xí)另外一種類型的設(shè)計(jì)模式:結(jié)構(gòu)型模式。其主要是解決一些類之間組合在一起的經(jīng)典結(jié)構(gòu),這些經(jīng)典模式可以解決特定的問(wèn)題。結(jié)構(gòu)型模式包括:代理模式、橋接模式、裝飾器模式、適配器模式、門面模式、組合模式、享元模式。本文主要學(xué)習(xí)代理模式。
代理模式原理解析
????????代理模式原理和代碼實(shí)現(xiàn)比較簡(jiǎn)單,它在不改變?cè)碱惖那闆r下,創(chuàng)建出代理對(duì)象,以達(dá)到對(duì)原始類功能的增強(qiáng)。
? ? ? ? 我們看一個(gè)例子,例如:開(kāi)發(fā)了一個(gè)MetricsCollector類,用來(lái)收集接口請(qǐng)求的原始數(shù)據(jù),比如訪問(wèn)時(shí)間、處理時(shí)長(zhǎng)等。如果我在UserController中記錄訪問(wèn)時(shí)長(zhǎng)和處理時(shí)長(zhǎng)等信息。那么我們期初設(shè)計(jì)為如下代碼:
public class UserController {
//...省略其他屬性和方法...
private MetricsCollector metricsCollector; // 依賴注入
public UserVo login(String telephone, String password) {
long startTimestamp = System.currentTimeMillis();
// ... 省略login邏輯...
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
//...返回UserVo數(shù)據(jù)...
}
public UserVo register(String telephone, String password) {
long startTimestamp = System.currentTimeMillis();
// ... 省略register邏輯...
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
//...返回UserVo數(shù)據(jù)...
}
}
? ? ? ? 上述寫法會(huì)有一下問(wèn)題:1、第一,性能計(jì)數(shù)器框架代碼侵入到業(yè)務(wù)代碼中,跟業(yè)務(wù)代碼高度耦合。如果未來(lái)需要替換這個(gè)框架,那替換的成本會(huì)比較大。第二,收集接口請(qǐng)求的代碼跟業(yè)務(wù)代碼無(wú)關(guān),本就不應(yīng)該放到一個(gè)類中。業(yè)務(wù)類最好職責(zé)更加單一,只聚焦業(yè)務(wù)處理。
思考:能不能在將其寫成兩個(gè)controller中,然后將兩個(gè)controller進(jìn)行動(dòng)態(tài)結(jié)合,以達(dá)到上述代碼的功能。
我們可以采用代理模式進(jìn)行處理。代理類UserControllerProxy和原始類UserController實(shí)現(xiàn)相同的接口IUserController。UserController類只負(fù)責(zé)業(yè)務(wù)功能。代理類UserControllerProxy負(fù)責(zé)在業(yè)務(wù)代碼執(zhí)行前后附加其他邏輯代碼,并通過(guò)委托的方式調(diào)用原始類來(lái)執(zhí)行業(yè)務(wù)代碼。具體的代碼實(shí)現(xiàn)如下所示:
public interface IUserController {
UserVo login(String telephone, String password);
UserVo register(String telephone, String password);
}
public class UserController implements IUserController {
//...省略其他屬性和方法...
@Override
public UserVo login(String telephone, String password) {
//...省略login邏輯...
//...返回UserVo數(shù)據(jù)...
}
@Override
public UserVo register(String telephone, String password) {
//...省略register邏輯...
//...返回UserVo數(shù)據(jù)...
}
}
public class UserControllerProxy implements IUserController {
private MetricsCollector metricsCollector;
private UserController userController;
public UserControllerProxy(UserController userController) {
this.userController = userController;
this.metricsCollector = new MetricsCollector();
}
@Override
public UserVo login(String telephone, String password) {
long startTimestamp = System.currentTimeMillis();
// 委托
UserVo userVo = userController.login(telephone, password);
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return userVo;
}
@Override
public UserVo register(String telephone, String password) {
long startTimestamp = System.currentTimeMillis();
UserVo userVo = userController.register(telephone, password);
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return userVo;
}
}
//UserControllerProxy使用舉例
//因?yàn)樵碱惡痛眍悓?shí)現(xiàn)相同的接口,是基于接口而非實(shí)現(xiàn)編程
//將UserController類對(duì)象替換為UserControllerProxy類對(duì)象,不需要改動(dòng)太多代碼
IUserController userController = new UserControllerProxy(new UserController());
參照基于接口而非實(shí)現(xiàn)編程的設(shè)計(jì)思想,將原始類對(duì)象替換為代理類對(duì)象的時(shí)候,為了讓代碼改動(dòng)盡量少,在剛剛的代理模式的代碼實(shí)現(xiàn)中,代理類和原始類需要實(shí)現(xiàn)相同的接口。但是,如果原始類并沒(méi)有定義接口,并且原始類代碼并不是我們開(kāi)發(fā)維護(hù)的(比如它來(lái)自一個(gè)第三方的類庫(kù)),我們也沒(méi)辦法直接修改原始類,給它重新定義一個(gè)接口。在這種情況下,我們?cè)撊绾螌?shí)現(xiàn)代理模式呢?一般采用繼承。
????????我們讓代理類繼承原始類,然后擴(kuò)展附加功能。原理很簡(jiǎn)單,不需要過(guò)多解釋,你直接看代碼就能明白。具體代碼如下所示:
public class UserControllerProxy extends UserController {
private MetricsCollector metricsCollector;
public UserControllerProxy() {
this.metricsCollector = new MetricsCollector();
}
public UserVo login(String telephone, String password) {
long startTimestamp = System.currentTimeMillis();
UserVo userVo = super.login(telephone, password);
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return userVo;
}
public UserVo register(String telephone, String password) {
long startTimestamp = System.currentTimeMillis();
UserVo userVo = super.register(telephone, password);
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return userVo;
}
}
//UserControllerProxy使用舉例
UserController userController = new UserControllerProxy();
不過(guò),剛剛的代碼實(shí)現(xiàn)還是有點(diǎn)問(wèn)題。一方面,我們需要在代理類中,將原始類中的所有的方法,都重新實(shí)現(xiàn)一遍,并且為每個(gè)方法都附加相似的代碼邏輯。另一方面,如果要添加的附加功能的類有不止一個(gè),我們需要針對(duì)每個(gè)類都創(chuàng)建一個(gè)代理類。
如果有50個(gè)要添加附加功能的原始類,那我們就要?jiǎng)?chuàng)建50個(gè)對(duì)應(yīng)的代理類。這會(huì)導(dǎo)致項(xiàng)目中類的個(gè)數(shù)成倍增加,增加了代碼維護(hù)成本。并且,每個(gè)代理類中的代碼都有點(diǎn)像模板式的“重復(fù)”代碼,也增加了不必要的開(kāi)發(fā)成本。那這個(gè)問(wèn)題怎么解決呢?
我們可以使用動(dòng)態(tài)代理來(lái)解決這個(gè)問(wèn)題。所謂動(dòng)態(tài)代理(Dynamic Proxy),就是我們不事先為每個(gè)原始類編寫代理類,而是在運(yùn)行的時(shí)候,動(dòng)態(tài)地創(chuàng)建原始類對(duì)應(yīng)的代理類,然后在系統(tǒng)中用代理類替換掉原始類。那如何實(shí)現(xiàn)動(dòng)態(tài)代理呢?
public class MetricsCollectorProxy {
private MetricsCollector metricsCollector;
public MetricsCollectorProxy() {
this.metricsCollector = new MetricsCollector();
}
public Object createProxy(Object proxiedObject) {
Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
}
private class DynamicProxyHandler implements InvocationHandler {
private Object proxiedObject;
public DynamicProxyHandler(Object proxiedObject) {
this.proxiedObject = proxiedObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTimestamp = System.currentTimeMillis();
Object result = method.invoke(proxiedObject, args);
long endTimeStamp = System.currentTimeMillis();
long responseTime = endTimeStamp - startTimestamp;
String apiName = proxiedObject.getClass().getName() + ":" + method.getName();
RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return result;
}
}
}
//MetricsCollectorProxy使用舉例
MetricsCollectorProxy proxy = new MetricsCollectorProxy();
IUserController userController = (IUserController) proxy.createProxy(new UserController());
?不過(guò)上述代碼的動(dòng)態(tài)代理需要有接口才行。如果是繼承的話,可以使用其他的動(dòng)態(tài)代理框架。這樣就不用像靜態(tài)代理(第二個(gè)代碼)哪有,針對(duì)每個(gè)類都產(chǎn)生代理對(duì)象。
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-537332.html
業(yè)務(wù)系統(tǒng)的非功能性需求開(kāi)發(fā)
代理模式最常用的一個(gè)應(yīng)用場(chǎng)景就是,在業(yè)務(wù)系統(tǒng)中開(kāi)發(fā)一些非功能性需求,比如:監(jiān)控、統(tǒng)計(jì)、鑒權(quán)、限流、事務(wù)、冪等、日志和RPC請(qǐng)求等。我們將這些附加功能與業(yè)務(wù)功能解耦,放到代理類中統(tǒng)一處理,讓程序員只需要關(guān)注業(yè)務(wù)方面的開(kāi)發(fā)。實(shí)際上,前面舉的搜集接口請(qǐng)求信息的例子,就是這個(gè)應(yīng)用場(chǎng)景的一個(gè)典型例子。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-537332.html
到了這里,關(guān)于代理模式:代理在RPC、緩存、監(jiān)控等場(chǎng)景中的應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!