代碼寫(xiě)的爛,經(jīng)常被同事懟,教你一招!
面對(duì)復(fù)雜的業(yè)務(wù)場(chǎng)景,千變?nèi)f化的客戶需求,如何以一變應(yīng)萬(wàn)變,以最小的開(kāi)發(fā)成本快速落地實(shí)現(xiàn),同時(shí)保證系統(tǒng)有著較低的復(fù)雜度,能夠保證系統(tǒng)后續(xù)de持續(xù)迭代能力,讓系統(tǒng)擁有較高的可擴(kuò)展性。
本文是主要講解橋接模式、組合模式、裝飾模式、門面模式、代理模式、責(zé)任鏈模式
將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。
一個(gè)類存在兩個(gè)(或多個(gè))獨(dú)立變化的維度,且這兩個(gè)(或多個(gè))維度都需要獨(dú)立進(jìn)行擴(kuò)展。 對(duì)于那些不希望使用繼承或因?yàn)槎鄬永^承導(dǎo)致系統(tǒng)類的個(gè)數(shù)急劇增加的系統(tǒng),橋接模式尤為適用。
抽象實(shí)體:定義的一種抽象分類。比如:人 具體實(shí)體:繼承抽象實(shí)體的子類實(shí)體。比如:中國(guó)人、美國(guó)人、韓國(guó)人 抽象行為:定義抽象實(shí)體中具備的多種行為。比如:學(xué)漢語(yǔ)、吃漢堡 具體行為:實(shí)現(xiàn)抽象行為的具體算法。比如:中國(guó)人學(xué)漢語(yǔ)、美國(guó)人吃漢堡

/**
* @author 微信公眾號(hào):微觀技術(shù)
* 抽象實(shí)體
*/
public abstract class AbstractEntity {
protected AbstractBehavior abstractBehavior;
public AbstractEntity(AbstractBehavior abstractBehavior) {
this.abstractBehavior = abstractBehavior;
}
public abstract void out();
}
/**
* 抽象行為
*/
public interface AbstractBehavior {
public String action(String name);
}
/**
* 關(guān)于食物的行為
*/
public class FoodBehavior implements AbstractBehavior {
@Override
public String action(String name) {
if ("中國(guó)人".equals(name)) {
return "吃 餃子";
} else if ("美國(guó)人".equals(name)) {
return "吃 漢堡";
}
return null;
}
}
本質(zhì)是將一個(gè)對(duì)象的實(shí)體和行為分離,然后再基于這兩個(gè)維度進(jìn)行獨(dú)立的演化。拆分復(fù)雜的類對(duì)象時(shí)。當(dāng)一個(gè)類中包含大量對(duì)象和方法時(shí),既不方便閱讀,也不方便修改。 希望從多個(gè)獨(dú)立維度上擴(kuò)展時(shí)。比如,系統(tǒng)功能性和非功能性角度,業(yè)務(wù)或技術(shù)角度等。 運(yùn)行時(shí),組合不同的組件
組合模式也稱整體模式,把一組相似的對(duì)象當(dāng)作一個(gè)單一的對(duì)象,然后將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示整個(gè)層次結(jié)構(gòu)。
抽象組件(AbstractNode):定義需要實(shí)現(xiàn)的統(tǒng)一操作。 組合節(jié)點(diǎn)(CompositeNode):抽象組件的衍生子類,包含了若干孩子節(jié)點(diǎn)(其它組合節(jié)點(diǎn)或葉子節(jié)點(diǎn))。 葉子節(jié)點(diǎn)(LeafNode):抽象組件的子類,但它的下面沒(méi)有子節(jié)點(diǎn)。

public abstract class AbstractNode {
public abstract void add(AbstractNode abstractNode);
public abstract void remove(AbstractNode abstractNode);
public abstract void action();
}
public class CompositeNode extends AbstractNode {
private Long nodeId;
private List<AbstractNode> childNodes; //存放子節(jié)點(diǎn)列表
public CompositeNode(Long nodeId, List<AbstractNode> childNodes) {
this.nodeId = nodeId;
this.childNodes = childNodes;
}
@Override
public void add(AbstractNode abstractNode) {
childNodes.add(abstractNode);
}
@Override
public void remove(AbstractNode abstractNode) {
childNodes.remove(abstractNode);
}
@Override
public void action() {
for (AbstractNode childNode : childNodes) {
childNode.action();
}
}
}
public class LeafNode extends AbstractNode {
private Long nodeId;
public LeafNode(Long nodeId) {
this.nodeId = nodeId;
}
@Override
public void add(AbstractNode abstractNode) {
// 無(wú)子節(jié)點(diǎn),無(wú)需處理
return;
}
@Override
public void remove(AbstractNode abstractNode) {
// 無(wú)子節(jié)點(diǎn),無(wú)需處理
return;
}
@Override
public void action() {
System.out.println("葉子節(jié)點(diǎn)編號(hào):" + nodeId);
}
}
樹(shù)形結(jié)構(gòu)、環(huán)形結(jié)構(gòu)、網(wǎng)狀結(jié)構(gòu)。如我們常見(jiàn)的 深度優(yōu)先搜索、廣度優(yōu)先搜索都是采用這種模式。一組對(duì)象按照某種層級(jí)結(jié)構(gòu)進(jìn)行管理。如:管理文件夾和文件,管理訂單下的商品。 需要按照統(tǒng)一的行為來(lái)處理復(fù)雜結(jié)構(gòu)中的對(duì)象 快速擴(kuò)展對(duì)象組合。

動(dòng)態(tài)地向一個(gè)現(xiàn)有對(duì)象添加新的職責(zé)和行為,同時(shí)又不改變其結(jié)構(gòu),相當(dāng)于對(duì)現(xiàn)有的對(duì)象進(jìn)行包裝。
抽象組件(Component):裝飾器基類,定義組件的基本功能 具體組件(ConcreteComponent):抽象組件的具體實(shí)現(xiàn) 抽象裝飾器(Decorator):包含抽象組件的引用 具體裝飾器(ConcreteDecorator):抽象裝飾器的子類,并重寫(xiě)組件接口方法,同時(shí)可以添加附加功能。

public abstract class Component {
public abstract void execute();
}
public class ConcreteComponent extends Component {
@Override
public void execute() {
System.out.println("具體子類 ConcreteComponent invoke !");
}
}
public class Decorator extends Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void execute() {
component.execute();
}
}
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void execute() {
System.out.println("裝飾器子類 ConcreteDecorator invoke !");
super.execute();
}
}
無(wú)需修改代碼的情況下即可使用對(duì)象, 且希望在運(yùn)行時(shí)為對(duì)象新增額外的功能 將業(yè)務(wù)邏輯組織為層次結(jié)構(gòu),可以為各層創(chuàng)建一個(gè)裝飾,在運(yùn)行時(shí)將各種不同邏輯組合成對(duì)象。由于這些對(duì)象都遵循通用接口,客戶端代碼能以相同的方式使用這些對(duì)象。 不支持繼承擴(kuò)展類的場(chǎng)景。如: final關(guān)鍵字限制了某個(gè)類的進(jìn)一步擴(kuò)展,可以通過(guò)裝飾器對(duì)其進(jìn)行封裝,從而具備擴(kuò)展能力。
門面模式提供一個(gè)高層次的接口,要求一個(gè)子系統(tǒng)的外部與其內(nèi)部的通信必須通過(guò)一個(gè)統(tǒng)一的對(duì)象進(jìn)行,使得子系統(tǒng)更易于使用。
slf4j框架,其內(nèi)部統(tǒng)一了log4j、log4j2、CommonLog等日志框架,簡(jiǎn)化了我們的開(kāi)發(fā)成本。門面系統(tǒng)。接收外部請(qǐng)求,并將請(qǐng)求轉(zhuǎn)發(fā)給適當(dāng)?shù)淖酉到y(tǒng)進(jìn)行處理 子系統(tǒng)。表示某個(gè)領(lǐng)域內(nèi)的功能實(shí)現(xiàn)、或者具體子接口實(shí)現(xiàn),比如,訂單、支付等,專門處理由門面系統(tǒng)指派的任務(wù)。

簡(jiǎn)化復(fù)雜系統(tǒng),提供統(tǒng)一接口規(guī)范。比如: JPA提供了統(tǒng)一Java持久層API,底層適配多樣化的存儲(chǔ)系統(tǒng)。復(fù)雜的業(yè)務(wù)邏輯由內(nèi)部子系統(tǒng)消化,只要對(duì)外接口規(guī)范不變,外部調(diào)用方不需要頻繁修改 擴(kuò)展性較好,類似于 SPI架構(gòu)一樣,支持水平擴(kuò)展。較高的平滑過(guò)渡性。比如:我們要對(duì)老的系統(tǒng)架構(gòu)升級(jí),開(kāi)發(fā)一系列新接口來(lái)替換原來(lái)的老接口,過(guò)渡期需要新老灰度測(cè)試、流量切換、平滑升級(jí),可以采用該模式。門面模式在兼容多套系統(tǒng)、系統(tǒng)重構(gòu)方面是把利器。
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)
房產(chǎn)中介 包工頭
抽象主題類(AbstractSubject):定義接口方法,供客戶端使用 主題實(shí)現(xiàn)類(RealSubject):實(shí)現(xiàn)了 抽象主題類的接口方法代理類(Proxy):實(shí)現(xiàn)了 抽象主題類的接口方法,內(nèi)部包含主題實(shí)現(xiàn)類的邏輯, 同時(shí)還包含一些自身的擴(kuò)展操作。
代理模式與適配器模式相似。但適配器模式是轉(zhuǎn)換為新的接口,而代理模式不會(huì)改變?cè)薪涌凇?/span>

/**
* @author 微信公眾號(hào):微觀技術(shù)
*/
public interface AbstractSubject {
void execute();
}
public class RealSubject implements AbstractSubject {
@Override
public void execute() {
System.out.println("我是Tom哥,我要努力工作!");
}
}
public class Proxy implements AbstractSubject {
private AbstractSubject abstractSubject;
public Proxy(AbstractSubject abstractSubject) {
this.abstractSubject = abstractSubject;
}
@Override
public void execute() {
System.out.println("老板給Tom哥分配工作了。。。");
abstractSubject.execute();
}
}
靜態(tài)代理,代理類需要自己編寫(xiě)代碼完成。 動(dòng)態(tài)代理,代理類通過(guò) Proxy#newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法生成。 JDK實(shí)現(xiàn)的代理中不管是靜態(tài)代理還是動(dòng)態(tài)代理,都是面向接口編程。CGLib可以不限制一定是接口。
職責(zé)清晰 高擴(kuò)展,只要實(shí)現(xiàn)了接口,都可以用代理 智能化,動(dòng)態(tài)代理 降低了對(duì)象的直接耦合
遠(yuǎn)程代理。無(wú)法直接操作遠(yuǎn)程對(duì)象。比如:Dubbo、gRPC,提供遠(yuǎn)程服務(wù),客戶端調(diào)用時(shí)需要走參數(shù)組裝、序列化、網(wǎng)絡(luò)傳輸?shù)炔僮?,這些通用邏輯都可以封裝到代理中,客戶端調(diào)用代理對(duì)象訪問(wèn)遠(yuǎn)程服務(wù),就像調(diào)用本地對(duì)象一樣方便。 保護(hù)代理。當(dāng)客戶端通過(guò)代理對(duì)象訪問(wèn)原始對(duì)象時(shí),代理對(duì)象會(huì)根據(jù)規(guī)則判斷客戶端是否有權(quán)限訪問(wèn)。比如:防火墻 日志代理。比如:日志監(jiān)控,正常業(yè)務(wù)訪問(wèn)時(shí),調(diào)用代理,增加一些額外的日志記錄功能。 虛擬代理,適用于延遲初始化,用小對(duì)象表示大對(duì)象的場(chǎng)景,減少資源損耗,提升運(yùn)行速度。 不希望改變?cè)瓕?duì)象,但需要增加類似于權(quán)限控制、日志、流控等附加功能時(shí),可以使用代理模式。
責(zé)任鏈模式是一種行為設(shè)計(jì)模式,將所有請(qǐng)求的處理者通過(guò)前一對(duì)象記住其下一個(gè)對(duì)象的引用而連成一條鏈。收到請(qǐng)求后,每個(gè)處理者均可對(duì)請(qǐng)求進(jìn)行處理,或?qū)⑵鋫鬟f給鏈中的下個(gè)處理者。
鏈表結(jié)構(gòu)的具體應(yīng)用。抽象處理者(Handler):定義一個(gè)接口,內(nèi)部包含處理方法和下一個(gè)節(jié)點(diǎn)的引用對(duì)象 具體處理者(ConcreteHandler): 抽象處理者的實(shí)現(xiàn)子類,判斷本次請(qǐng)求是否處理,如果需要?jiǎng)t處理,否則跳過(guò),然后將請(qǐng)求轉(zhuǎn)發(fā)給下一個(gè)節(jié)點(diǎn)。

降低了對(duì)象之間的耦合度。鏈上各個(gè)節(jié)點(diǎn)各司其職,通過(guò)上下文傳遞數(shù)據(jù),避免直接依賴。 增強(qiáng)系統(tǒng)的可擴(kuò)展性。如果有新的業(yè)務(wù)需求,只需要在合適的位置增加一個(gè)鏈節(jié)點(diǎn)即可,滿足開(kāi)閉原則。 靈活性強(qiáng)。如果業(yè)務(wù)有變化,需要對(duì)工作流程做調(diào)整,只需要?jiǎng)討B(tài)調(diào)整鏈上節(jié)點(diǎn)的次序即可。甚至為了滿足多元化業(yè)務(wù)的多樣化需求,我們可以為不同的業(yè)務(wù)類型定義自己的專屬執(zhí)行順序。 簡(jiǎn)化了對(duì)象之間的連接。每個(gè)對(duì)象只需保存下一個(gè)節(jié)點(diǎn)的引用,而不需保持所有節(jié)點(diǎn)。 責(zé)任明確。每個(gè)節(jié)點(diǎn)只需處理自己的工作,如果不處理則傳遞給下一個(gè)對(duì)象。明確各類的責(zé)任范圍,符合類的單一職責(zé)原則。

在設(shè)計(jì)中思考什么應(yīng)該變化,并封裝會(huì)發(fā)生變化的概念。
SOLID原則,自我review是否滿足業(yè)務(wù)的持續(xù)擴(kuò)展性。有句話說(shuō)的好,“不論白貓黑貓,能抓老鼠就是好貓?!?/span>有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號(hào)
好文章,我在看??
