組合模式看起來就像對象組的樹形結構,一個對象裏面包含一個或一組其餘的對象。它是屬於結構型模式。 例如,一個公司包括不少個部門,每一個部門又包括不少人,這個用數據結構來表示就是樹形結構,實際上也是用到來組合模式,多我的組成一個部門,多個部門組成一個公司。算法
例如,咱們用下面這個公司、部門、員工的例子來更好的理解組合模式。設計模式
class Company {
private String name;
private List<Dept> depts;
}
class Dept {
private String name;
private List<User> users;
}
class User {
private String name;
}
複製代碼
裝飾器設計模式容許咱們動態地向對象添加功能和行爲,而不會影響同一類中其餘現有對象的行爲。而且能夠根據咱們的要求和選擇將此自定義功能應用於單個對象。 假如使用繼承來擴展類的行爲,這發生在編譯期,該類的全部實例都得到擴展行爲。安全
裝飾器設計模式的特色: 它容許咱們在運行時向對象(而不是類)添加功能。 它是一種結構模式,它爲現有類提供了一個包裝器。 它使用抽象類或接口與組合來實現包裝器。 它建立裝飾器類,它包裝原始類並經過保持類方法的簽名不變來提供其餘功能。 它最經常使用於應用單一責任原則,由於咱們將功能劃分爲具備獨特關注區域的類。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();
}
}
複製代碼
它提供了一個能夠訪問系統的接口,這個接口裏面的實現可能很複雜,調用了其餘多個接口,咱們並不知道它裏面的具體實現,隱藏了系統的複雜性。它屬於結構型模式。數據結構
它的優勢:
一、減小系統相互依賴。
二、提升靈活性。
三、提升了安全性。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);
}
}
複製代碼
享元模式主要用於減小建立對象的數量,以減小內存佔用和提升性能。使許多細粒度對象的重用,使得大量對象的利用更加有效。它屬於結構型模式。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));
}
}
複製代碼
它經過一個代理類對外提供訪問,代理類在真正調用實現類。對外部來講,並不知道真正實現類的詳情,提升類系統的安全性。咱們能夠在代理類裏作一系列攔截,把異常的請求都提早處理掉,擴展性很高。它屬於結構型模式。
它的優勢:
一、職責清晰。
二、高擴展性。
三、更安全。
它的缺點:
一、因爲在客戶端和真實主題之間增長了代理對象,所以可能會形成請求的處理速度變慢。
二、實現代理模式須要額外的工做,有些代理模式的實現很是複雜。
例如,咱們用下面這個代理顯示圖片的例子來更好的理解代理模式。
//定義的圖片接口
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:
清山綠水始於塵,博學多識貴於勤。
微信公衆號:「清塵閒聊」。
歡迎一塊兒談天說地,聊代碼。