1. 程式人生 > >Java學習--設計模式之結構型模式(二)

Java學習--設計模式之結構型模式(二)

and 它的 null spa bubuko imp AD mco flyweight

一、裝飾器模式(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學習--設計模式之結構型模式(二)