文章目錄
- 一、結構型設計模式
- 二、適配器模式
- 三、橋接模式
- 四、組合模式
- 五、享元模式
- 六、裝飾者模式
- 七、外觀模式
- 八、代理設計模式
一、結構型設計模式
這篇文章我們來講解下結構型設計模式,結構型設計模式,主要處理類或對象的組合關系,為如何設計類以形成更大的結構提供指南。
結構型設計模式包括:適配器模式(Adapter Pattern)、橋接模式(Bridge Pattern)、組合模式(Composite Pattern)、裝飾器模式(Decorator Pattern)、外觀模式(Facade Pattern)、享元模式(Flyweight Pattern)、代理模式(Proxy Pattern)
二、適配器模式
適配器模式(Adapter Pattern)是作為兩個不兼容的接口之間的橋梁。它結合了兩個獨立接口的功能。
優點:
- 可以讓任何兩個沒有關聯的類一起運行。
- 提高了類的復用。
- 增加了類的透明度。
- 靈活性好。
這種模式涉及到一個單一的類,該類負責加入獨立的或不兼容的接口功能。舉個真實的例子,在視頻播放器中,假設視頻播放器只能播放MP4格式的視頻,那現在又有個VLC格式的視頻,就不能播放了,那要如何解決這個問題?如果我們做個轉換器,將VLC格式的視頻轉換為MP4格式的視頻不就可以播放了嗎,那這個轉換器我們就可以采用適配器設計模式來設計。
下面使用程序演示下上面的例子:
- 定義視頻接口
public interface VideoInterFace {String getVideoPath();
}
- 定時Mp4格式視頻實例
public class Mp4Video implements VideoInterFace {@Overridepublic String getVideoPath() {return "Mp4視頻的路徑";}
}
- 定義VLC格式視頻實例
public class VlcVideo implements VideoInterFace{@Overridepublic String getVideoPath() {return "Vlc視頻的路徑";}
}
- 定義播放器,只接口Mp4格式的視頻
public class Player {private Mp4Video video;public Player(Mp4Video video) {this.video = video;}public void play() {System.out.println(StringFormatter.concat("播放視頻視頻地址:", video.getVideoPath()).getValue());}
}
- 需要播放VLC格式的視頻,定義Mp4的適配器,并接收VLC格式視頻,進行轉碼。
public class Mp4Adapter extends Mp4Video {private VlcVideo vlcVideo;public Mp4Adapter(VlcVideo vlcVideo) {this.vlcVideo = vlcVideo;}@Overridepublic String getVideoPath() {System.out.println(StringFormatter.concat("開始格式轉換,vlc地址:", vlcVideo.getVideoPath()).getValue());return "轉換后的Mp4路徑!";}
}
- 測試
public class demo {public static void main(String[] args) {Player player = new Player(new Mp4Video());player.play();VlcVideo vlcVideo = new VlcVideo();Player player1 = new Player(new Mp4Adapter(vlcVideo));player1.play();}
}
從上面的例子可以看出,需要播放VLC格式,就需要寫一個目標適配器,這里是Mp4適配器,并繼承Mp4,使之有Mp4的特性,并在內部做相應的轉換即可,提高了系統的可擴展性。
三、橋接模式
橋接模式(Bridge)是用于把抽象化與實現化解耦,使得二者可以獨立變化。它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。
這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立于接口實現類。這兩種類型的類可被結構化改變而互不影響。
優點:
- 抽象和實現的分離。
- 優秀的擴展能力。
- 實現細節對客戶透明。
舉個例子:繪畫不同顏色的各種圖像,畫不同的形狀和涂顏色,便是兩個不同的功能,但兩者又相互聯系,在畫完形狀后需要涂顏色,但顏色和形狀有使多種多樣的,此時就可以采用橋接設計模式,將兩者的抽象化與實現化解耦,形狀和顏色可以獨立變化。
下面使用程序演示下上面的例子:
- 定義顏色的接口
public interface ColorApi {public void drawCircle();
}
- 定義不同顏色的實現,這里采用紅色和綠色
public class ReqColor implements ColorApi {@Overridepublic void drawCircle() {System.out.println("開始涂紅色!");}
}
public class GreenColor implements ColorApi {@Overridepublic void drawCircle() {System.out.println("開始涂綠色!");}
}
- 定義形狀的接口
public interface ShapeApi {//畫形狀void draw();//畫形狀并涂顏色void drawShapeAndsColor();
}
- 定義形狀的抽象模板,將共性的操作定義到抽象中
public abstract class ShapeAbstract implements ShapeApi {public ColorApi colorApi;public ShapeAbstract(ColorApi colorApi) {this.colorApi = colorApi;}@Overridepublic void drawShapeAndsColor() {draw();colorApi.drawCircle();}
}
- 定義圓形的實例
public class Circle extends ShapeAbstract {public Circle(ColorApi colorApi) {super(colorApi);}@Overridepublic void draw() {System.out.println("開始畫圓形!");}
}
- 定義矩形的實例
public class Rectangle extends ShapeAbstract {public Rectangle(ColorApi colorApi) {super(colorApi);}@Overridepublic void draw() {System.out.println("開始畫矩形");}
}
- 演示
public class demo {public static void main(String[] args) {ShapeApi shapeReq = new Circle(new ReqColor());shapeReq.drawShapeAndsColor();ShapeApi shapeGreen = new Circle(new GreenColor());shapeGreen.drawShapeAndsColor();ShapeApi rectangle = new Rectangle(new GreenColor());rectangle.drawShapeAndsColor();}
}
上面可以看出,可以靈活的定義形狀和顏色的組合,并且他們兩個都可以獨立變化,添加新的形狀只需,建立新的類并實現形狀接口,添加顏色也是如此,極大的提高的系統的可擴展性和可維護型。
四、組合模式
組合模式(Composite Pattern),又叫部分整體模式,是用于把一組相似的對象當作一個單一的對象。
組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。它創建了對象組的樹形結構。
優點:
- 高層模塊調用簡單。
- 節點自由增加。
缺點:
在使用組合模式時,其葉子和樹枝的聲明都是實現類,而不是接口,違反了依賴倒置原則。
舉個例子:一個公司,從上到下分為,公司、部門、小組等,他們整個在一起才能稱為一個完整的公司,要表示做個公司的結構,就可以采用組合設計模式。
下面使用程序演示下上面的例子:
- 定義屬性類,用來表示不同層級的對象
@Data
public class Property {private String name;//下一層的子集private List<Property> next;public Property(String name) {this.name = name;next = new ArrayList<Property>();}public void add(Property e) {next.add(e);}public void remove(Property e) {next.remove(e);}public List<Property> getSubordinates(){return next;}
}
- 使用演示
public class demo {public static void main(String[] args) {Property company = new Property("公司");Property department = new Property("部門");Property group = new Property("小組");company.add(department);department.add(group);System.out.println(company);}
}
上面就演示了一個公司的組合,通過company
對象就可以獲得整個公司的各個部分的對象。組合設計模式主要適合于整體部分的場景。
五、享元模式
享元模式(Flyweight Pattern)主要用于減少創建對象的數量,以減少內存占用和提高性能。它提供了減少對象數量從而改善應用所需的對象結構的方式。
享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。他的優點是大大減少對象的創建,降低系統的內存,使效率提高,但也有可能造成內存的浪費。比如Spring的采用容器的方式存儲bean,使用時從容器中獲取。
還是拿畫不同顏色形狀的例子演示下享元設計模式的使用:
- 定義形狀的接口
public interface Shape {void draw();
}
- 定義圓形的實現,并接收一個顏色值:
public class Circle implements Shape {private String color;public Circle(String color) {this.color = color;}@Overridepublic void draw() {System.out.println(StringFormatter.concat("開始畫 ", color, " 色的圓 ").getValue());}
}
- 定義形狀對象獲取工廠,根據顏色值將對象存儲到HashMap中,后再根據顏色值取對象,達到復用的效果。
public class ShapeFactory {private static final HashMap<String, Shape> circleMap = new HashMap<>();public static Shape getCircle(String color) {if (!circleMap.containsKey(color)) {System.out.println(StringFormatter.concat(">> 創建", color, "顏色的圓 ").getValue());circleMap.put(color, new Circle(color));}return circleMap.get(color);}
}
- 使用
public class demo {private static final String colors[] ={"Red", "Green", "Blue", "White", "Black"};public static void main(String[] args) {for (int i = 0; i < 20; ++i) {Shape circle = ShapeFactory.getCircle(getRandomColor());circle.draw();}}private static String getRandomColor() {return colors[(int) (Math.random() * colors.length)];}
}
上面可以看出,享元設計模式可以大大減少對象的創建,但也有可以造成內存的浪費,比如某個對象的使用頻率非常低,如果一直存在內存中就有點浪費空間了。
六、裝飾者模式
裝飾者模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。它是作為現有的類的一個包裝。
裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾者模式是繼承的一個替代模式,裝飾者模式可以動態擴展一個實現類的功能。
舉個例子:還是繪畫不同的形狀的例子,加入系統中有畫各種形狀的功能,但隨著功能后期的演化,需要畫出帶有邊框的各種形狀,那么此時就可以采用裝飾者設計模式來做增強。
下面使用程序演示下上面的例子:
- 定義形狀接口
public interface Shape {void draw();
}
- 定義圓形的實現
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("開始畫圓形!");}
}
- 定義矩形的實現
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("開始畫矩形!");}
}
- 定義裝飾器的抽象模板
public abstract class ShapeDecorator implements Shape {protected Shape decoratedShape;public ShapeDecorator(Shape decoratedShape){this.decoratedShape = decoratedShape;}@Overridepublic void draw(){decoratedShape.draw();}
}
- 定義具體的邊框裝飾器
public class BorderShapeDecorator extends ShapeDecorator {public BorderShapeDecorator(Shape decoratedShape) {super(decoratedShape); }@Overridepublic void draw() {decoratedShape.draw(); setRedBorder(decoratedShape);}private void setRedBorder(Shape decoratedShape){System.out.println("畫邊框!");}
}
- 演示
public class demo {public static void main(String[] args) {Shape circle = new Circle();circle.draw();Shape shape = new BorderShapeDecorator(new Circle());shape.draw();Shape shape1 = new BorderShapeDecorator(new Rectangle());shape1.draw();}
}
上面可以看出再不改變原先類的基礎上,做了畫邊框的效果,對原有做增強,使用裝飾者設計模式,可以大大提高系統的可擴展性。
七、外觀模式
外觀模式(Facade Pattern)隱藏系統的復雜性,并向客戶端提供了一個客戶端可以訪問系統的接口。它向現有的系統添加一個接口,來隱藏系統的復雜性。
這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委托調用。
它的優點是可以減少系統相互依賴、提高靈活性、提高了安全性。但是它不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。
舉個例子:畫各種圖形的例子,比如要畫圓形、矩形、三角形,每畫一種圖像都要拿到對應的抽象,并調用繪制方法,如果要畫的形狀過多,這么多的抽象就不好管理了,而如果使用外觀設計模式,提供一個統一的抽象,在這個抽象中就可以完成上面不同的繪制,這樣就方便了我們的管理。
下面使用程序演示下上面的例子:
- 定義形狀的接口
public interface Shape {void draw();
}
- 定義圓形的實例
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("開始畫圓!");}
}
- 定義矩形的實例
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("開始畫矩形!");}
}
- 定義三角形的實例
public class Triangle implements Shape {@Overridepublic void draw() {System.out.println("開始畫三角形!");}
}
- 定義一個外觀類,并調用上面的功能
public class ShapeFacade {private Shape circle;private Shape rectangle;private Shape square;public ShapeFacade() {circle = new Circle();rectangle = new Rectangle();square = new Triangle();}public void drawCircle(){circle.draw();}public void drawRectangle(){rectangle.draw();}public void drawSquare(){square.draw();}
}
- 演示
public class demo {public static void main(String[] args) {ShapeFacade shapeFacade = new ShapeFacade();shapeFacade.drawCircle();shapeFacade.drawRectangle();shapeFacade.drawSquare();}
}
外觀設計模式還是比較容易理解的,就是把多個功能統一整個到一個對象中,由這個對象再去調用具體的類和方法。
八、代理設計模式
代理設計模式通過代理控制對象的訪問,可以詳細訪問某個對象的方法,在這個方法調用處理,或調用后處理。既(AOP微實現) 。
代理有分靜態代理和動態代理:
- 靜態代理:在程序運行前就已經存在代理類的字節碼文件,代理類和委托類的關系在運行前就確定了。
- 動態代理:是在使用時,動態的生成代理對象,他是在內存中構建代理對象的。
舉個例子,在做數據庫操作時,一般我們都會在事物中做SQL的操作,那就需要在操作前開啟事物,操作后如果成功就需要提交事物,如果代用代理設計模式,就可以將事物開啟提交邏輯放在代理類中,被代理的類,只需要關注業務邏輯即可。
下面以支付和事物為例演示下代理模式
采用靜態代理,實現上面例子:
- 定義支付接口
public interface PayInterFace {void pay();
}
- 定義微信支付實現
public class WxPay implements PayInterFace {@Overridepublic void pay() {System.out.println("支付中...");}
}
- 定義支付的代理類
public class PayProxy implements PayInterFace {private WxPay pay;public PayProxy(WxPay pay) {this.pay = pay;}@Overridepublic void pay() {System.out.println("事物開始!");pay.pay();System.out.println("提交事物!");}
}
- 演示
public class demo {public static void main(String[] args) {PayInterFace pay = new PayProxy(new WxPay());pay.pay();}
}
上面的靜態代理,可以看出,我們需要對每個被代理對象設計一個代理類,如果代理的功能非常多,那就需要開發人員寫特別多的代理類,下面可以看下動態代理的使用。
采用動態代理,實現上面例子:
這里使用JDK自帶的動態代理來實現
- 再定義一個支付寶的支付實現
public class ZfbPay implements PayInterFace {@Overridepublic void pay() {System.out.println("支付寶支付中...");}
}
- 定義代理對象,采用jdk的
InvocationHandler
接口
public class PayProxy implements InvocationHandler {private Object object;public PayProxy(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("事物開始!");Object result = method.invoke(object, args);System.out.println("提交事物!");return result;}
}
- 演示
public class demo {public static void main(String[] args) {PayInterFace pay = (PayInterFace) Proxy.newProxyInstance(PayInterFace.class.getClassLoader(),new Class[]{PayInterFace.class},new PayProxy(new WxPay()));pay.pay();PayInterFace pay1 = (PayInterFace) Proxy.newProxyInstance(PayInterFace.class.getClassLoader(),new Class[]{PayInterFace.class},new PayProxy(new ZfbPay()));pay1.pay();}
}
上面使用一個代理類,代理了多個對象,相對于靜態代理,是代碼更簡介,靈活性也更高。