日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
聊一聊裝飾者模式

一、概述

裝飾者模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象擴(kuò)展新的功能,同時(shí)不改變其結(jié)構(gòu)。主要解決直接繼承下因功能的不斷橫向擴(kuò)展導(dǎo)致子類(lèi)膨脹的問(wèn)題,無(wú)需考慮子類(lèi)的維護(hù)。

裝飾者模式有4種角色:

抽象構(gòu)件角色(Component):具體構(gòu)件類(lèi)和抽象裝飾者類(lèi)的共同父類(lèi)。

具體構(gòu)件角色(ConcreteComponent):抽象構(gòu)件的子類(lèi),裝飾者類(lèi)可以給它增加額外的職責(zé)。

裝飾角色(Decorator):抽象構(gòu)件的子類(lèi),具體裝飾類(lèi)的父類(lèi),用于給具體構(gòu)件增加職責(zé),但在子類(lèi)中實(shí)現(xiàn)。

具體裝飾角色(ConcreteDecorator):具體裝飾類(lèi),定義了一些新的行為,向構(gòu)件類(lèi)添加新的特性。

二、入門(mén)案例

2.1、類(lèi)圖

2.2、基礎(chǔ)類(lèi)介紹

// 抽象構(gòu)件角色
public interface Component {

void doSomeThing();
}

// 具體構(gòu)件角色
public class ConcreteComponent implements Component {

@Override
public void doSomeThing(){
System.out.println("處理業(yè)務(wù)邏輯");
}
}

// 裝飾者類(lèi)
public abstract class Decorator implements Component {

private Component component;

public Decorator(Component component){
this.component = component;
}

@Override
public void doSomeThing(){
// 調(diào)用處理業(yè)務(wù)邏輯
component.doSomeThing();
}
}

// 具體裝飾類(lèi)
public class ConcreteDecorator extends Decorator {

public ConcreteDecorator(Component component){
super(component);
}

@Override
public void doSomeThing(){
System.out.println("業(yè)務(wù)邏輯功能擴(kuò)展");
super.doSomeThing();
}
}

當(dāng)然,如果需要擴(kuò)展更多功能的話,可以再定義其他的ConcreteDecorator類(lèi),實(shí)現(xiàn)其他的擴(kuò)展功能。如果只有一個(gè)ConcreteDecorator類(lèi),那么就沒(méi)有必要建立一個(gè)單獨(dú)的Decorator類(lèi),而可以把Decorator和ConcreteDecorator的責(zé)任合并成一個(gè)類(lèi)。

三、應(yīng)用場(chǎng)景

如風(fēng)之前在一家保險(xiǎn)公司干過(guò)一段時(shí)間。其中保險(xiǎn)業(yè)務(wù)員也會(huì)在自家產(chǎn)品注冊(cè)賬號(hào),進(jìn)行推銷(xiāo)。不過(guò)在這之前,他們需要經(jīng)過(guò)培訓(xùn),導(dǎo)入一張展業(yè)資格證書(shū)。然后再去推銷(xiāo)保險(xiǎn)產(chǎn)品供用戶下單,自己則通過(guò)推銷(xiāo)產(chǎn)生的業(yè)績(jī),參與分潤(rùn),拿對(duì)應(yīng)的傭金。

對(duì)于上面導(dǎo)證書(shū)這個(gè)場(chǎng)景,實(shí)際上是會(huì)根據(jù)不同的保險(xiǎn)產(chǎn)品,導(dǎo)入不同的證書(shū)的。并且證書(shū)的類(lèi)型也不同,對(duì)應(yīng)的解析、校驗(yàn)、執(zhí)行的業(yè)務(wù)場(chǎng)景都是不同的。如何去實(shí)現(xiàn)呢?當(dāng)然if-else確實(shí)也是一種不錯(cuò)的選擇。下面放一段偽代碼

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/11/17 11:32
* @description
*/
@RestController
@RequestMapping("/certificate")
public class CertificateController {

@Resource
private CommonCertificateService certificateService;

@PostMapping("/import")
public Result importFile(@RequestParam MultipartFile file, @RequestParam String productCode){
return Result.success(certificateService.importCertificate(file, productCode));
}
}

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/11/17 13:25
* @description
*/
@Service
public class CommonCertificateService {

public Integer importCertificate(MultipartFile file, String productCode){
// 1、參數(shù)非空校驗(yàn)
// 2、通過(guò)file后綴判斷file類(lèi)型,支持excel和pdf
// 3、解析file文件,獲取數(shù)據(jù),統(tǒng)一封裝到定義的CertificatePojo類(lèi)中
// 4、根據(jù)產(chǎn)品類(lèi)型判斷導(dǎo)入之前的業(yè)務(wù)邏輯
if (productCode.equals(DecorateConstants.PRODUCT_A)) {
// 重新計(jì)算業(yè)績(jī)邏輯
// 重新算業(yè)績(jī)類(lèi)型邏輯
// 一坨坨代碼去實(shí)現(xiàn)....
}
else if (productCode.equals(DecorateConstants.PRODUCT_B)) {
// 導(dǎo)入證書(shū)的代理人自己以及上級(jí)身份晉升邏輯
// 業(yè)績(jī)計(jì)算邏輯
// 一坨坨代碼去實(shí)現(xiàn)...
} else if (productCode.equals(DecorateConstants.PRODUCT_C)) {
// c產(chǎn)品下的業(yè)務(wù)邏輯
// 一坨坨代碼去實(shí)現(xiàn)...
} else {
// 默認(rèn)的處理邏輯
// 一坨坨代碼去實(shí)現(xiàn)...
}
// 5、證書(shū)數(shù)據(jù)保存
// 6、代理人信息保存
// 7、相關(guān)流水?dāng)?shù)據(jù)保存
// 返回代理人id
Integer agentId = Integer.MAX_VALUE;
return agentId;
}
}

從上面的偽代碼看到,所有的業(yè)務(wù)邏輯是在一起處理的,通過(guò)productCode去處理對(duì)應(yīng)產(chǎn)品的相關(guān)邏輯。這么一看,好像也沒(méi)毛病,但是還是被技術(shù)大佬給否決了。好吧,如風(fēng)決定重寫(xiě)。運(yùn)用裝飾者模式,重新處理下了下這段代碼。1、一切再?gòu)淖⒔獬霭l(fā),自定義Decorate注解,這里定義2個(gè)屬性,scene和type

  • scene:標(biāo)記具體的業(yè)務(wù)場(chǎng)景
  • type:表示在該種業(yè)務(wù)場(chǎng)景下,定義一種具體的裝飾器類(lèi)
/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/11/8 17:44
* @description
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Service
public @interface Decorate {
/**
* 具體的業(yè)務(wù)場(chǎng)景
* @return
*/
String scene();
/**
* 類(lèi)型:不同業(yè)務(wù)場(chǎng)景下,不同的裝飾器類(lèi)型
* @return
*/
String type();
}

2、抽象構(gòu)件接口,BaseHandler,這個(gè)是必須滴

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/11/8 17:07
* @description 抽象處理接口
*/
public interface BaseHandler {
/**
* 統(tǒng)一的處理方法
* @param t
* @return
*/
R handle(T t);
}

3、抽象裝飾器類(lèi),AbstractHandler,持有一個(gè)被裝飾類(lèi)的引用,這個(gè)引用具體在運(yùn)行時(shí)被指定

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022-11-13 22:10:05
* @desc 抽象父類(lèi)
*/
public abstract class AbstractHandler implements BaseHandler {
protected BaseHandler service;

public void setService(BaseHandler service){
this.service = service;
}
}

4、具體的裝飾器類(lèi)AProductServiceDecorate?,主要負(fù)責(zé)處理“導(dǎo)師證書(shū)”這個(gè)業(yè)務(wù)場(chǎng)景下,A產(chǎn)品相關(guān)的導(dǎo)入邏輯,并且標(biāo)記了自定義注解Decorate,表示該類(lèi)是裝飾器類(lèi)。主要負(fù)責(zé)對(duì)A產(chǎn)品證書(shū)導(dǎo)入之前邏輯的增強(qiáng),我們這里稱之為“裝飾”。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022-11-13 23:11:16
* @desc
*/
@Decorate(scene = SceneConstants.CERTIFICATE_IMPORT, type = DecorateConstants.PRODUCT_A)
public class AProductServiceDecorate extends AbstractHandler {

/**
* 重寫(xiě)父類(lèi)處理數(shù)據(jù)方法
* @param file
* @return
*/
@Override
public Integer handle(MultipartFile file){
// 解析
CertificatePojo data = parseData(file);
// 校驗(yàn)
check(data);
// 業(yè)績(jī)計(jì)算
calAchievement(data.getMobile());
return (Integer) service.handle(data);
}

public CertificatePojo parseData(MultipartFile file){
// file,證書(shū)解析
System.out.println("A產(chǎn)品的證書(shū)解析......");
CertificatePojo certificatePojo = new CertificatePojo();
certificatePojo.setMobile("12323");
certificatePojo.setName("張三");
certificatePojo.setMemberNo("req_343242ds");
certificatePojo.setEffectDate("2022-10-31:20:20:10");
return certificatePojo;
}

/**
* 證書(shū)數(shù)據(jù)校驗(yàn)
* @param data
*/
public void check(CertificatePojo data){
// 數(shù)據(jù)規(guī)范和重復(fù)性校驗(yàn)
// .....
System.out.println("A證書(shū)數(shù)據(jù)校驗(yàn)......");
}

/**
* 計(jì)算業(yè)績(jī)信息
*/
private void calAchievement(String mobile){
System.out.println("查詢用戶信息, 手機(jī)號(hào):" + mobile);
System.out.println("重新計(jì)算業(yè)績(jī)...");
}
}

當(dāng)然,還是其他裝飾類(lèi),BProductServiceDecorate,CProductServiceDecorate等等,負(fù)責(zé)裝飾其他產(chǎn)品,這里就不舉例了。

5、當(dāng)然還有管理裝飾器類(lèi)的裝飾器類(lèi)管理器DecorateManager,內(nèi)部維護(hù)一個(gè)map,負(fù)責(zé)存放具體的裝飾器類(lèi)

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/11/15 17:18
* @description 裝飾管理器
*/
public class DecorateManager {

/**
* 用于存放裝飾器類(lèi)
*/
private Map decorateHandleMap = new HashMap<>();

/**
* 將具體裝飾器類(lèi)放在map中
*
* @param handlerList
*/
public void setDecorateHandler(List handlerList){
for (AbstractHandler h : handlerList) {
Decorate annotation = AnnotationUtils.findAnnotation(h.getClass(), Decorate.class);
decorateHandleMap.put(createKey(annotation.scene(), annotation.type()), h);
}
}

/**
* 返回具體的裝飾器類(lèi)
*
* @param type
* @return
*/
public AbstractHandler selectHandler(String scene, String type){
String key = createKey(scene, type);
return decorateHandleMap.get(key);
}

/**
* 拼接map的key
* @param scene
* @param type
* @return
*/
private String createKey(String scene, String type){
return StrUtil.builder().append(scene).append(":").append(type).toString();
}
}

6、用了springboot,當(dāng)然需要將這個(gè)管理器交給spring的bean容器去管理,需要?jiǎng)?chuàng)建一個(gè)配置類(lèi)DecorateAutoConfiguration

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022-11-12 19:22:41
* @desc
*/
@Configuration
public class DecorateAutoConfiguration {

@Bean
public DecorateManager handleDecorate(List handlers){
DecorateManager manager = new DecorateManager();
manager.setDecorateHandler(handlers);
return manager;
}
}

7、被裝飾的service類(lèi),CertificateService,只需要關(guān)注自己的核心邏輯就可以

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/11/8 17:10
* @description 執(zhí)行證書(shū)導(dǎo)入的service
*/
@Service
public class CertificateService implements BaseHandler {

/**
* 處理導(dǎo)入證書(shū)的核心邏輯service
* @param certificate
* @return
*/
@Override
public Integer handle(CertificatePojo certificate){
System.out.println("核心業(yè)務(wù),證書(shū)數(shù)據(jù):" + JSONUtil.toJsonStr(certificate));
// 1、證書(shū)數(shù)據(jù)保存
// 2、代理人信息保存
// 3、相關(guān)流水?dāng)?shù)據(jù)保存
// 其他的一些列核心操作
Integer agentId = Integer.MAX_VALUE;
// 返回代理人id
return agentId;
}
}

8、在原來(lái)的controller中,注入管理器類(lèi)DecorateManager去調(diào)用,以及service,也就是被裝飾的類(lèi)。首先拿到裝飾器,然后再通過(guò)setService方法,傳入被裝飾的service。也就是具體裝飾什么類(lèi),需要在運(yùn)行時(shí)才確定。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022-11-13 23:30:37
* @desc
*/
@RestController
public class WebController {

@Resource
private DecorateManager decorateManager;

@Resource
private CertificateService certificateService;

@PostMapping("/import")
public Result importFile(@RequestParam MultipartFile file, @RequestParam String productCode){
AbstractHandler handler = decorateManager.selectHandler(SceneConstants.CERTIFICATE_IMPORT, productCode);
if (Objects.isNull(handler)) {
return Result.fail();
}
handler.setService(certificateService);
return Result.success(handler.handle(file));
}
}

下面模擬下代理人導(dǎo)入證書(shū)的流程,當(dāng)選擇A產(chǎn)品,productCode傳A過(guò)來(lái),后端的處理流程。

  • 對(duì)于A產(chǎn)品下,證書(shū)的解析,A產(chǎn)品傳的是excel
  • 然后數(shù)據(jù)校驗(yàn),這個(gè)產(chǎn)品下,特有的數(shù)據(jù)校驗(yàn)
  • 最后是核心的業(yè)績(jī)重算,只有A產(chǎn)品才會(huì)有這個(gè)邏輯

當(dāng)選擇B產(chǎn)品,productCode傳A過(guò)來(lái),后端的處理流程。

  • 對(duì)于B產(chǎn)品下,證書(shū)的解析,A產(chǎn)品傳的是pdf
  • 然后數(shù)據(jù)校驗(yàn),跟A產(chǎn)品也不同,多了xxx步驟
  • 核心是代理人的晉升處理,這部分是B產(chǎn)品獨(dú)有的

最后說(shuō)一句,既然都用springboot了,這塊可以寫(xiě)一個(gè)starter,做一個(gè)公用的裝飾器模式。如果哪個(gè)服務(wù)需要用到,依賴這個(gè)裝飾器的starter,然后標(biāo)記Decorate注解,定義對(duì)應(yīng)的scene和type屬性,就可以直接使用了。

四、源碼中運(yùn)用

4.1、JDK源碼中的運(yùn)用

來(lái)看下IO流中,InputStream、FilterInputStream、FileInputStream、BufferedInputStream的一段代碼

public abstract class InputStream implements Closeable {

public abstract int read() throws IOException;


public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}

int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;

int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
//--------------------------
public class FilterInputStream extends InputStream {

protected FilterInputStream(InputStream in){
this.in = in;
}
public int read() throws IOException {
return in.read();
}
}

//--------------------------
public class BufferedInputStream extends FilterInputStream {
public BufferedInputStream(InputStream in, int size){
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}

public BufferedInputStream(InputStream in){
this(in, DEFAULT_BUFFER_SIZE);
}

public int read() throws IOException {
return in.read();
}

public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
}

//--------------------------
public class FileInputStream extends InputStream {
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
}

再來(lái)看下這幾個(gè)類(lèi)的類(lèi)圖 

 這些類(lèi)的代碼有刪改,可以看到BufferedInputStream中定義了很多屬性,這些數(shù)據(jù)都是為了可緩沖讀取來(lái)作準(zhǔn)備的,看到其有構(gòu)造方法會(huì)傳入一個(gè)InputStream的實(shí)例。實(shí)際編碼如下

//被裝飾的對(duì)象,文件輸入流
InputStream in=new FileInputStream("/data/log/app.log");
//裝飾對(duì)象,可緩沖
InputStream bufferedIn=new BufferedInputStream(in);
bufferedIn.read();

這里覺(jué)得很眼熟吧,其實(shí)已經(jīng)運(yùn)用了裝飾模式了。

4.2、mybatis源碼中的運(yùn)用

在mybatis中,有個(gè)接口Executor?,顧名思義這個(gè)接口是個(gè)執(zhí)行器,它底下有許多實(shí)現(xiàn)類(lèi),如CachingExecutor、SimpleExecutor、BaseExecutor等等。類(lèi)圖如下: 

主要看下CachingExecutor類(lèi),看著很眼熟,很標(biāo)準(zhǔn)的裝飾器。其中該類(lèi)中的update是裝飾方法,在調(diào)用真正update方法之前,會(huì)執(zhí)行刷新本地緩存的方法,對(duì)原來(lái)的update做增強(qiáng)和擴(kuò)展。

public class CachingExecutor implements Executor {

private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();

public CachingExecutor(Executor delegate){
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}

@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
// 增強(qiáng)內(nèi)容
// 修改方法就要清空本地的緩存
flushCacheIfRequired(ms);
// 調(diào)用原有的方法
return delegate.update(ms, parameterObject);
}
}

再來(lái)看下BaseExecutor類(lèi),這里有一個(gè)update方法,這個(gè)是原本的被裝飾的update方法。然后再看這個(gè)原本的update方法,它調(diào)用的doUpdate方法是個(gè)抽象方法,用protected修飾。咦,這不就是模板方法么,關(guān)于模板方法模式,這里就不展開(kāi)贅述了。

public abstract class BaseExecutor implements Executor {
protected Executor wrapper;

@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
return doUpdate(ms, parameter);
}
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
}

五、總結(jié)

優(yōu)點(diǎn)

通過(guò)組合而非繼承的方式,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能,在運(yùn)行時(shí)可以選擇不同的裝飾器從而實(shí)現(xiàn)不同的功能。

有效的避免了使用繼承的方式擴(kuò)展對(duì)象功能而帶來(lái)的靈活性差、子類(lèi)無(wú)限制擴(kuò)張的問(wèn)題。

具體組件類(lèi)與具體裝飾類(lèi)可以獨(dú)立變化,用戶可以根據(jù)需要新增具體組件類(lèi)跟裝飾類(lèi),在使用時(shí)在對(duì)其進(jìn)行組合,原有代碼無(wú)須改變,符合"開(kāi)閉原則"。

缺點(diǎn)

這種比繼承更加靈活機(jī)動(dòng)的特性,也同時(shí)意味著更加多的復(fù)雜性。

裝飾模式會(huì)導(dǎo)致設(shè)計(jì)中出現(xiàn)許多小類(lèi) (I/O 類(lèi)中就是這樣),如果過(guò)度使用,會(huì)使程序變得很復(fù)雜。

裝飾模式是針對(duì)抽象組件(Component)類(lèi)型編程。但是,如果你要針對(duì)具體組件編程時(shí),就應(yīng)該重新思考你的應(yīng)用架構(gòu),以及裝飾者是否合適。

六、參考源碼

?編程文檔: https://gitee.com/cicadasmile/butte-java-note

應(yīng)用倉(cāng)庫(kù): https://gitee.com/cicadasmile/butte-flyer-parent


名稱欄目:聊一聊裝飾者模式
轉(zhuǎn)載注明:http://m.5511xx.com/article/dpeisgj.html