通熟易懂的設計模式(二)

組合模式(Composite pattern)

組合模式看起來就像對象組的樹形結構,一個對象裏面包含一個或一組其餘的對象。它是屬於結構型模式。 例如,一個公司包括不少個部門,每一個部門又包括不少人,這個用數據結構來表示就是樹形結構,實際上也是用到來組合模式,多我的組成一個部門,多個部門組成一個公司。算法

例如,咱們用下面這個公司、部門、員工的例子來更好的理解組合模式。設計模式

class Company {
    private String name;
    private List<Dept> depts;
}

class Dept {
    private String name;
    private List<User> users;
}

class User {
    private String name;
}
複製代碼

裝飾模式(Decorator pattern)

裝飾器設計模式容許咱們動態地向對象添加功能和行爲,而不會影響同一類中其餘現有對象的行爲。而且能夠根據咱們的要求和選擇將此自定義功能應用於單個對象。 假如使用繼承來擴展類的行爲,這發生在編譯期,該類的全部實例都得到擴展行爲。安全

裝飾器設計模式的特色: 它容許咱們在運行時向對象(而不是類)添加功能。 它是一種結構模式,它爲現有類提供了一個包裝器。 它使用抽象類或接口與組合來實現包裝器。 它建立裝飾器類,它包裝原始類並經過保持類方法的簽名不變來提供其餘功能。 它最經常使用於應用單一責任原則,由於咱們將功能劃分爲具備獨特關注區域的類。bash

例如,咱們用下面這個畫圖形的例子來更好的理解裝飾模式。微信

//定義一個形狀的接口
public interface Shape {
    void draw();
    void resize();
}
//一個畫圓的實現
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
    @Override
    public void resize() {
        System.out.println("Resizing Circle");
    }
}
//一個畫矩形的實現
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
    @Override
    public void resize() {
        System.out.println("Resizing Rectangle");
    }
}
//定義一個形狀的裝飾器抽象類,並用組合模式定義一個形狀的屬性
public abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;
    public ShapeDecorator(Shape decoratedShape) {
        super();
        this.decoratedShape = decoratedShape;
    }
}
//顏色的枚舉
public enum Color {
    RED,
    GREEN,
    BLUE
}
//線條樣式的枚舉
public enum LineStyle {
    SOLID,
    DASH,
    DOT
}
//定義一個填充顏色的實現類實現裝飾器,並重寫 draw() 方法,resize() 方法咱們能夠保持不變,也能夠自定義,看使用場景
public class FillColorDecorator extends ShapeDecorator {
    protected Color color;
    public FillColorDecorator(Shape decoratedShape, Color color) {
        super(decoratedShape);
        this.color = color;
    }
    @Override
    public void draw() {
        decoratedShape.draw();
        System.out.println("Fill Color: " + color);
    }
    @Override
    public void resize() {
      decoratedShape.resize();
    }
}
//定義一個線條樣式的實現類實現裝飾器,並重寫 draw() 方法,resize() 方法咱們能夠保持不變,也能夠自定義,看使用場景
public class LineStyleDecorator extends ShapeDecorator {
    protected LineStyle style;
    public LineStyleDecorator(Shape decoratedShape, LineStyle style) {
        super(decoratedShape);
        this.style = style;
    }
    @Override
    public void draw() {
        decoratedShape.draw();
        System.out.println("Line Style: " + style);
    }
    // 
    @Override
    public void resize() {
        decoratedShape.resize();
    }
}
//使用裝飾器模式
public class Client {
    public static void main(String[] args) {
        //在使用時能夠任意組裝,提高代碼靈活性和擴展性。
        Shape circle = new FillColorDecorator(new LineStyleDecorator(new Circle(), LineStyle.DASH), Color.RED);
        circle.draw();
    }
}
複製代碼

外觀模式(Facade Pattern)

它提供了一個能夠訪問系統的接口,這個接口裏面的實現可能很複雜,調用了其餘多個接口,咱們並不知道它裏面的具體實現,隱藏了系統的複雜性。它屬於結構型模式。數據結構

它的優勢:
一、減小系統相互依賴。
二、提升靈活性。
三、提升了安全性。ide

它的缺點:
不符合開閉原則,若是要改東西很麻煩,繼承重寫都不合適。性能

以旅行社網站爲例,它能夠預訂酒店和航班,咱們來更好的理解下這個模式。網站

//提供酒店相關的接口
public class HotelBooker{  
    public ArrayList<Hotel> getHotelNamesFor(Date from, Date to)   {  
    //返回該時間段有效的酒店
    }
}
//提供航班相關的接口
public class FlightBooker{  
    public ArrayList<Flight> getFlightsFor(Date from, Date to)   { 
    //返回該時間段有效的航班
    }
}
//提供一個旅行對外的接口,一次返回酒店和航班信息
public class TravelFacade{   
    private HotelBooker hotelBooker;   
    private FlightBooker flightBooker;   
    public void getFlightsAndHotels(Date from, Data to)  {      ArrayList<Flight> flights = flightBooker.getFlightsFor(from, to);                       ArrayList<Hotel> hotels = hotelBooker.getHotelsFor(from, to);         
    }
}
//調用旅行外觀模式
public class Client{   
    public static void main(String[] args)   {   
        TravelFacade facade = new TravelFacade();         
        facade.getFlightsAndHotels(from, to);   
    }
}
複製代碼

享元模式(Flyweight Pattern)

享元模式主要用於減小建立對象的數量,以減小內存佔用和提升性能。使許多細粒度對象的重用,使得大量對象的利用更加有效。它屬於結構型模式。ui

它的優勢:
大大減小對象的建立,下降系統的內存,使效率提升。

它的缺點:
由於一個對象你們共享,最好就不要有狀態區分,假若有狀態的話, 須要分離出外部狀態和內部狀態,並且外部狀態具備固有化的性質,不該該隨着內部狀態的變化而變化,不然會形成系統的混亂。

例如,咱們用一個畫線條的例子來更好的理解這個模式。

//定義一個畫線條的接口
public interface LineFlyweight{
    public Color getColor();
    public void draw(Point location);
}
//定義一個畫線條的實現
public class Line implements LineFlyweight{
    private Color color; 
    public Line(Color c){
        color = c;
    }
    public Color getColor(){
        return color;
    }
    public void draw(Point location){
        //實現
    }
}
//定一個畫線條的工廠類,根據顏色來獲取線條,而且把不一樣顏色的線存儲在一個 pool 中,方便下次直接使用
public class LineFlyweightFactory{
    private List<LineFlyweight> pool; 
    public LineFlyweightFactory(){
        pool = new ArrayList<LineFlyweight>();
    }
    public LineFlyweight getLine(Color c){
        //循環檢查這個顏色的線是否存在,存在直接返回
        for(LineFlyweight line: pool){
            if(line.getColor().equals(c)){
                return line;
            }
        }
        //假如不存在,就建立一個放入這個 pool 中,方便下次直接使用
        LineFlyweight line = new Line(c);
        pool.add(line);
        return line;
    }
}
//調用享元模式
public class Client{   
    public static void main(String[] args)   {  
        //調用
        LineFlyweightFactory factory = new LineFlyweightFactory(); 
        LineFlyweight line = factory.getLine(Color.RED); 
        LineFlyweight line2 = factory.getLine(Color.RED); 
        line.draw(new Point(100, 100));
        line2.draw(new Point(200, 100));
    }
}
複製代碼

代理模式(Proxy Pattern)

它經過一個代理類對外提供訪問,代理類在真正調用實現類。對外部來講,並不知道真正實現類的詳情,提升類系統的安全性。咱們能夠在代理類裏作一系列攔截,把異常的請求都提早處理掉,擴展性很高。它屬於結構型模式。

它的優勢:
一、職責清晰。
二、高擴展性。
三、更安全。

它的缺點:
一、因爲在客戶端和真實主題之間增長了代理對象,所以可能會形成請求的處理速度變慢。
二、實現代理模式須要額外的工做,有些代理模式的實現很是複雜。

例如,咱們用下面這個代理顯示圖片的例子來更好的理解代理模式。

//定義的圖片接口
public interface Image{ 
  public void displayImage();
}
//真正的實現類
public class RealImage implements Image{   
    public RealImage(URL url)   {      
        //加載這個圖片    
        loadImage(url);   
    }   
    public void displayImage()   {       
    //顯示這個圖片 
    }   
    private void loadImage(URL url)  {      
     
    }
}
//代理類
public class ProxyImage implements Image{    
    private URL url;     
    public ProxyImage(URL url)    {       
        this.url = url;    
    }    
    //this method delegates to the real image    
    public void displayImage()   {        
        RealImage real = new RealImage(url);  
        real.displayImage();    
    }
}
//使用代理模式
public class Client {
   public static void main(String[] args) {
      Image image = new ProxyImage("test.png");
      image.display();  
   }
}
複製代碼

代理模式分爲靜態代理和動態代理,靜態代理的真正實現類是提早寫好而且編譯好的,動態代理的真正實現類是運行是生成而且編譯的,上面的例子使用的是靜態代理。
動態代理又分爲 JDK動態代理 和 CGLIB動態代理,JDK動態代理是基於接口的動態代理,CGLIB動態代理是基於類的代理。有興趣的能夠找下相關資料。

責任鏈模式(Chain of Responsibility Pattern)

責任鏈模式用於管理對象之間的算法,關係和責任,經過將多個不一樣處理對象連接在一塊兒處理請求,下降耦合度,提升系統靈活性。它屬於行爲型模式。

它的優勢:
一、下降耦合度。
二、簡化了對象。
三、加強給對象指派職責的靈活性。
四、增長新的請求處理類很方便。

它的缺點:
一、系統性能將受到必定影響,並且在進行代碼調試時不太方便,可能會形成循環調用。
二、可能不容易觀察運行時的特徵,不方便排錯。

例如,咱們用下面這個記錄日誌的例子來更好的理解責任鏈模式。

//定義一個抽象的日誌接口,而且提供一個能夠設置下一個處理日誌對象的方法
public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;
   protected int level;
   //責任鏈中的下一個元素
   protected AbstractLogger nextLogger;
   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }
   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }
    //抽象方法
   abstract protected void write(String message);
}
//定義一個標準日誌的實現類
public class ConsoleLogger extends AbstractLogger {
   public ConsoleLogger(int level){
      this.level = level;
   }
   @Override
   protected void write(String message) {    
      System.out.println("Standard Console::Logger: " + message);
   }
}
//定義一個錯誤日誌的實現類
public class ErrorLogger extends AbstractLogger {
   public ErrorLogger(int level){
      this.level = level;
   }
   @Override
   protected void write(String message) {    
      System.out.println("Error Console::Logger: " + message);
   }
}
//定義一個文件日誌的實現類
public class FileLogger extends AbstractLogger {
   public FileLogger(int level){
      this.level = level;
   }
   @Override
   protected void write(String message) {    
      System.out.println("File::Logger: " + message);
   }
}
//使用責任鏈模式
public class Client {
    //設置責任鏈的調用順序
   private static AbstractLogger getChainOfLoggers(){
      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
      
      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);
      return errorLogger;  
   }
 
   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();
      loggerChain.logMessage(AbstractLogger.INFO, "info level information.");
      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "debug level information.");
      loggerChain.logMessage(AbstractLogger.ERROR, 
         "error information.");
   }
}
複製代碼

命令模式(Command Pattern)

命令模式是請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找能夠處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。它屬於行爲型模式。

它的優勢:
一、下降了系統耦合度。
二、新的命令能夠很容易添加到系統中去。

它的缺點:
使用命令模式可能會致使某些系統有過多的具體命令類。

例如,咱們用下面這個開關燈的例子來更好的理解命令模式。

// 定義執行命令接口
public interface Command{
  public void execute();
}
//開燈命令實現類
public class LightOnCommand implements Command{
  Light light;
  public LightOnCommand(Light light){
    this.light = light;
  }
  public void execute(){
    light.switchOn();
  }
}
//關燈命令實現類
public class LightOffCommand implements Command{
  Light light;
  public LightOffCommand(Light light){
    this.light = light;
  }
  public void execute(){
    light.switchOff();
  }
}
//真正開關燈的類
public class Light{
  private boolean on;
  public void switchOn(){
    on = true;
  }
  public void switchOff(){
    on = false;
  }
}
//根據不一樣命令執行開關燈
public class RemoteControl{
  private Command command;
  public void setCommand(Command command){
    this.command = command;
  }
  public void pressButton(){
    command.execute();
  }
}
//調用命令模式
public class Client{
  public static void main(String[] args){
    RemoteControl control = new RemoteControl();
    Light light = new Light();
    Command lightsOn = new LightsOnCommand(light);
    Command lightsOff = new LightsOffCommand(light);
    //設置開燈命令來開燈
    control.setCommand(lightsOn);
    control.pressButton();
    //設置關燈命令來關燈
    control.setCommand(lightsOff);
    control.pressButton();
  }
}
複製代碼

解釋器模式(Interpreter Pattern)

解釋器模式是給定一種語言,定義其語法以及使用該語法來表示語言中句子的解釋器。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。它屬於行爲型模式。

它的優勢:
一、可擴展性比較好,靈活。
二、增長了新的解釋表達式的方式。
三、易於實現簡單語法。

它的缺點:
一、可利用場景比較少。
二、對於複雜的文法比較難維護。
三、解釋器模式會引發類膨脹。
四、解釋器模式採用遞歸調用方法。

例如,咱們用下面這個簡單規則表達式的例子來更好的理解解釋器模式。

//定義一個表達式接口
public interface Expression {
   public boolean interpret(String context);
}
//定義一個基本規則的實現,輸入的內容包含初始化的內容時,返回 true
public class TerminalExpression implements Expression {
   private String data;
   public TerminalExpression(String data){
      this.data = data; 
   }
   @Override
   public boolean interpret(String context) {
      if(context.contains(data)){
         return true;
      }
      return false;
   }
}
//定義一個或者規則的實現,輸入的內容包含初始化時任意一個內容時,返回 true,不然 false
public class OrExpression implements Expression {
   private Expression expr1 = null;
   private Expression expr2 = null;
   public OrExpression(Expression expr1, Expression expr2) {
      this.expr1 = expr1;
      this.expr2 = expr2;
   }
   @Override
   public boolean interpret(String context) {      
      return expr1.interpret(context) || expr2.interpret(context);
   }
}
//定義一個而且規則的實現,輸入的內容同時包含初始化時兩個內容時,返回 true,不然 false
public class AndExpression implements Expression {
   private Expression expr1 = null;
   private Expression expr2 = null;
   public AndExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }
 
   @Override
   public boolean interpret(String context) {      
      return expr1.interpret(context) && expr2.interpret(context);
   }
}
//調用規則表達式
public class Client {
   //規則:Robert 和 John 是男性,輸入只要知足其中一個規則就行
   public static Expression getMaleExpression(){
      Expression robert = new TerminalExpression("Robert");
      Expression john = new TerminalExpression("John");
      return new OrExpression(robert, john);    
   }
   //規則:Julie 是一個已婚的女性,輸入只要知足兩個規則
   public static Expression getMarriedWomanExpression(){
      Expression julie = new TerminalExpression("Julie");
      Expression married = new TerminalExpression("Married");
      return new AndExpression(julie, married);    
   }
 
   public static void main(String[] args) {
      Expression isMale = getMaleExpression();
      Expression isMarriedWoman = getMarriedWomanExpression();
 
      System.out.println("John is male? " + isMale.interpret("John"));
      System.out.println("Julie is a married women? " 
      + isMarriedWoman.interpret("Married Julie"));
   }
}
複製代碼

最後,仍是那句話,每種設計模式有它本身的好處,也有它的壞處。在寫代碼時,多思考,想好在寫,省得返工,先從思惟方式上改變。使用單一原則,一個類,一個方法只作一件事情,這樣方便維護,耦合低,可擴展。

PS:
清山綠水始於塵,博學多識貴於勤。
微信公衆號:「清塵閒聊」。
歡迎一塊兒談天說地,聊代碼。

相關文章
相關標籤/搜索