組合模式看起來就像對象組的樹形結構,一個對象裏面包含一個或一組其餘的對象。它是屬於結構型模式。
例如,一個公司包括不少個部門,每一個部門又包括不少人,這個用數據結構來表示就是樹形結構,實際上也是用到來組合模式,多我的組成一個部門,多個部門組成一個公司。算法
例如,咱們用下面這個公司、部門、員工的例子來更好的理解組合模式。設計模式
class Company { private String name; private List<Dept> depts; } class Dept { private String name; private List<User> users; } class User { private String name; }
裝飾器設計模式容許咱們動態地向對象添加功能和行爲,而不會影響同一類中其餘現有對象的行爲。而且能夠根據咱們的要求和選擇將此自定義功能應用於單個對象。
假如使用繼承來擴展類的行爲,這發生在編譯期,該類的全部實例都得到擴展行爲。安全
裝飾器設計模式的特色:
它容許咱們在運行時向對象(而不是類)添加功能。
它是一種結構模式,它爲現有類提供了一個包裝器。
它使用抽象類或接口與組合來實現包裝器。
它建立裝飾器類,它包裝原始類並經過保持類方法的簽名不變來提供其餘功能。
它最經常使用於應用單一責任原則,由於咱們將功能劃分爲具備獨特關注區域的類。微信
例如,咱們用下面這個畫圖形的例子來更好的理解裝飾模式。數據結構
//定義一個形狀的接口 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(); } }
它提供了一個能夠訪問系統的接口,這個接口裏面的實現可能很複雜,調用了其餘多個接口,咱們並不知道它裏面的具體實現,隱藏了系統的複雜性。它屬於結構型模式。ide
它的優勢:
一、減小系統相互依賴。
二、提升靈活性。
三、提升了安全性。 性能
它的缺點:
不符合開閉原則,若是要改東西很麻煩,繼承重寫都不合適。網站
以旅行社網站爲例,它能夠預訂酒店和航班,咱們來更好的理解下這個模式。this
//提供酒店相關的接口 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); } }
享元模式主要用於減小建立對象的數量,以減小內存佔用和提升性能。使許多細粒度對象的重用,使得大量對象的利用更加有效。它屬於結構型模式。url
它的優勢:
大大減小對象的建立,下降系統的內存,使效率提升。
它的缺點:
由於一個對象你們共享,最好就不要有狀態區分,假若有狀態的話,
須要分離出外部狀態和內部狀態,並且外部狀態具備固有化的性質,不該該隨着內部狀態的變化而變化,不然會形成系統的混亂。
例如,咱們用一個畫線條的例子來更好的理解這個模式。
//定義一個畫線條的接口 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)); } }
它經過一個代理類對外提供訪問,代理類在真正調用實現類。對外部來講,並不知道真正實現類的詳情,提升類系統的安全性。咱們能夠在代理類裏作一系列攔截,把異常的請求都提早處理掉,擴展性很高。它屬於結構型模式。
它的優勢:
一、職責清晰。
二、高擴展性。
三、更安全。
它的缺點:
一、因爲在客戶端和真實主題之間增長了代理對象,所以可能會形成請求的處理速度變慢。
二、實現代理模式須要額外的工做,有些代理模式的實現很是複雜。
例如,咱們用下面這個代理顯示圖片的例子來更好的理解代理模式。
//定義的圖片接口 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動態代理是基於類的代理。有興趣的能夠找下相關資料。
責任鏈模式用於管理對象之間的算法,關係和責任,經過將多個不一樣處理對象連接在一塊兒處理請求,下降耦合度,提升系統靈活性。它屬於行爲型模式。
它的優勢:
一、下降耦合度。
二、簡化了對象。
三、加強給對象指派職責的靈活性。
四、增長新的請求處理類很方便。
它的缺點:
一、系統性能將受到必定影響,並且在進行代碼調試時不太方便,可能會形成循環調用。
二、可能不容易觀察運行時的特徵,不方便排錯。
例如,咱們用下面這個記錄日誌的例子來更好的理解責任鏈模式。
//定義一個抽象的日誌接口,而且提供一個能夠設置下一個處理日誌對象的方法 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."); } }
命令模式是請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找能夠處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。它屬於行爲型模式。
它的優勢:
一、下降了系統耦合度。
二、新的命令能夠很容易添加到系統中去。
它的缺點:
使用命令模式可能會致使某些系統有過多的具體命令類。
例如,咱們用下面這個開關燈的例子來更好的理解命令模式。
// 定義執行命令接口 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(); } }
解釋器模式是給定一種語言,定義其語法以及使用該語法來表示語言中句子的解釋器。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 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:
清山綠水始於塵,博學多識貴於勤。
微信公衆號:「清塵閒聊」。
歡迎一塊兒談天說地,聊代碼。