sketch做網(wǎng)站怎么建網(wǎng)站賣東西
目錄
- 一、場(chǎng)景
- 二、面對(duì)場(chǎng)景中的新需求,我們?cè)趺崔k?
- 1、暴力法:直接修改原有的代碼。
- 2、子類繼承法:既然要增強(qiáng)行為,那我搞一個(gè)子類,覆寫不就完事了?
- 3、裝飾器模式
- 三、對(duì)裝飾器模式的思考
- 1、從代碼結(jié)構(gòu)上來看,咋和代理模式這么像呢?
- 2、設(shè)計(jì)原則 >> 設(shè)計(jì)模式
一、場(chǎng)景
- 在Java中,提供某種功能/服務(wù)給客戶端(應(yīng)用層的類)去使用,一般都是先定義接口,然后實(shí)現(xiàn)該接口。
// 應(yīng)用層
@Component
public class DataSourceApplication {@Autowiredprivate DataSource dataSource;public void write(String data) {dataSource.writeData(data);}public String read() {return dataSource.readData();}
}
// 服務(wù)層
public interface DataSource {void writeData(String data);String readData();
}@Service
public class FileDataSourceImpl implements DataSource {@Overridepublic void writeData(String data) {System.out.println("[FileDataSourceImpl.writeData]");}@Overridepublic String readData() {System.out.println("[FileDataSourceImpl.readData]");return "FileDataSourceImpl.readData";}
}
- 這時(shí)候,產(chǎn)品經(jīng)理要求寫入數(shù)據(jù)前要先加密,讀出數(shù)據(jù)后要解密。
二、面對(duì)場(chǎng)景中的新需求,我們?cè)趺崔k?
1、暴力法:直接修改原有的代碼。
@Service
public class FileDataSourceImpl implements DataSource {@Overridepublic void writeData(String data) {// 對(duì)數(shù)據(jù)加密...System.out.println("[FileDataSourceImpl.writeData]");}@Overridepublic String readData() {System.out.println("[FileDataSourceImpl.readData]");// 對(duì)數(shù)據(jù)進(jìn)行解密...return "FileDataSourceImpl.readData";}
}
- 雖然這種做法存在一些問題,但在公司可能很普遍…(不然屎山怎么形成的?😃)
- 問題1:之前辛苦寫的單測(cè)都白費(fèi)了。(雖然并不是所有程序員都會(huì)寫單測(cè) 😃)
- 問題2:邏輯耦合,數(shù)據(jù)的讀寫和數(shù)據(jù)的加密,從客觀上來說,是相互獨(dú)立的邏輯。
- 如果我們耦合在一起,那么方法會(huì)越來越冗長(zhǎng),邏輯也越來越不清晰。例如,產(chǎn)品經(jīng)理又改需求了,要求支持多種加密算法。
2、子類繼承法:既然要增強(qiáng)行為,那我搞一個(gè)子類,覆寫不就完事了?
@Primary
@Service
public class EncryptFileDataSourceImpl extends FileDataSourceImpl {@Overridepublic void writeData(String data) {// 對(duì)數(shù)據(jù)進(jìn)行加密String encryptedData = null;...super.writeData(encryptedData);System.out.println("[EncryptFileDataSourceImpl.writeData]");}@Overridepublic String readData() {super.readData();// 對(duì)數(shù)據(jù)進(jìn)行解密...System.out.println("[EncryptFileDataSourceImpl.readData]");return "EncryptFileDataSourceImpl.readData";}
}
- 如果產(chǎn)品經(jīng)理要求先壓縮數(shù)據(jù),再加密,最后寫入數(shù)據(jù)呢?難道咱再搞一個(gè)子類?
- 咱最好避免這種鏈?zhǔn)降睦^承。
3、裝飾器模式
- 實(shí)現(xiàn):
- (1)壓縮數(shù)據(jù) -> 加密數(shù)據(jù) -> 寫入數(shù)據(jù)
- (2)讀取數(shù)據(jù) -> 解密數(shù)據(jù) -> 解壓數(shù)據(jù)
- 代碼:
public interface DataSource {void writeData(String data);String readData();
}public class FileDataSourceImpl implements DataSource {@Overridepublic void writeData(String data) {System.out.println("[FileDataSourceImpl.writeData] 寫入數(shù)據(jù)");}@Overridepublic String readData() {return "[FileDataSourceImpl.readData] 讀取數(shù)據(jù)";}
}public class EncryptDataSourceImpl implements DataSource {private DataSource dataSource;public EncryptDataSourceImpl(DataSource dataSource) {this.dataSource = dataSource;}@Overridepublic void writeData(String data) {System.out.println("[EncryptFileDataSourceImpl.writeData] 加密數(shù)據(jù)");dataSource.writeData(data);}@Overridepublic String readData() {System.out.println(dataSource.readData());return "[EncryptFileDataSourceImpl.readData] 解密數(shù)據(jù)";}
}public class CompressDataSourceImpl implements DataSource {private DataSource dataSource;public CompressDataSourceImpl(DataSource dataSource) {this.dataSource = dataSource;}@Overridepublic void writeData(String data) {System.out.println("[CompressDataSourceImpl.writeData] 壓縮數(shù)據(jù)");dataSource.writeData(data);}@Overridepublic String readData() {System.out.println(dataSource.readData());return "[CompressDataSourceImpl.readData] 解壓數(shù)據(jù)";}
}
@Configuration
public class DataSourceConfig {@Beanpublic DataSource compressDataSourceImpl() {return new CompressDataSourceImpl(new EncryptDataSourceImpl(new FileDataSourceImpl()));}
}
@ComponentScan
public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);DataSource dataSource = applicationContext.getBean(DataSource.class);dataSource.writeData("hello world");System.out.println("-----------------------------");System.out.println(dataSource.readData());}
}/*
[CompressDataSourceImpl.writeData] 壓縮數(shù)據(jù)
[EncryptFileDataSourceImpl.writeData] 加密數(shù)據(jù)
[FileDataSourceImpl.writeData] 寫入數(shù)據(jù)
-----------------------------
[FileDataSourceImpl.readData] 讀取數(shù)據(jù)
[EncryptFileDataSourceImpl.readData] 解密數(shù)據(jù)
[CompressDataSourceImpl.readData] 解壓數(shù)據(jù)
*/
- 一旦產(chǎn)品經(jīng)理要改需求了,例如:
- (1)壓縮數(shù)據(jù) -> 寫入數(shù)據(jù)
- (2)讀取數(shù)據(jù) -> 解壓數(shù)據(jù)
- 咱只要修改DataSourceConfig即可:
@Configuration
public class DataSourceConfig {@Beanpublic DataSource compressDataSourceImpl() {
// return new CompressDataSourceImpl(new EncryptDataSourceImpl(new FileDataSourceImpl()));return new CompressDataSourceImpl(new FileDataSourceImpl());}
}/*
[CompressDataSourceImpl.writeData] 壓縮數(shù)據(jù)
[FileDataSourceImpl.writeData] 寫入數(shù)據(jù)
-----------------------------
[FileDataSourceImpl.readData] 讀取數(shù)據(jù)
[CompressDataSourceImpl.readData] 解壓數(shù)據(jù)
*/
三、對(duì)裝飾器模式的思考
1、從代碼結(jié)構(gòu)上來看,咋和代理模式這么像呢?
- 裝飾器模式:
- (1)實(shí)現(xiàn)和被裝飾類一樣的接口(如上述的:
implements DataSource
) - (2)持有被裝飾的類(如上述的:
private DataSource dataSource;
) - (3)增強(qiáng)接口的方法
- (1)實(shí)現(xiàn)和被裝飾類一樣的接口(如上述的:
- 代理模式:【詳見:對(duì)代理模式的理解】
- (1)實(shí)現(xiàn)和被代理類一樣的接口
- (2)持有被代理的類
- (3)增強(qiáng)接口的方法
像,太像了!
- 真的是這樣嗎?
- 看看客戶端是如何使用的吧~
裝飾器模式
:我們要根據(jù)需求,“裝飾”出接口對(duì)應(yīng)的實(shí)現(xiàn)類。- 通過層層套娃,豐富基礎(chǔ)功能。結(jié)合上文,從基本的寫入數(shù)據(jù)功能,豐富為:壓縮數(shù)據(jù) -> 加密數(shù)據(jù) -> 寫入數(shù)據(jù)。
代理模式
:接口原本的實(shí)現(xiàn)類是類A,代理后,客戶端真正使用的是實(shí)現(xiàn)類B(通常,客戶端感知不到這種變化)。
- 看看客戶端是如何使用的吧~
- 一圖勝前言:
- 一個(gè)接口的實(shí)現(xiàn)類,從邏輯上說,存在
組合邏輯
,例如:- 加密數(shù)據(jù) + 寫入數(shù)據(jù)
- 壓縮數(shù)據(jù) + 寫入數(shù)據(jù)
- 壓縮數(shù)據(jù) + 加密數(shù)據(jù) + 寫入數(shù)據(jù)
- 如果采用繼承的方式,會(huì)導(dǎo)致定義很多子類,那么用組合吧!用裝飾器模式吧!【Java的io便是這種場(chǎng)景~】
- 一個(gè)接口的實(shí)現(xiàn)類,從邏輯上說,存在
2、設(shè)計(jì)原則 >> 設(shè)計(jì)模式
- 我也學(xué)了一陣子設(shè)計(jì)模式了,已經(jīng)感受到設(shè)計(jì)模式的局限性了。以上文為例,如果我們拿到的FileDataSourceImpl已經(jīng)是一坨屎山了,里面寫入數(shù)據(jù)的邏輯并不純粹,那么,通過裝飾器模式豐富寫入數(shù)據(jù)的能力可能會(huì)出問題(例如,寫入數(shù)據(jù)前做了一些特殊處理;需求是我們先做特殊處理,再加密后寫入;使用裝飾器模式就變成了,先加密,然后特殊處理,再寫入。)。此時(shí),還不如暴力法來得簡(jiǎn)潔高效。
破罐子破摔,世界是熵增的…
- 因此,設(shè)計(jì)模式非常依賴場(chǎng)景。場(chǎng)景稍微變一下,設(shè)計(jì)模式就失效了… 而真正有用的是,設(shè)計(jì)模式遵循的設(shè)計(jì)原則,以及背后的終極奧義:
高內(nèi)聚,低耦合
。