Java學習--設計模式之結構型模式(二)
一、裝飾器模式(Decorator Pattern)
1、概念
裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作為現有的類的一個包裝。這種模式創建了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
2、簡介
意圖:動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活。
主要解決:一般的,我們為了擴展一個類經常使用繼承方式實現,由於繼承為類引入靜態特征,並且隨著擴展功能的增多,子類會很膨脹。
何時使用:
應用實例:
(1)、孫悟空有 72 變,當他變成"廟宇"後,他的根本還是一只猴子,但是他又有了廟宇的功能。
(2)、不論一幅畫有沒有畫框都可以掛在墻上,但是通常都是有畫框的,並且實際上是畫框被掛在墻上。在掛在墻上之前,畫可以被蒙上玻璃,裝到框子裏;這時畫、玻璃和畫框形成了一個物體。
優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。
缺點:多層裝飾比較復雜。
使用場景:
(1)、擴展一個類的功能。
(2)、動態增加功能,動態撤銷。
註意事項:可代替繼承。
3、實例
我們將創建一個 Shape 接口和實現了 Shape 接口的實體類。然後我們創建一個實現了 Shape 接口的抽象裝飾類 ShapeDecorator,並把 Shape 對象作為它的實例變量。RedShapeDecorator 是實現了 ShapeDecorator 的實體類。演示類 DecoratorPatternDemo,我們的演示類使用 RedShapeDecorator 來裝飾 Shape 對象。
(1)、創建一個接口Shape
public interface Shape { void draw(); }
(2)、創建接口的實現類
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Shape: Rectangle"); } }
public class Circle implements Shape { @Override public void draw() { System.out.println("Shape: Circle"); } }
(3)、創建實現了 Shape 接口的抽象裝飾類 ShapeDecorator
public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; } public void draw(){ decoratedShape.draw(); } }
(4)、創建擴展了 ShapeDecorator 類的實體裝飾類 RedShapeDecorator 。
public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } }
(5)、使用 RedShapeDecorator 來裝飾 Shape 對象。
public class DecoratorPatternDemo { public static void main(String[] args) { Shape circle = new Circle(); Shape redCircle = new RedShapeDecorator(new Circle()); Shape redRectangle = new RedShapeDecorator(new Rectangle()); System.out.println("Circle with normal border"); circle.draw(); System.out.println("\nCircle of red border"); redCircle.draw(); System.out.println("\nRectangle of red border"); redRectangle.draw(); } }
(6)、演示結果
1 Circle with normal border 2 Shape: Circle 3 4 Circle of red border 5 Shape: Circle 6 Border Color: Red 7 8 Rectangle of red border 9 Shape: Rectangle 10 Border Color: Red
二、外觀模式(Facade Pattern)
1、概念
外觀模式(Facade Pattern)隱藏系統的復雜性,並向客戶端提供了一個客戶端可以訪問系統的接口。這種類型的設計模式屬於結構型模式,它向現有的系統添加一個接口,來隱藏系統的復雜性。這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委托調用。
2、簡介
意圖:為子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
主要解決:降低訪問復雜系統的內部子系統時的復雜度,簡化客戶端與之相應的接口。
何時使用:
(1)、客戶端不需要知道系統內部的復雜聯系,整個系統只需提供一個"接待員"即可。
(2)、定義系統的入口。
如何解決:客戶端不與系統耦合,外觀類與系統耦合。
關鍵代碼:在客戶端和復雜系統之間再加一層,這一層將調用順序、依賴關系等處理好。
應用實例:
(1)、去醫院看病,可能要去掛號、門診、劃價、取藥,讓患者或患者家屬覺得很復雜,如果有提供接待人員,只讓接待人員來處理,就很方便。
(2)、JAVA 的三層開發模式。
優點:
(1)、減少系統相互依賴。
(2)、提高靈活性。
(3)、提高了安全性。
缺點:不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。
使用場景:
(1)、為復雜的模塊或子系統提供外界訪問的模塊。
(2)、子系統相對獨立。
(3)、預防低水平人員帶來的風險。
註意事項:在層次化結構中,可以使用外觀模式定義系統中每一層的入口。
3、實例
我們將創建一個 Shape 接口和實現了 Shape 接口的實體類。下一步是定義一個外觀類 ShapeMaker。ShapeMaker 類使用實體類來代表用戶對這些類的調用。演示類 FacadePatternDemo,我們的演示類使用 ShapeMaker 類來顯示結果。
(1)、創建接口 Shape 。
public interface Shape { void draw(); }
(2)、創建實現了 Shape 接口的實現類。
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } }
public class Square implements Shape { @Override public void draw() { System.out.println("Square::draw()"); } }
public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } }
(3)、創建一個外觀類 ShapeMaker 。
public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } }
(4)、使用外觀類畫出各種形狀。
public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } }
(5)、演示結果
1 Circle::draw() 2 Rectangle::draw() 3 Square::draw()
三、享元模式(Flyweight Pattern)
1、概念
享元模式(Flyweight Pattern)主要用於減少創建對象的數量,以減少內存占用和提高性能。這種類型的設計模式屬於結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式。享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。
2、簡介
意圖:運用共享技術有效地支持大量細粒度的對象。
主要解決:在有大量對象時,有可能會造成內存溢出,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接返回在內存中已有的對象,避免重新創建。
何時使用:
(1)、系統中有大量對象。
(2)、這些對象消耗大量內存。
(3)、這些對象的狀態大部分可以外部化。
(4)、這些對象可以按照內蘊狀態分為很多組,當把外蘊對象從對象中剔除出來時,每一組對象都可以用一個對象來代替。
(5)、系統不依賴於這些對象身份,這些對象是不可分辨的。
如何解決:用唯一標識碼判斷,如果在內存中有,則返回這個唯一標識碼所標識的對象。
關鍵代碼:用 HashMap 存儲這些對象。
優點:大大減少對象的創建,降低系統的內存,使效率提高。
缺點:提高了系統的復雜度,需要分離出外部狀態和內部狀態,而且外部狀態具有固有化的性質,不應該隨著內部狀態的變化而變化,否則會造成系統的混亂。
註意事項:
(1)、註意劃分外部狀態和內部狀態,否則可能會引起線程安全問題。
(2)、這些類必須有一個工廠對象加以控制。
3、實例
我們將創建一個 Shape 接口和實現了 Shape 接口的實體類 Circle。下一步是定義工廠類 ShapeFactory。ShapeFactory 有一個 Circle 的 HashMap,其中鍵名為 Circle 對象的顏色。無論何時接收到請求,都會創建一個特定顏色的圓。ShapeFactory 檢查它的 HashMap 中的 circle 對象,如果找到 Circle 對象,則返回該對象,否則將創建一個存儲在 hashmap 中以備後續使用的新對象,並把該對象返回到客戶端。FlyWeightPatternDemo,我們的演示類使用 ShapeFactory 來獲取 Shape 對象。它將向 ShapeFactory 傳遞信息(red / green / blue/ black / white),以便獲取它所需對象的顏色。
(1)、創建一個接口 Shape。
public interface Shape { void draw(); }
(2)、創建接口的實體類。
public class Circle implements Shape { private String color; private int x; private int y; private int radius; public Circle(String color){ this.color = color; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setRadius(int radius) { this.radius = radius; } @Override public void draw() { System.out.println("Circle: Draw() [Color : " + color +", x : " + x +", y :" + y +", radius :" + radius); } }
(3)、創建一個工廠 ShapeFactory ,生成基於給定信息的實體類的對象。
import java.util.HashMap; public class ShapeFactory { private static final HashMap<String, Shape> circleMap = new HashMap<>(); public static Shape getCircle(String color) { Circle circle = (Circle)circleMap.get(color); if(circle == null) { circle = new Circle(color); circleMap.put(color, circle); System.out.println("Creating circle of color : " + color); } return circle; } }
(4)、使用該工廠,通過傳遞顏色信息來獲取實體類的對象。
public class FlyweightPatternDemo { private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" }; public static void main(String[] args) { for(int i=0; i < 20; ++i) { Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor()); circle.setX(getRandomX()); circle.setY(getRandomY()); circle.setRadius(100); circle.draw(); } } private static String getRandomColor() { return colors[(int)(Math.random()*colors.length)]; } private static int getRandomX() { return (int)(Math.random()*100 ); } private static int getRandomY() { return (int)(Math.random()*100); } }
(5)、演示結果
1 Creating circle of color : Black 2 Circle: Draw() [Color : Black, x : 36, y :71, radius :100 3 Creating circle of color : Green 4 Circle: Draw() [Color : Green, x : 27, y :27, radius :100 5 Creating circle of color : White 6 Circle: Draw() [Color : White, x : 64, y :10, radius :100 7 Creating circle of color : Red 8 Circle: Draw() [Color : Red, x : 15, y :44, radius :100 9 Circle: Draw() [Color : Green, x : 19, y :10, radius :100 10 Circle: Draw() [Color : Green, x : 94, y :32, radius :100 11 Circle: Draw() [Color : White, x : 69, y :98, radius :100 12 Creating circle of color : Blue 13 Circle: Draw() [Color : Blue, x : 13, y :4, radius :100 14 Circle: Draw() [Color : Green, x : 21, y :21, radius :100 15 Circle: Draw() [Color : Blue, x : 55, y :86, radius :100 16 Circle: Draw() [Color : White, x : 90, y :70, radius :100 17 Circle: Draw() [Color : Green, x : 78, y :3, radius :100 18 Circle: Draw() [Color : Green, x : 64, y :89, radius :100 19 Circle: Draw() [Color : Blue, x : 3, y :91, radius :100 20 Circle: Draw() [Color : Blue, x : 62, y :82, radius :100 21 Circle: Draw() [Color : Green, x : 97, y :61, radius :100 22 Circle: Draw() [Color : Green, x : 86, y :12, radius :100 23 Circle: Draw() [Color : Green, x : 38, y :93, radius :100 24 Circle: Draw() [Color : Red, x : 76, y :82, radius :100 25 Circle: Draw() [Color : Blue, x : 95, y :82, radius :100
四、代理模式(Proxy Pattern)
1、概念
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬於結構型模式。在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。
2、簡介
意圖:為其他對象提供一種代理以控制對這個對象的訪問。
主要解決:在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由於某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。
如何解決:增加中間層。
關鍵代碼:實現與被代理類組合。
應用實例:
(1)、Windows 裏面的快捷方式。
(2)、買火車票不一定在火車站買,也可以去代售點。
(3)、一張支票或銀行存單是賬戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人賬號上資金的控制。
(4)、spring aop。
優點:
(1)、職責清晰。
(2)、高擴展性。
(3)、智能化。
缺點:
(1)、由於在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
(2)、實現代理模式需要額外的工作,有些代理模式的實現非常復雜。
使用場景:按職責來劃分,通常有以下使用場景:
(1)、遠程代理。
(2)、虛擬代理。
(3)、Copy-on-Write 代理。
(4)、保護(Protect or Access)代理。
(5)、Cache代理。
(6)、防火墻(Firewall)代理。
(7)、同步化(Synchronization)代理。
(8)、智能引用(Smart Reference)代理。
註意事項:
(1)、和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。
(2)、和裝飾器模式的區別:裝飾器模式為了增強功能,而代理模式是為了加以控制。
3、實例
我們將創建一個 Image 接口和實現了 Image 接口的實體類。ProxyImage 是一個代理類,減少 RealImage 對象加載的內存占用。ProxyPatternDemo,我們的演示類使用 ProxyImage 來獲取要加載的 Image 對象,並按照需求進行顯示。
(1)、創建接口。
public interface Image { void display(); }
(2)、創建接口的實現類。
public class RealImage implements Image { private String fileName; public RealImage(String fileName){ this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName){ System.out.println("Loading " + fileName); } }
public class ProxyImage implements Image{ private RealImage realImage; private String fileName; public ProxyImage(String fileName){ this.fileName = fileName; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } realImage.display(); } }
(3)、當被請求時,使用 ProxyImage 來獲取 RealImage 類的對象。
public class ProxyPatternDemo { public static void main(String[] args) { Image image = new ProxyImage("test_10mb.jpg"); //圖像將從磁盤加載 image.display(); System.out.println(""); //圖像將不再從磁盤加載 image.display(); } }
(4)、演示結果
1 Loading test_10mb.jpg 2 Displaying test_10mb.jpg 3 4 Displaying test_10mb.jpg
結構型模式的講解到此也就結束了,如有問題,還望各位大佬指正。
Java學習--設計模式之結構型模式(二)