設計模式看這一篇就夠了 超詳細總結

創建型

文章很多部分內容來自此,以下爲個人學習筆記,侵刪
ps:圖中代碼實例部分 一個文件不能有多個public類,爲了閱讀方便都放入了同一個代碼框中

單例模式

確保一個類只有一個實例,並提供該實例的全局訪問點

私有構造函數保證了不能通過構造函數來創建對象實例,只能通過公有靜態函數返回唯一的私有靜態變量

image-20200630095556785

懶漢式-線程不安全

以下實現中,私有靜態變量 uniqueInstance 被延遲實例化,這樣做的好處是,如果沒有用到該類,那麼就不會實例化 uniqueInstance,從而節約資源。

public class Singleton {
	private static Singleton uniqueInstance;
	private Singleton() {
}
public static Singleton getUniqueInstance() {
    //線程不安全 可能同時進入
	if (uniqueInstance == null) { 
	uniqueInstance = new Singleton();	
	}
	return uniqueInstance;
	}
}

餓漢式-線程安全

線程不安全問題主要是由於 uniqueInstance 被實例化多次,採取直接實例化 uniqueInstance 的方式就不會產生線程不安全問題。但是直接實例化的方式也丟失了延遲實例化帶來的節約資源的好處。

private static Singleton uniqueInstance = new Singleton();

懶漢式-線程安全

  1. 直接對方法加鎖,性能較差

        public static synchronized Singleton 	getUniqueInstance() {
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    
  2. 雙重校驗鎖,保證性能的同時又實現了線程安全


public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

volatile保證可見性

靜態內部類實現

經過測試,靜態內部類和普通內部類一樣,都是隻有當需要用到的時候才進行加載。

當 Singleton 類被加載時,靜態內部類 SingletonHolder 沒有被加載進內存。只有當調用 getUniqueInstance()方法從而觸發 SingletonHolder.INSTANCE 時 SingletonHolder 纔會被加載,此時初始化 INSTANCE 實例,並且JVM 能確保 INSTANCE 只被實例化一次。
這種方式不僅具有延遲初始化的好處,而且由 JVM 提供了對線程安全的支持。


public class Singleton {
    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}

枚舉類實現

public enum  SingletonEnum {
    instance;
    private String objName;

    public String getObjName() {
        return objName;
    }

    public void setObjName(String objName) {
        this.objName = objName;
    }

    public static void main(String[] args) {
        SingletonEnum firstSingleton=SingletonEnum.instance;
        firstSingleton.setObjName("firstName");
        System.out.println(firstSingleton.getObjName());
        SingletonEnum secondSingleton=SingletonEnum.instance;
        secondSingleton.setObjName("secondName");
        System.out.println("firstSingleton = " + firstSingleton.getObjName());
        System.out.println(secondSingleton.getObjName());

        //反射獲取實例測試
        try {
            SingletonEnum[] enums=SingletonEnum.class.getEnumConstants();
            for (SingletonEnum singletonEnum:enums
                 ) {
                System.out.println(singletonEnum.getObjName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}


輸出:

firstName
firstSingleton = secondName
secondName
secondName

該實現可以防止反射攻擊。在其它實現中,通過 setAccessible() 方法可以將私有構造函數的訪問級別設置爲public,然後調用構造函數從而實例化對象,如果要防止這種攻擊,需要在構造函數中添加防止多次實例化的代碼。
該實現是由 JVM 保證只會實例化一次,因此不會出現上述的反射攻擊。該實現在多次序列化和序列化之後,不會得到多個實例。而其它實現需要使用 transient 修飾所有字段,並且實現序列化和反序列化的方法。這與enum的性質有關,詳細可以查看 java 枚舉(enum) 全面解讀

好處:可以防止反射攻擊,在enum變量中,成員變量instance是一個SingletonEnum的類繼承了Enum,並且被聲明爲static final,不可繼承,而且保證單一性,可以防止反射調用私有構造函數,可以防止序列化和反序列化造成對象有多個。

缺點:枚舉類會自動分配空間,枚舉類的裝載和初始化時會有時間和空間的成本. 它的實現比其他方式需要更多的內存空間,所以在Android這種受資源約束的設備中儘量避免使用枚舉單例。

enum幾點總結:

  1. public enum Color {
        BLACK, WHITE
    }
    

    ​ 反編譯後會發現

    // final修飾,無法被繼承
    public final class Color extends Enum {
    
        // 爲了避免 返回的數組修改,而引起內部values值的改變,返回的是原數組的副本
        public static Color[] values() {
            return (Color[]) $VALUES.clone();
        }
    
        // 按名字獲取枚舉實例
        public static Color valueOf(String name) {
            return (Color) Enum.valueOf(em / Color, name);
        }
    
        // 私有的構造函數
        private Color(String name, int ordinal) {
            super(name, ordinal);
        }
    
        // enum第一行的聲明的變量,都對應一個枚舉實例對象
        public static final Color BLACK;
        public static final Color WHITE;
        //
        private static final Color $VALUES[];
    
        // 靜態域初始化,說明在類加載的cinit階段就會被實例化,jvm能夠保證類加載過程的線程安全
        static {
            BLACK = new Color("BLACK", 0);
            WHITE = new Color("WHITE", 1);
            $VALUES = (new Color[]{
                    BLACK, WHITE
            });
        }
    }
    
    

    2.對序列化進行特殊處理,防止反序列化時創建新的對象

    在序列化的時候Java僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是通過Enum的valueOf()方法來根據名字查找枚舉對象。同時,編譯器是不允許任何對這種序列化進行定製,因此禁用了writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve等方法。

    3.私有構造函數, 無法正常的 new出對象

    4.無法通過 clone()方法,克隆對象

      /**
         * Throws CloneNotSupportedException.  This guarantees that enums
         * are never cloned, which is necessary to preserve their "singleton"
         * status.
         */
        protected final Object clone() throws CloneNotSupportedException {
            throw new CloneNotSupportedException();
        }
    

    5.無法通過反射的方式創建枚舉對象

    枚舉類型,在 JVM 層面禁止了通過反射構造枚舉實例的行爲,如果嘗試通過反射創建,將會報Cannot reflectively create enum objects.

    枚舉實例必須在 enum關鍵字聲明的類中顯式的指定(首行開始的以第一個分號結束)

    除了上面, 沒有任何方式(new,clone,反射,序列化)可以手動創建枚舉實例

    枚舉類不可被繼承

    枚舉類是線程安全的

    枚舉類型是類型安全的(typesafe)

    (枚舉類型是類型安全的,可以對傳入的值進行類型檢查:

    如有個 handleColor(Color color)方法,那麼方法參數自動會對類型進行檢查,只能傳入 Color.WHITEColor.BLACK

    無法繼承其他類(已經默認繼承Enum)

簡單工廠

在創建一個對象時不向客戶暴露內部細節,提供一個創建對象的通用接口

簡單工廠把實例化的操作單獨放到一個類中,這個類就成爲簡單工廠類,讓簡單工廠類來決定應該用哪個具體子類來實例化。
**這樣做能把客戶類和具體子類的實現解耦,**客戶類不再需要知道有哪些子類以及應當實例化哪個子類。客戶類往往有多個,如果不使用簡單工廠,那麼所有的客戶類都要知道所有子類的細節。而且一旦子類發生改變,例如增加子類,那麼所有的客戶類都要進行修改。

總結:實現解耦,一個工廠可以創建多個產品

image-20200630184936161

實現:


public interface Product {
}

public class ConcreteProduct implements Product {
}

public class ConcreteProduct1 implements Product {
}

public class ConcreteProduct2 implements Product {
}

public class SimpleFactory {
    public Product createProduct(int type) {
        if (type == 1) {
            return new ConcreteProduct1();
        } else if (type == 2) {
            return new ConcreteProduct2();
        }
        return new ConcreteProduct();
    }
}

public class Client {
    public static void main(String[] args) {
        SimpleFactory simpleFactory = new SimpleFactory();
        Product product = simpleFactory.createProduct(1);
// do something with the product
    }
}



工廠方法

定義了一個創建對象的接口,但由子類決定要實例化哪個類。工廠方法把實例化操作推遲到子類。image-20200630191342424

與上面一個類的區別是,定義了一個抽象工廠方法, 想要增加產品的時候,要增加應對的工廠,那麼就可以繼承抽象工廠,實現它的方法,而且在抽象工廠裏可以添加所有工廠都要使用的方法。

代碼實現


public abstract class Factory {
    abstract public Product factoryMethod();

    public void doSomething() {
        Product product = factoryMethod();
// do something with the product
    }
}

public class ConcreteFactory extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}

public class ConcreteFactory1 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct1();
    }
}

public class ConcreteFactory2 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct2();
    }
}

抽象工廠

提供一個接口,用於創建 相關的對象家族

image-20200630210117718

抽象工廠跟上面的工廠方法挺像的,不同的是抽象工廠一個工廠裏可以創建一系列產品,比如A可以衍生出A1,A2,B可以衍生出B1,B2 工廠可以AB一起生產。

舉個例子,假如A是鍵盤,B是鼠標,那麼1表示惠普,2表示戴爾,在ConcreteFactory1裏,可以同時生產A1,B1 惠普的鼠標鍵盤,如下圖所示:

preview

抽象工廠模式和工廠模式的區別?

抽象工廠模式創建的是對象家族,也就是很多對象而不是一個對象,並且這些對象是相關的,也就是說必須一起創建出來。而工廠方法模式只是用於創建一個對象,這和抽象工廠模式有很大不同。

代碼實現:

public class AbstractProductA {
}

public class AbstractProductB {
}

public class ProductA1 extends AbstractProductA {
}

public class ProductA2 extends AbstractProductA {
}

public class ProductB1 extends AbstractProductB {
}

public class ProductB2 extends AbstractProductB {
}

public abstract class AbstractFactory {
    abstract AbstractProductA createProductA();

    abstract AbstractProductB createProductB();
}

public class ConcreteFactory1 extends AbstractFactory {
    AbstractProductA createProductA() {
        return new ProductA1();
    }

    AbstractProductB createProductB() {
        return new ProductB1();
    }
}

public class ConcreteFactory2 extends AbstractFactory {
    AbstractProductA createProductA() {
        return new ProductA2();
    }

    AbstractProductB createProductB() {
        return new ProductB2();
    }
}

public class Client {
    public static void main(String[] args) {
        AbstractFactory abstractFactory = new ConcreteFactory1();
        AbstractProductA productA = abstractFactory.createProductA();
        AbstractProductB productB = abstractFactory.createProductB();
// do something with productA and productB
    }
}

建造者模式(生成器模式)

封裝一個對象的構造過程,並允許按步驟構造。將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

image-20200630212105369

Builder就是各個不同的對象的具體構造者,Director負責指揮Builder的建造過程,客戶端只需要跟指揮者交互,在客戶端確定具體建造者的類型,然後傳入Director就行。

案例:

某遊戲軟件中人物角色包括多種類型,不同類型的人物角色,其性別、臉型、服裝、髮型等外部特徵有所差異,使用建造者模式創建人物角色對象,請根據類圖編程實現該系統,並寫出相應Java代碼。

img

package lab2_1;

//Actor角色類:複合產品
class Actor
{
	private String type;
	private String sex;
	private String face;
	private String costume;
	private String hairstyle;
	
	public void setType(String type) {
		this.type = type; 
	}
	public void setSex(String sex) {
		this.sex = sex; 
	}

	public void setFace(String face) {
		this.face = face; 
	}

	public void setCostume(String costume) {
		this.costume = costume; 
	}

	public void setHairstyle(String hairstyle) {
		this.hairstyle = hairstyle; 
	}
	
	public String getType() {
		return (this.type); 
	}
	
	public String getSex() {
		return (this.sex); 
	}

	public String getFace() {
		return (this.face); 
	}

	public String getCostume() {
		return (this.costume); 
	}

	public String getHairstyle() {
		return (this.hairstyle); 
	}
}

//角色建造器:抽象建造者
abstract class ActorBuilder
{
	protected Actor actor = new Actor();
	
	public abstract void buildType();
	public abstract void buildSex();
	public abstract void buildFace();
	public abstract void buildCostume();
	public abstract void buildHairstyle();
	public Actor createActor()
	{
		return actor;
	}
}

//英雄角色建造器:具體建造者
class HeroBuilder extends ActorBuilder
{
	public void buildType()
	{
		actor.setType("英雄");
	}
	public void buildSex()
	{
		actor.setSex("男");
	}
	public void buildFace()
	{
		actor.setFace("英俊");
	}
	public void buildCostume()
	{
		actor.setCostume("盔甲");
	}
	public void buildHairstyle()
	{
		actor.setHairstyle("飄逸");
	}	
}

//天使角色建造器:具體建造者
class AngelBuilder extends ActorBuilder
{
	public void buildType()
	{
		actor.setType("天使");
	}
	public void buildSex()
	{
		actor.setSex("女");
	}
	public void buildFace()
	{
		actor.setFace("漂亮");
	}
	public void buildCostume()
	{
		actor.setCostume("白裙");
	}
	public void buildHairstyle()
	{
		actor.setHairstyle("披肩長髮");
	}	
}

//魔鬼角色建造器:具體建造者
class GhostBuilder extends ActorBuilder
{
	public void buildType()
	{
		actor.setType("魔鬼");
	}
	public void buildSex()
	{
		actor.setSex("妖");
	}
	public void buildFace()
	{
		actor.setFace("醜陋");
	}
	public void buildCostume()
	{
		actor.setCostume("黑衣");
	}
	public void buildHairstyle()
	{
		actor.setHairstyle("光頭");
	}	
}
//Actor角色創建控制器:指揮者
class ActorController
{
	public Actor construct(ActorBuilder ab)
	{
		//傳入一個構造者,然後構造各種屬性,返回結果
		Actor actor;
		ab.buildType();
		ab.buildSex();
		ab.buildFace();
		ab.buildCostume();
		ab.buildHairstyle();
		actor=ab.createActor();
		return actor;
	}
}

//客戶端測試類
class Client
{
	public static void main(String args[])
	{
		ActorController ac = new ActorController();
		ActorBuilder ab;
		ab = new AngelBuilder();
		Actor angel;
		angel = ac.construct(ab);
		String type = angel.getType();
		System.out.println(type + "的外觀:");
		System.out.println("性別:" + angel.getSex());
		System.out.println("面容:" + angel.getFace());
		System.out.println("服裝:" + angel.getCostume());
		System.out.println("髮型:" + angel.getHairstyle());
	}
}

案例2,簡易StringBuilder實現

import java.util.Arrays;

public class AbstractStringBuilder {
    //value數組
    protected char[] value;

    //記錄存儲的最後一個下標
    protected int count;

    public AbstractStringBuilder(int capacity) {
        count = 0;
        value = new char[capacity];
    }
    public AbstractStringBuilder append(char c) {
        ensureCapacityInternal(count + 1);
        value[count++] = c;
        return this;
    }

    //確保容量足夠
    private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }

    //擴容函數
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }
}
class StringBuilder extends AbstractStringBuilder {
    public StringBuilder() {
        super(16);
    }
    @Override
    public String toString() {
// Create a copy, don't share the array
        return new String(value, 0, count);
    }
}

 class ClientStringBuilder {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        final int count = 26;
        for (int i = 0; i < count; i++) {
            sb.append((char) ('a' + i));
        }
        System.out.println(sb.toString());
    }
}

輸出:

abcdefghijklmnopqrstuvwxyz

解釋:這裏的構造過程可以理解爲append方法,也就是類圖中的buildPart()方法,而這裏不存在builder哪一個類,只是追加char數組,不像上面的案例,可能是構造天使,魔鬼等不同角色,所以沒有director,或者說StringBuilder本身就是director

原型模式

使用原型實例指定要創建對象的類型,通過複製這個原型來創建新對象。其實就是有個clone方法,像object類中有個clone方法

image-20200701095049542

代碼實現


public abstract class Prototype {
    abstract Prototype myClone();
}

public class ConcretePrototype extends Prototype {
    private String filed;

    public ConcretePrototype(String filed) {
        this.filed = filed;
    }

    //這個clone方法是關鍵
    @Override  
    Prototype myClone() {
        return new ConcretePrototype(filed);
    }

    @Override
    public String toString() {
        return filed;
    }
}
    

public class Client {
    public static void main(String[] args) {
        Prototype prototype = new ConcretePrototype("abc");
        Prototype clone = prototype.myClone();
        System.out.println(clone.toString());
    }
}

行爲型

責任鏈

使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈發送該請求,直到有一個對象處理它爲止。

image-20200701095558639

successor就是用來傳遞請求的,舉個例子,加入Handler1是老師,Handler2是校長,那麼handler1就可以通過這個successor傳遞給handler2。

  1. 實例1:

某公司欲開發一個軟件的在線文檔幫助系統,用戶可以在任何一個查詢環境中輸入查詢關鍵字,如果當前查詢環境下沒有相關的內容,則系統會將查詢按照一定的順序轉發給其他查詢環境。客戶可以定製自己的查詢順序,例如先查詢Java關鍵字,再查詢SQL關鍵字,最後查詢UML關鍵字。請根據類圖編程實現該系統,並寫出相應Java代碼。

img

implementation:

/**
 *
 * @author:yxh
 * 責任鏈模式實例
 */

/**
 * 測試類
 */
class Client{
    public static void main(String[] args) {
            SearchContext jContext,sContent,uContext;
            jContext=new JavaSearchContext();
            sContent=new SQLSearchContext();
            uContext=new UMLSearchContext();
            jContext.setSuccessor(sContent);
            sContent.setSuccessor(uContext);
            String keyword="UML類圖繪製疑惑";
            jContext.search(keyword);
    }
}
abstract class SearchContext{
    SearchContext successor;
    public void setSuccessor(SearchContext successor) {
        this.successor = successor;
    }
    abstract void search(String keyWord);
}
/**
 * Java查詢類
 * */
class  JavaSearchContext extends SearchContext {
    @Override
    void search(String keyWord) {
        //處理請求
        if (keyWord.indexOf("Java")>=0)
            System.out.println("Java關鍵字查詢");
        //轉發請求
        else
            successor.search(keyWord);
    }
}

/**
 * SQL查詢類
 */
class  SQLSearchContext extends SearchContext {

    @Override
    void search(String keyWord) {
        //處理請求
        if (keyWord.indexOf("SQL")>=0)
            System.out.println("SQL關鍵字查詢");
        //轉發請求
        else
            successor.search(keyWord);
    }
}


/**
 * UML搜索類
 */
class  UMLSearchContext extends SearchContext {

    @Override
    void search(String keyWord) {
        if (keyWord.indexOf("UML")>=0)
            System.out.println("UML關鍵字查詢");
    }
}

輸出:

查詢關鍵字UML
  1. 實例2:

此處定義了一個枚舉變量,TYPE來確定是由第幾個Handler處理,TYPE1就由第一個Handler處理



public abstract class Handler {
    protected Handler successor;

    public Handler(Handler successor) {
        this.successor = successor;
    }

    protected abstract void handleRequest(Request request);
}

public class ConcreteHandler1 extends Handler {
    public ConcreteHandler1(Handler successor) {
        super(successor);
    }

    @Override
    protected void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE1) {
            System.out.println(request.getName() + " is handle by ConcreteHandler1");
            return;
        }
        if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class ConcreteHandler2 extends Handler {
    public ConcreteHandler2(Handler successor) {
        super(successor);
    }

    @Override
    protected void handleRequest(Request request) {
        if (request.getType() == RequestType.TYPE2) {
            System.out.println(request.getName() + " is handle by ConcreteHandler2");
            return;
        }
        if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class Request {
    private RequestType type;
    private String name;

    public Request(RequestType type, String name) {
        this.type = type;
        this.name = name;
    }

    public RequestType getType() {
        return type;
    }

    public String getName() {
        return name;
    }
}

public enum RequestType {
    TYPE1, TYPE2
}

public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1(null);
        Handler handler2 = new ConcreteHandler2(handler1);
        Request request1 = new Request(RequestType.TYPE1,
                "request1");
        handler2.handleRequest(request1);
        Request request2 = new Request(RequestType.TYPE2,
                "request2");
        handler2.handleRequest(request2);
    }
}

輸出:

request1 is handle by ConcreteHandler1
request2 is handle by ConcreteHandler2

命令模式

把發送者和接收者完全解耦,發送者和接收者沒有直接引用關係,發送請求的對象只需要知道如何發送請求,而不必知道如何完成請求。

image-20200701113958360

  • Command和ConcreteCommand就是抽象命令和具體命令,commande裏可以增加undo()方法表示撤銷

  • receiver command裏的execute調用receiver的action,也就是他是命令的真正執行者,

  • invoker調用命令,多個命令組成invoker,invoker調用command裏的execute,

  • client只需要與invoker交互

爲什麼需要一個Receiver呢,如果需要增加命令,那麼修改receiver的值,再增加一個ConcreteCommand就可以了,解耦。或者說結構清晰功能明確,receiver專門用來實現命令,而ConcreteCommand調用命令。

實例:

了用戶使用方便,某系統提供了一系列功能鍵,用戶可以自定義功能鍵的功能,如功能鍵FunctionButton可以用於退出系統(SystemExitClass),也可以用於打開幫助界面(DisplayHelpClass)。用戶可以通過修改配置文件來改變功能鍵的用途,現使用命令模式來設計該系統,使得功能鍵類與功能類之間解耦,相同的功能鍵可以對應不同的功能。請根據類圖編程實現該系統,並寫出相應Java代碼。

img

/**
 *
 * @author:yxh
 * 命令模式實例
 * 將命令調用者和命令請求者解耦
 * 好處: 1、降低了系統耦合度。
 * 2、新的命令可以很容易添加到系統中去。
 * 3.命令的執行更加靈活,可以進行命令排序,記錄撤銷等操作
 */

/**
 * 測試類
 */
class  client4_3{
    public static void main(String[] args) {
            Command command1,command2;
            command1=new ExitCommand();
            command2=new HelpCommand();
            FunctionButton functionButton1=new FunctionButton(command1);
            FunctionButton functionButton2=new FunctionButton(command2);
            functionButton1.click();
            functionButton2.click();
    }

}

/**
 * 抽象命令類
 */
abstract class Command{
   abstract void excute();
        }

/**
 * 命令請求類 對應receiver
 */
class SystemExitClass{
    void exit(){
        System.out.println("退出系統");
    }
}
class DisplayHelpClass{
    void display(){
        System.out.println("打開幫助系統");
    }
}

/**
 * 命令實體類
 */
class  ExitCommand extends Command{
    private SystemExitClass systemExitClass=new SystemExitClass();

    @Override
    void excute() {
        systemExitClass.exit();
    }
}
class HelpCommand extends Command{
    private DisplayHelpClass displayHelpClass=new DisplayHelpClass();
    @Override
    void excute() {
        displayHelpClass.display();
    }
}

/**
 * 命令調用類,對應Invoker
 * */
class FunctionButton{
    Command command;

    public FunctionButton(Command command) {
        this.command = command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }
    public void click(){
        command.excute();
    }
}

解釋器模式

語言創建解釋器,通常由語言的語法和語法分析來定義。

image-20200701114521873

TerminalExpression:終結符表達式,每個終結符都需要一個
Context:上下文,包含解釋器之外的一些全局信息。

實例:

import java.util.StringTokenizer;

public class jieshiqi {
}
 abstract class Expression {
    //interpret 翻譯
    public abstract boolean interpret(String str);
}
/**
 *終結符 ,也就是比如 A OR B A和B屬於終結符, OR屬於非終結符 ,
 */

class TerminalExpression extends Expression {
    private String literal = null;
    public TerminalExpression(String str) {
        literal = str;
    }
    @Override
    public boolean interpret(String str) {
        /*
        * Java StringTokenizer 屬於 java.util 包,用於分隔字符串。
        StringTokenizer 構造方法:
        1. StringTokenizer(String str) :構造一個用來解析 str 的 StringTokenizer 對象。
        * java 默認的分隔符是空格("")、製表符(\t)、換行符(\n)、回車符(\r)。*/
        StringTokenizer st = new StringTokenizer(str);
        while (st.hasMoreTokens()) {
            String test = st.nextToken();
            if (test.equals(literal)) {
                return true;
            }
        }
        return false;
    }

}
/*
* 這兩個屬於非終結符
* */
class AndExpression extends Expression {

    private Expression expression1 = null;
    private Expression expression2 = null;
    public AndExpression(Expression expression1, Expression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
    @Override
    public boolean interpret(String str) {
      
        return expression1.interpret(str) && expression2.interpret(str);
    }
}
class OrExpression extends Expression {
    private Expression expression1 = null;
    private Expression expression2 = null;
    public OrExpression(Expression expression1, Expression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
    @Override
    public boolean interpret(String str) {


        return expression1.interpret(str) || expression2.interpret(str);
    }
}
class Clientjieshiqi {
    /**
     * 構建解析樹
     */
    public static Expression buildInterpreterTree() {
// Literal
        Expression terminal1 = new TerminalExpression("A");
        Expression terminal2 = new TerminalExpression("B");
        Expression terminal3 = new TerminalExpression("C");
        Expression terminal4 = new TerminalExpression("D");
// B C
        Expression alternation1 = new OrExpression(terminal2, terminal3);
// A Or (B C)
        Expression alternation2 = new OrExpression(terminal1, alternation1);
// D And (A Or (B C))
        return new AndExpression(terminal4, alternation2);
    }
    public static void main(String[] args) {
        //構建一個表達式
        Expression define = buildInterpreterTree();
        //Context: 環境類 也可以理解爲上下文,存儲一些全局信息
        String context1 =
                "D A";
        String context2 =
                "A B";

        //DA是否滿足這個表達式
        System.out.println(define.interpret(context1));
        System.out.println(define.interpret(context2));
    }
}

解釋:

D And (A Or (B C)),構建的文法樹中 express1爲D epresssion2爲 一個OrExpression , OrExpression裏又包含一個 B OR C,只有當只包含一個終結符時,纔會執行 TerminalExpression.interpret()。

迭代器模式

提供了一種訪問聚合對象內部元素的方式,並且不暴露對象的內部表示細節

image-20200701172442480

  • 類圖中的iterator可以增加一個first方法用於獲取第一個元素

  • Aggregate 是聚合類,其中 createIterator() 方法可以產生一個 Iterator;

  • Iterator 主要定義了 hasNext() 和 next() 方法。

  • Client 組合了 Aggregate,爲了迭代遍歷 Aggregate,也需要組合 Iterator。

實例

某商品管理系統的商品名稱存儲在一個字符串數組中,現需要自定義一個雙向迭代器(MyIterator)實現對該商品名稱數組的雙向(前向和後向)遍歷。請根據類圖編程實現該系統,並寫出相應Java代碼。

img

/**
 * @author yxh
 * 迭代器模式實例
 */

import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * 實現迭代器接口
 */
class MyIterator implements AbstractIterator {
    private String[] productsName;
    int index1;     //nextIndex
    int index2;

    MyIterator(AbstractProductList list) {
        productsName = list.getProductsName();
        index1=0;
        index2=productsName.length-1;
    }

    @Override
    public void next() {
        index1++;
        if (index1 >= productsName.length + 1)
            throw new IndexOutOfBoundsException();
    }

    @Override
    public boolean isLast() {

        //到達末尾
        return index1 == productsName.length;
    }

    @Override
    public void previous() {

        if (index2 == -1)
            throw new IndexOutOfBoundsException();
        else {
            index2--;
        }


    }

    @Override
    public boolean isFirst() {
        return index2 == -1;
    }

    @Override
    public String getnextItem() {
        return productsName[index1];
    }

    @Override
    public String getPreviousItem() {
        if (index2 <= -1)
            throw new NoSuchElementException();
        return productsName[index2];
    }
}

/**
 * 抽象迭代器接口
 */
interface AbstractIterator {
    void next();

    boolean isLast();

    void previous();

    boolean isFirst();

    String getnextItem();

    String getPreviousItem();


}

/**
 * 抽象商品列表類
 */
abstract class AbstractProductList {
    public void setProductsName(String[] productsName) {
        this.productsName = productsName;
    }

    private String[] productsName;

    public AbstractProductList(String[] productsName) {
        this.productsName = productsName;
    }

    public String[] getProductsName() {
        return productsName;
    }

    public abstract AbstractIterator getIterator();
}

/**
 * 商品列表類
 */
class ProductList extends AbstractProductList {

    ProductList(String[] productsName) {
        super(productsName);
    }

    @Override
    public AbstractIterator getIterator() {
        return new MyIterator(this);
    }
}

class Client5_2 {
    public static void main(String[] args) {
        String[] pNames = {"ThinkPad電腦", "Tissot手錶", "iPhone手機", "LV手提包"};
        AbstractIterator iterator;
        AbstractProductList list;
        list = new ProductList(pNames);
        iterator = list.getIterator();
        //如果不是末尾的元素就取
        while (!iterator.isLast()) {

            System.out.println(iterator.getnextItem());
            iterator.next();
        }
        System.out.println("------------------");
        while (!iterator.isFirst()) {
            System.out.println(iterator.getPreviousItem());
            iterator.previous();
        }
    }
}

中介者模式

集中相關對象之間複雜的溝通和控制方式。用一箇中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。

一個實例幫助理解:QQ羣就是典型的中介者,假如每個QQ號互相單獨溝通,對象之間的溝通就會非常複雜,如下圖

image-20200701175743412

類圖:

image-20200701175502981

Mediator:中介者,定義一個接口用於與各同事(Colleague)對象通信。
Colleague:同事,相關對象

實例

Alarm(鬧鐘)、CoffeePot(咖啡壺)、Calendar(日曆)、Sprinkler(噴頭)是一組相關的對象,在某個對象的事件產生時需要去操作其它對象,形成了下面這種依賴結構:

image-20200701201040085

使用中介者模式可以將複雜的依賴結構變成星形結構:

image-20200701201052944

public abstract class Colleague {
    public abstract void onEvent(Mediator mediator);
}

public class Alarm extends Colleague {
    @Override
    
    //調用中介者,跟其他對象交互
    public void onEvent(Mediator mediator) {
        mediator.doEvent("alarm");
    }

    public void doAlarm() {
        System.out.println("doAlarm()");
    }
}

public class CoffeePot extends Colleague {
    @Override
    public void onEvent(Mediator mediator) {
        mediator.doEvent("coffeePot");
    }

    public void doCoffeePot() {
        System.out.println("doCoffeePot()");
    }
}

public class Calender extends Colleague {
    @Override
    public void onEvent(Mediator mediator) {
        mediator.doEvent("calender");
    }

    public void doCalender() {
        System.out.println("doCalender()");
    }
}

public class Sprinkler extends Colleague {
    @Override
    public void onEvent(Mediator mediator) {
        mediator.doEvent("sprinkler");
    }

    public void doSprinkler() {
        System.out.println("doSprinkler()");
    }
}

public abstract class Mediator {
    public abstract void doEvent(String eventType);
}

public class ConcreteMediator extends Mediator {
    private Alarm alarm;
    private CoffeePot coffeePot;
    private Calender calender;
    private Sprinkler sprinkler;

    public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler
            sprinkler) {
        this.alarm = alarm;
        this.coffeePot = coffeePot;
        this.calender = calender;
        this.sprinkler = sprinkler;
    }
   
    
    //其他對象通過中介者的doEvent方法與另外的對象交互
    @Override
    public void doEvent(String eventType) {
        //根據這個對象的類型,選擇交互的順序
        switch (eventType) {
            case "alarm":
                doAlarmEvent();
                break;
            case "coffeePot":
                doCoffeePotEvent();
                break;
            case "calender":
                doCalenderEvent();
                break;
            default:
                doSprinklerEvent();
        }
    }

    public void doAlarmEvent() {
        alarm.doAlarm();
        coffeePot.doCoffeePot();
        calender.doCalender();
        sprinkler.doSprinkler();
    }

    public void doCoffeePotEvent() {
        coffeePot.doCoffeePot();
   		alarm.doAlarm();
        calender.doCalender();
        sprinkler.doSprinkler();
    }

    public void doCalenderEvent() {
// ...
    }

    public void doSprinklerEvent() {
// ...
    }
}

public class Client {
    public static void main(String[] args) {
        Alarm alarm = new Alarm();
        CoffeePot coffeePot = new CoffeePot();
        Calender calender = new Calender();
        Sprinkler sprinkler = new Sprinkler();
        Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);
// 鬧鐘事件到達,調用中介者就可以操作相關對象
        alarm.onEvent(mediator);
    }
}
doAlarm()
doCoffeePot()
doCalender()
doSprinkler()

備忘錄模式

在不違反封裝的情況下獲得對象的內部狀態,從而在需要時可以將對象恢復到最初狀態。備份的意思

image-20200701212041180

  • Originator:原始對象
  • Caretaker:負責人,負責保存備忘錄,但是不能對備忘錄的內容進行檢查或者曹祖,負責人類可以存儲一個或多個備忘錄對象,它只負責存儲對象,而不能修改對象。
  • Menento:備忘錄,存儲原始對象的的狀態。需要注意的是,只有原始對象和負責人可以與備忘錄直接交互
  • 舉個栗子:假設Originator是我的系統,Menento是我係統的一次備份,那麼caretake就是存儲我備份的磁盤,我要存備份和恢復備份都需要通過Caretaker

實例

img

/**
 * 備忘錄模式實例
 * 中國象棋軟件,由於考慮到有些用戶是“菜鳥”,經常不小心走錯棋;
 * 還有些用戶因爲不習慣使用手指在手機屏幕上拖動棋子,常常出現操作失誤,
 * 因此該中國象棋軟件要提供“悔棋”功能,在用戶走錯棋或操作失誤後可恢復到前一個步驟。
 *
 * @author yxh
 * @Student Id 
 */

import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.Stack;

/**
 * 象棋類
 */
class Chessman {
    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    String label;
    int x;
    int y;

    @Override
    public String toString() {
        return "棋子:" + label + "當前位置爲" +
                "第" + x + "行" +
                "第" + y +
                "列";
    }

    public Chessman(String label, int x, int y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }

    public ChessmanMemento save() {
        ChessmanMemento chessmanMemento = new ChessmanMemento(label, x, y);
        return chessmanMemento;
    }

    public void restore(ChessmanMemento chessmanMemento) {
        this.label = chessmanMemento.getLabel();
        this.x = chessmanMemento.getX();
        this.y = chessmanMemento.getY();
    }
}

/**
 * 備忘錄類
 */
class ChessmanMemento {
    String label;
    int x;
    int y;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public ChessmanMemento(String label, int x, int y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }
}

/**
 * Caretaker類 用來傳遞備忘錄
 */
class MementoCaretaker {
    private ArrayList mementolist = new ArrayList();

    public ChessmanMemento getMemento(int i) {
        return (ChessmanMemento) mementolist.get(i);
    }

    public void setMemento(ChessmanMemento memento) {
        mementolist.add(memento);
    }


}

class client7_1 {
    private static int index = -1; //定義一個索引來記錄當前狀態所在位置
    private static MementoCaretaker mc = new MementoCaretaker();

    public static void main(String[] args) {
        Chessman chessman = new Chessman("車", 1, 1);
        System.out.println(chessman.toString());

        //保存caretaker裏
        mc.setMemento(chessman.save());
        index++;

        chessman.setY(4);
        System.out.println(chessman.toString());
        mc.setMemento(chessman.save());
        index++;

        chessman.setX(5);
        System.out.println(chessman.toString());
        mc.setMemento(chessman.save());
        index++;

        System.out.println("---悔棋---");
        chessman.restore(mc.getMemento(--index));
        System.out.println(chessman.toString());

        System.out.println("---悔棋---");
        chessman.restore(mc.getMemento(--index));
        System.out.println(chessman.toString());

        System.out.println("---撤銷悔棋---");
        chessman.restore(mc.getMemento(++index));
        System.out.println(chessman.toString());

        System.out.println("---撤銷悔棋---");
        chessman.restore(mc.getMemento(++index));
        System.out.println(chessman.toString());

    }

}

看上面一個實例,chessman就是原始狀態,save返回當前的狀態,是一個memento對象,然後用caretaker的set方法保存這個memento對象。下次chessman用restore恢復對象,從caretaker中取備忘錄然後恢復。

觀察者

定義對象之間的一對多依賴,當一個對象狀態改變時,它的所有依賴都會收到通知並且自動更新狀態。主題(Subject)是被觀察的對象,而其所有依賴者(Observer)稱爲觀察者。

  • 主題(Subject)具有註冊和移除觀察者、並通知所有觀察者的功能,主題是通過維護一張觀察者列表來實現這些操作的。
  • 觀察者(Observer)的註冊功能需要調用主題的 registerObserver() 方法。也就是圖中弄個的attach方法,功能都是一樣,有的detach方法也叫remove方法
  • Subject註冊和移除觀察者,當Subject更新後,notify觀察者,並調用觀察者的update方法更新觀察者

image-20200701223011730

實例

某在線股票軟件需要提供如下功能:當股票購買者所購買的某支股票價格變化幅度達到5%時,系統將自動發送通知(包括新價格)給購買該股票的股民。現使用觀察者模式設計該系統。請根據類圖編程實現該系統,並寫出相應Java代碼。

img

/**
 * @author yxh
 * 觀察者模式實例
 */

import java.util.ArrayList;


class Stock{

    //該股票投資者
    private ArrayList<Investor> investors=new ArrayList<>();

    //該股票的名字
    private String stockName;

    //股票價格
    private double price;

    public String getStockName() {
        return stockName;
    }

    public void setStockName(String stockName) {
        this.stockName = stockName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        //如果漲幅超過5%,就通知
        if(Math.abs((this.price-price)/this.price)>=0.05)
            this.notifyInvestor();
        this.price = price;
    }

    public Stock(String stockName, double price) {
        this.stockName = stockName;
        this.price = price;
    }
    void attach(Investor investor){
        investors.add(investor);
    }
    void detach(Investor investor)
    {
        investors.remove(investor);
    }
    void notifyInvestor(){
        for (Investor investor:this.investors
             ) {
            investor.response(this);
        }
    }
}
interface Investor{
    void response(Stock stock);
}
class ConcreteInvestor implements Investor{
    private String name;

    public ConcreteInvestor(String name) {
        this.name = name;
    }

    @Override
    public void response(Stock stock) {
        System.out.println("提示股民:"+this.name+"股票"+stock.getStockName()+"價格波動幅度超過5%,現在的價格是:"+stock.getPrice());

    }
}
class Client6_11{
    public static void main(String[] args) {
        Investor investor1,investor2;
        investor1=new ConcreteInvestor("楊過");
        investor2=new ConcreteInvestor("小龍女");
        Stock haier=new Stock("青島海爾",20.00);
        haier.attach(investor1);
        haier.attach(investor2);

        haier.setPrice(25.00);

    }
}

該例子中,股票就是被觀察對象,而投資者就是觀察者,當股票價格波動超過一定範圍會通知所以投資者,response可以理解股民其更新狀態。

狀態模式

image-20200701222618363

可以看到Context包含state,不同的狀態有不同的處理,所以調用state.handle;這也是java多態的一種體現

實例

在某銀行系統中,我們定義了賬戶的三種狀態:

(1) 如果賬戶中餘額大於等於0,則賬戶的狀態爲綠色狀態(GreenState),此時用戶既可以向該賬戶存款也可以從該賬戶取款;

(2) 如果賬戶中餘額小於0,並且大於等於-1000,則賬戶的狀態爲黃色狀態(YellowState),此時用戶既可以向該賬戶存款也可以從該賬戶取款;

(3) 如果賬戶中餘額小於-1000,那麼賬戶的狀態爲紅色狀態(RedState),此時用戶只能向該賬戶存款,不能再從中取款;

現用狀態模式來實現狀態的轉化問題,用戶只需執行簡單的存款和取款操作,系統根據餘額數量自動轉換到相應的狀態。請根據類圖編程實現該系統,並寫出相應Java代碼。

img


/*
* 賬戶類
* */
class Account{

    AccountState state;
    String owner;

     Account(String owner,double init) {
        this.owner =owner;
        this.state = new GreenState(init,this);
        System.out.println(this.owner +"開戶,初始金額爲:" +init);
        System.out.println("**************************************");

    }

    public void setState(AccountState state) {
        this.state = state;

    }


    public void deposit(double amount){
        System.out.println(this.owner + "存款"+amount + "元");
        state.deposit(amount);
        System.out.println("現在餘額爲"+state.balance);
        System.out.println("現在賬戶狀態爲" + state.getClass().getName());
        System.out.println("**************************************");


    }
    public void withdraw(double amount){
        System.out.println(this.owner +"取款" +amount);
        state.withdraw(amount);
        System.out.println("現在餘額爲" +state.balance);
        System.out.println("現在賬戶狀態爲" + state.getClass().getName());
        System.out.println("**************************************");

    }
}
abstract class AccountState{
    protected Account acc;
    protected double balance;
    public abstract void deposit(double amount);
    public  abstract void withdraw(double amount);
    public  abstract void stateCheck();

}
class YellowState extends AccountState{
    YellowState(double balance,Account account){
        this.balance=balance;
        this.acc=account;
    }

    @Override
    public void deposit(double amount) {
        balance+=amount;
        stateCheck();
    }

    @Override
    public void withdraw(double amount) {
        balance-=amount;
        stateCheck();
    }

    @Override
    public void stateCheck() {
            if(balance>=0)
                acc.state=new GreenState(balance,acc);
            if(balance<-1000)
                acc.state=new YellowState(balance,acc);
    }
}
class RedState extends AccountState{

   RedState(double balance,Account account){
       this.balance=balance;
       this.acc=account;
    }
    @Override
    public void deposit(double amount) {
       balance+=amount;
        stateCheck();

    }

    @Override
    public void withdraw(double amount) {
        System.out.println("該賬戶不能取款");

    }

    @Override
    public void stateCheck() {
        if(balance>=-1000)
            if(balance<0)
                acc.state=new YellowState(balance,acc);
            else
                acc.state=new GreenState(balance,acc);
    }
}
class GreenState extends AccountState{

   GreenState(double balance,Account account){
       this.balance=balance;
       this.acc=account;
    }
    @Override
    public void deposit(double amount) {
        balance+=amount;
        stateCheck();

    }

    @Override
    public void withdraw(double amount) {
        balance-=amount;
        stateCheck();


    }

    @Override
    public void stateCheck() {
        if(balance<0)
            if(balance>=-1000)
                acc.state=new YellowState(balance,acc);
            else
                acc.state=new RedState(balance,acc);
    }

    
}
class Client7_2{
    public static void main(String[] args) {
            Account account=new Account("yxh",5.0);
            account.deposit(100);
            account.withdraw(200);
            account.deposit(1000);
            account.withdraw(2000);
    }

}
yxh開戶,初始金額爲:5.0
**************************************
yxh存款100.0元
現在餘額爲105.0
現在賬戶狀態爲GreenState
**************************************
yxh取款200.0
現在餘額爲-95.0
現在賬戶狀態爲YellowState
**************************************
yxh存款1000.0元
現在餘額爲905.0
現在賬戶狀態爲GreenState
**************************************
yxh取款2000.0
現在餘額爲-1095.0
現在賬戶狀態爲RedState
**************************************

策略模式

定義一系列算法,封裝每個算法,並使它們可以互換。
策略模式可以讓算法獨立於使用它的客戶端。

image-20200702103516205

  • Strategy 接口定義了一個算法族,它們都實現了 behavior() 方法。
  • Context 是使用到該算法族的類,其中的 doSomething() 方法會調用 behavior(),setStrategy(Strategy) 方法可以動態地改變 strategy 對象,也就是說能動態地改變 Context 所使用的算法。
  • 與狀態模式的比較
    狀態模式的類圖和策略模式類似,並且都是能夠動態改變對象的行爲。但是**狀態模式是通過狀態轉移來改變 Context所組合的 State 對象,而策略模式是通過 Context 本身的決策來改變組合的 Strategy 對象。**所謂的狀態轉移,是指Context 在運行過程中由於一些條件發生改變而使得 State 對象發生改變,注意必須要是在運行過程中。
    狀態模式主要是用來解決狀態轉移的問題,當狀態發生轉移了,那麼 Context 對象就會改變它的行爲;而策略模式主要是用來封裝一組可以互相替代的算法族,並且可以根據需要動態地去替換 Context 使用的算法。

Implementation

設計一個鴨子,它可以動態地改變叫聲。這裏的算法族是鴨子的叫聲行爲。

public interface QuackBehavior {
    void quack();
}

public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("quack!");
    }
}

public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("squeak!");
    }
}

public class Duck {
    private QuackBehavior quackBehavior;

    public void performQuack() {
        if (quackBehavior != null) {
            quackBehavior.quack();
        }
    }

    //根據不同的情況設置不同的鴨子叫
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}

public class Client {
    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.setQuackBehavior(new Squeak());
        duck.performQuack();
        duck.setQuackBehavior(new Quack());
        duck.performQuack();
    }
}


squeak!
quack!

模板方法

定義算法框架,並將一些步驟的實現延遲到子類。
通過模板方法,子類可以重新定義算法的某些步驟,而不用改變算法的結構。

image-20200702161101318

implementation

衝咖啡和沖茶都有類似的流程,但是某些步驟會有點不一樣,要求複用那些相同步驟的代碼。

public abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew();

    abstract void addCondiments();

    void boilWater() {
        System.out.println("boilWater");
    }

    void pourInCup() {
        System.out.println("pourInCup");
    }
}

public class Coffee extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("Coffee.brew");
    }

    @Override
    void addCondiments() {
        System.out.println("Coffee.addCondiments");
    }
}

public class Tea extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("Tea.brew");
    }

    @Override
    void addCondiments() {
        System.out.println("Tea.addCondiments");
    }
}

public class Client {
    public static void main(String[] args) {
        CaffeineBeverage caffeineBeverage = new Coffee();
        caffeineBeverage.prepareRecipe();
        System.out.println("-----------");
        caffeineBeverage = new Tea();
        caffeineBeverage.prepareRecipe();
    }
}
boilWater
Coffee.brew
pourInCup
Coffee.addCondiments
-----------
boilWater
Tea.brew
pourInCup
Tea.addCondiments

訪問者

表示一個作用於某對象結構中的各元素的操作,它使我們可以在不改變各元素的類的前提下定義作用於這些元素的新操作。舉個栗子:醫院的處方單,藥房人員只關心藥品的種類數量,而收費人員只關心藥價,所以各對象所關注的數據是不一樣的,

  • Visitor(抽象訪問者)
  • ConcreteVisitor(具體訪問者)
  • Element(抽象元素)
  • ConcreteElement(具體元素)
  • ObjectStructure(對象結構):可以是組合結構,或者是一個集合

image-20200702161601276

implementation

import java.util.ArrayList;
import java.util.List;

interface Element {
    void accept(Visitor visitor);
}

class CustomerGroup {
    private List<Customer> customers = new ArrayList<>();

    public void accept(Visitor visitor) {
        for (Customer customer : customers) {
            customer.accept(visitor);
        }
    }

    public void addCustomer(Customer customer) {
        customers.add(customer);
    }
}

class Customer implements Element {
    private String name;
    private List<Order> orders = new ArrayList<>();

    public Customer(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void addOrder(Order order) {
        orders.add(order);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
        for (Order order : orders) {
            order.accept(visitor);
        }
    }
}

class Order implements Element {
    private String name;
    private List<Item> items = new ArrayList();

    public Order(String name) {
        this.name = name;
    }

    public Order(String name, String itemName) {
        this.name = name;
        this.addItem(new Item(itemName));
    }

    public   String getName() {
        return name;
    }

    public void addItem(Item item) {
        items.add(item);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
        for (Item item : items) {
            item.accept(visitor);
        }
    }
}

class Item implements Element {
    private String name;

    public Item(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

interface Visitor {
    void visit(Customer customer);

    void visit(Order order);

    void visit(Item item);
}

class GeneralReport implements Visitor {
    private int customersNo;
    private int ordersNo;
    private int itemsNo;

    @Override
    public void visit(Customer customer) {
        System.out.println(customer.getName());
        customersNo++;
    }

    @Override
    public void visit(Order order) {
        System.out.println(order.getName());
        ordersNo++;
    }

    @Override
    public void visit(Item item) {
        System.out.println(item.getName());
        itemsNo++;
    }

    void displayResults() {
        System.out.println("Number of customers: " + customersNo);
        System.out.println("Number of orders: " + ordersNo);
        System.out.println("Number of items: " + itemsNo);
    }
}

class Clientv {
    public static void main(String[] args) {
        Customer customer1 = new Customer("customer1");
        customer1.addOrder(new Order("order1", "item1"));
        customer1.addOrder(new Order("order2", "item1"));
        customer1.addOrder(new Order("order3", "item1"));
        Order order = new Order("order_a");
        order.addItem(new Item("item_a1"));
        order.addItem(new Item("item_a2"));
        order.addItem(new Item("item_a3"));
        Customer customer2 = new Customer("customer2");
        customer2.addOrder(order);
        CustomerGroup customers = new CustomerGroup();
        customers.addCustomer(customer1);
        customers.addCustomer(customer2);
        GeneralReport visitor = new GeneralReport();
        customers.accept(visitor);
        visitor.displayResults();
    }
}

customer1
order1
item1
order2
item1
order3
item1
customer2
order_a
item_a1
item_a2
item_a3
Number of customers: 2
Number of orders: 4
Number of items: 6


解析:文中的element就是item order customer,customer包含 order ,order包含item ,visitor傳入不同的element 遍歷就會不同,比如傳入的是customer,就會先獲取customer的name,再遍歷裏面的order

結構型

適配器

把一個類接口轉換成另一個用戶需要的接口。

image-20200702110713539

  • Target:目標抽象類定義客戶要用的特定領域的接口
  • Adpater:適配器類作爲一個轉換器,對Adaptee和Target進行適配
  • Adaptee:適配者即被適配的角色,adpatee可以作爲成員變量被adapter擁有,也可以作爲父類被adapter繼承,根據“合成複用原則”,在系統中儘量使用關聯關係來替代繼承關係。

implementation

鴨子(Duck)和火雞(Turkey)擁有不同的叫聲,Duck 的叫聲調用 quack() 方法,而 Turkey 調用 gobble() 方法。
要求將 Turkey 的 gobble() 方法適配成 Duck 的 quack() 方法,從而讓火雞冒充鴨子!

//Target類
public interface Duck {
void quack();
}
//Adaptee類
public interface Turkey {		
void gobble();
}

public class WildTurkey implements Turkey {
@Override
public void gobble() {
System.out.println("gobble!");
}
}
//adpter
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
    
    //適配方法
@Override
public void quack() {
turkey.gobble();
}
}
public class Client {
public static void main(String[] args) {
Turkey turkey = new WildTurkey();
Duck duck = new TurkeyAdapter(turkey);
duck.quack();
}
}


橋接

將抽象和實現分離開來,使它們可以獨立變化(把抽象部分和實現部分橋接)

image-20200702111301169
  • Abstraction:抽象類

  • RefinedAbstraction:擴充抽象類

  • Implementor:實現類接口

  • ConcreteImplementor:具體實現類

  • 注意Abstraction中的箭頭指向implementor的意思是:實現了Abstraction接口的接口實現類作爲implor的成員變量

  • 一般固有屬性作爲抽象部分,附加屬性或者外在屬性作爲實現部分,如毛筆與顏色的關係:

    image-20200702113542238

優點:

  • 分離抽象接口及其實現部分
  • 可以取代多層繼承方案,極大地減少了子類的個數
  • 提高了系統的可擴展性,在兩個變化維度中任意擴展一個維度,不需要修改原有系統,符合開閉原則

Implementation

RemoteControl 表示遙控器,指代 Abstraction。
TV 表示電視,指代 Implementor。
橋接模式將遙控器和電視分離開來,從而可以獨立改變遙控器或者電視的實現。

不同品牌的遙控器和不同品牌的TV機,(有點像比如不同型號的毛筆和不同顏色的墨水)

public abstract class TV {
    public abstract void on();

    public abstract void off();

    public abstract void tuneChannel();
}

public class Sony extends TV {
    @Override
    public void on() {
        System.out.println("Sony.on()");
    }

    @Override
    public void off() {
        System.out.println("Sony.off()");
    }

    @Override
    public void tuneChannel() {
        System.out.println("Sony.tuneChannel()");
    }
}

public class RCA extends TV {
    @Override
    public void on() {
        System.out.println("RCA.on()");
    }

    @Override
    public void off() {
        System.out.println("RCA.off()");
    }

    @Override
    public void tuneChannel() {
        System.out.println("RCA.tuneChannel()");
    }
}
public abstract class RemoteControl {
    protected TV tv;

    public RemoteControl(TV tv) {
        this.tv = tv;
    }

    public abstract void on();

    public abstract void off();

    public abstract void tuneChannel();
}

public class ConcreteRemoteControl1 extends RemoteControl {
    public ConcreteRemoteControl1(TV tv) {
        super(tv);
    }

    @Override
    public void on() {
        System.out.println("ConcreteRemoteControl1.on()");
        tv.on();
    }

    @Override
    public void off() {
        System.out.println("ConcreteRemoteControl1.off()");
        tv.off();
    }

    @Override
    public void tuneChannel() {
        System.out.println("ConcreteRemoteControl1.tuneChannel()");
        tv.tuneChannel();
    }
}

public class ConcreteRemoteControl2 extends RemoteControl {
    public ConcreteRemoteControl2(TV tv) {
        super(tv);
    }

    @Override
    public void on() {
        System.out.println("ConcreteRemoteControl2.on()");
        tv.on();
    }

    @Override
    public void off() {
        System.out.println("ConcreteRemoteControl2.off()");
        tv.off();
    }

    @Override
    public void tuneChannel() {
        System.out.println("ConcreteRemoteControl2.tuneChannel()");
        tv.tuneChannel();
    }
}

public class Client {
    public static void main(String[] args) {
        RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());
        remoteControl1.on();
        remoteControl1.off();
        remoteControl1.tuneChannel();
        RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
        remoteControl2.on();
        remoteControl2.off();
        remoteControl2.tuneChannel();
    }
}

組合模式

將對象組合成樹形結構來表示“整體/部分”層次關係,允許用戶以相同的方式處理單獨對象和組合對象。(如文件夾的表示)

image-20200702113718160

  • 舉個栗子就明白了:Component是文件或者文件夾,而Leaf是文件,Composite是文件夾,文件夾裏可能還有文件或者文件夾
  • 組件(Component)類是組合類(Composite)和葉子類(Leaf)的父類
  • 組合對象擁有一個或者多個組件對象,因此組合對象的操作可以委託給組件對象去處理,而組件對象可以是另一個組合對象或者葉子對象。

Implementation


public abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }

    public void print() {
        print(0);
    }

    abstract void print(int level);

    abstract public void add(Component component);

    abstract public void remove(Component component);
}

public class Composite extends Component {
    private List<Component> child;

    public Composite(String name) {
        super(name);
        child = new ArrayList<>();
    }

    @Override
    void print(int level) {
        for (int i = 0; i < level; i++) {
            System.out.print("
                            --
                    ");
        }
        System.out.println("Composite:" + name);
        for (Component component : child) {
            component.print(level + 1);
        }
    }

    @Override
    public void add(Component component) {
        child.add(component);
    }

    @Override
    public void remove(Component component) {
        child.remove(component);
    }
}

public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    void print(int level) {
        for (int i = 0; i < level; i++) {
            System.out.print("
                            --
                    ");
        }
        System.out.println("left:" + name);
    }

    @Override
    public void add(Component component) {
         // 犧牲透明性換取單一職責原則,這樣就不用考慮是葉子節點還是組合節點
        throw new UnsupportedOperationException();
    }

    @Override
    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }
}

public class Client {
    public static void main(String[] args) {
        Composite root = new Composite("root");
        Component node1 = new Leaf("1");
        Component node2 = new Composite("2");
        Component node3 = new Leaf("3");
        root.add(node1);
        root.add(node2);
        root.add(node3);
        Component node21 = new Leaf("21");
        Component node22 = new Composite("22");
        node2.add(node21);
        node2.add(node22);
        Component node221 = new Leaf("221");
        node22.add(node221);
        root.print();
    }
}

Composite:root
--left:1
--Composite:2
----left:21
----Composite:22
------left:221
--left:3

裝飾

爲對象動態添加功能。

image-20200702143137025

  • Component: 抽象構件類
  • ConcreteComponent: 具體構件類
  • Decorator: 抽象裝飾類
  • ConcreteDecorator: 具體裝飾類
  • 以利用裝飾者decorator動態的往component添加新功能,decorator是實現了component接口(爲了與component的子類對象,也就是它所修飾的對象有同樣的方法),同時把component作爲成員變量,是爲了操作修飾的對象

implementation

設計不同種類的飲料,飲料可以添加配料,比如可以添加牛奶,並且支持動態添加新配料。每增加一種配料,該飲料的價格就會增加,要求計算一種飲料的價格。
下圖表示在 DarkRoast 飲料上新增新添加 Mocha 配料,之後又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它們都繼承自相同父類,都有 cost() 方法,外層類的 cost() 方法調用了內層類的 cost() 方法。

image-20200702143329082


//beverage 飲料
public interface Beverage {
    double cost();
}
//darkroast 深烘焙 一種飲料口味
public class DarkRoast implements Beverage {
    @Override
    public double cost() {
        return 1;
    }
}

//House Blend 也是一種飲料口味
public class HouseBlend implements Beverage {
    @Override
    public double cost() {
        return 1;
    }
}

//裝飾者
public abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;
}

//牛奶
public class Milk extends CondimentDecorator {
    public Milk(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public double cost() {
        return 1 + beverage.cost();
    }
   
}

//摩卡
    public class Mocha extends CondimentDecorator {
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }

        @Override
        public double cost() {
            return 1 + beverage.cost();
        }
    }

    public class Client {
        public static void main(String[] args) {
            //造一杯咖啡
            Beverage beverage = new HouseBlend();
            //咖啡里加摩卡  ,這裏體現裝飾
            beverage = new Mocha(beverage);
            //咖啡里加牛奶,體現裝飾
            beverage = new Milk(beverage);
            System.out.println(beverage.cost());
        }
    }


3.0

設計原則
類應該對擴展開放,對修改關閉:也就是添加新功能時不需要修改代碼。飲料可以動態添加新的配料,而不需要去修改飲料的代碼。(也就是說,可以利用裝飾者decorator動態的往component添加新功能,decorator是實現了component接口(爲了與component的子類對象,也就是它所修飾的對象有同樣的方法),同時把component作爲成員變量,是爲了操作修飾的對象)
不可能把所有的類設計成都滿足這一原則,應當把該原則應用於最有可能發生改變的地方。

外觀(facade)

提供了一個統一的接口,用來訪問子系統中的一羣接口,從而讓子系統更容易使用。

image-20200702145629901

implementation

觀看電影需要操作很多電器,使用外觀模式實現一鍵看電影功能。


public interface Investor {
    public void response(Stock stock);
}

public class SubSystemA {
    public void turnOnTV() {
        System.out.println("turnOnTV()");
    }

   
}
public class SubSystemB {

    public void setCD(String cd) {
        System.out.println("setCD( " + cd + " )");
    }
}
public class SubSystemC {
    public void startWatching() {
        System.out.println("startWatching()");
    }
}

public class Facade {
    private SubSystemA subSystemA = new SubSystemA();
    private SubSystemB subSystemB = new SubSystemB();
    private SubSystemC subSystemC = new SubSystemC();

    public void watchMovie() {
        subSystemA.turnOnTV();
        subSystemB.setCD("a movie");
        subSystemC.startWatching();
    }
}

public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.watchMovie();
    }
}

implementation2

(其實就是三個文件操作聚合在同一個類,這個模式的中心思想也是如此)

某系統需要提供一個文件加密模塊,加密流程包括三個操作,分別是讀取源文件、加密、保存加密之後的文件。讀取文件和保存文件使用流來實現,這三個操作相對獨立,其業務代碼封裝在三個不同的類中。現在需要提供一個統一的加密外觀類,用戶可以直接使用該加密外觀類完成文件的讀取、加密和保存三個操作,而不需要與每一個類進行交互,使用外觀模式設計該加密模塊。請根據類圖編程實現該系統,並寫出相應Java代碼。

img

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author:yxh
 *外觀模式實例
 */

//加密外觀類:外觀類
class EncryptFacade {
    /************************/
    private FileReader reader;
    private CipherMachine cipher;
    private FileWriter writer;

    EncryptFacade() {
        reader = new FileReader();
        cipher = new CipherMachine();
        writer = new FileWriter();
    }

    public void fileEncrypt(String fileNameSrc, String fileNameDes) {
        //從文件中讀取的字符
        String fileString = reader.read(fileNameSrc);
        //加密後的字符
        String encryptedString = cipher.encrypt(fileString);
        //保存
        writer.write(encryptedString, fileNameDes);
    }


/************************/
}

//文件讀取類:子系統類
class FileReader {
    public String read(String fileNameSrc) {
        System.out.println("讀取文件,獲取明文。");
        StringBuffer sb = new StringBuffer();
        try {
            FileInputStream inFS = new FileInputStream(fileNameSrc);
            int data;
            while ((data = inFS.read()) != -1) {
                sb = sb.append((char) data);
            }
            inFS.close();
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在!");
        } catch (IOException e) {
            System.out.println("文件操作錯誤!");
        }
        return sb.toString();
    }
}

//數據加密類:子系統類
class CipherMachine {
    public String encrypt(String plainText) {
        System.out.println("數據加密,將明文轉換爲密文。");
        String es = "";
        for (int i = 0; i < plainText.length(); i++) {
            String c = String.valueOf(plainText.charAt(i) % 7);
            es += c;
        }
        return es;
    }
}

//文件保存類:子系統類
class FileWriter {
    public void write(String encryptStr, String fileNameDes) {
        System.out.println("保存密文,寫入文件。");
        try {
            FileOutputStream outFS = new FileOutputStream(fileNameDes);
            outFS.write(encryptStr.getBytes());
            outFS.close();
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在!");
        } catch (IOException e) {
            System.out.println("文件操作錯誤!");
        }
    }
}

//客戶端測試類
class Client3_1 {
    public static void main(String args[]) {
        //********************************//
        EncryptFacade ef = new EncryptFacade();
        ef.fileEncrypt("src.txt", "des.txt");

        //********************************//
    }
}



設計原則

最少知識原則:只和你的密友談話。也就是說客戶對象所需要交互的對象應當儘可能少。

享元模式

利用共享的方式來支持大量細粒度的對象,這些對象一部分內部狀態是相同的。

  • JDK中的應用
    Java 利用緩存來加速大量小對象的訪問時間。
    java.lang.Integer#valueOf(int)
    java.lang.Boolean#valueOf(boolean)
    java.lang.Byte#valueOf(byte)
    java.lang.Character#valueOf(char)

image-20200702150957141

  • Flyweight:享元對象
  • IntrinsicState:內部狀態,享元對象共享內部狀態
  • ExtrinsicState:外部狀態,每個享元對象的外部狀態不同

implementation

public interface Flyweight {
    void doOperation(String extrinsicState);
}

public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void doOperation(String extrinsicState) {
        System.out.println("Object address: " + System.identityHashCode(this));
        System.out.println("IntrinsicState: " + intrinsicState);
        System.out.println("ExtrinsicState: " + extrinsicState);
    }
}

public class FlyweightFactory {
    private HashMap<String, Flyweight> flyweights = new HashMap<>();

    Flyweight getFlyweight(String intrinsicState) {
        if (!flyweights.containsKey(intrinsicState)) {
            Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
            flyweights.put(intrinsicState, flyweight);
        }
        return flyweights.get(intrinsicState);
        }
}

public class Client {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight flyweight1 = factory.getFlyweight("aa");
        Flyweight flyweight2 = factory.getFlyweight("aa");
        flyweight1.doOperation("x");
        flyweight2.doOperation("y");
    }
}

從輸出結果可以看到,flyweight1和flyweight2是同一個變量 也就是共享的變量,而doOperation方法傳入的是不同的參數。

Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: x
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: y

implementation2

某軟件公司設計一個圍棋軟件,在系統中只存在一個白棋對象和一個黑棋對象,但是他們可以在棋盤的不同位置顯示多次。請使用享元模式設計該軟件,並寫出相應Java代碼。要求使用簡單工廠模式和單例模式實現享元工廠類的設計。

/**
 * 享元模式實例
 * @author:yxh
 */

import java.util.Hashtable;

/**
 * 享元工廠
 * */
class IgoChessmanFactory{
    private static Hashtable ht;
    private static IgoChessmanFactory instance;


    public IgoChessmanFactory() {
        ht=new Hashtable();
      ht.put("b",new BlackIgoChessman());
      ht.put("w",new WhiteIgoChessman());
    }
    public static IgoChessmanFactory getInstance(){
        if(instance==null){
            synchronized (IgoChessmanFactory.class){
                if(instance==null)
                instance=new IgoChessmanFactory();
            }
        }
        return instance;
    }
    public  IgoChessman getIgoChessman(String color){
        return (IgoChessman) ht.get(color);
    }
}

/**
 * 黑棋子
 */
class BlackIgoChessman extends IgoChessman{
    @Override
    String getColor() {
        return "黑色";
    }

    @Override
    void display(Coordinates coordinates) {
        System.out.println("棋子顏色:"+this.getColor()+",棋子位置:"+coordinates.getX()+","+coordinates.getY());
    }
}

/**
 * 白棋子
 */
class WhiteIgoChessman extends IgoChessman{
    @Override
    String getColor() {
        return "白色";
    }

    @Override
    void display(Coordinates coordinates) {
        System.out.println("棋子顏色:"+this.getColor()+",棋子位置:"+coordinates.getX()+","+coordinates.getY());
    }
}

/**
 * 棋子抽象類
 */
abstract  class IgoChessman{

    abstract String getColor();
    abstract void display(Coordinates coordinates);
}

/**
 * 座標類
 */
class Coordinates{
    int x;
    int y;
    public Coordinates(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

/**
 * 測試類
 */
class Client{
    public static void main(String[] args) {
            IgoChessman black1,black2,black3,white1,white2;
            IgoChessmanFactory factory;
            factory=IgoChessmanFactory.getInstance();
            black1=factory.getIgoChessman("b");
            black2=factory.getIgoChessman("b");
            black3=factory.getIgoChessman("b");
        System.out.println("判斷兩顆黑棋是否相同"+(black1==black2));

        white1=factory.getIgoChessman("w");
        white2=factory.getIgoChessman("w");
        System.out.println("判斷兩顆白棋是否相同"+(white1==white2));
        black1.display(new Coordinates(1,2));
        black2.display(new Coordinates(3,4));
        black3.display(new Coordinates(5,2));
        white1.display(new Coordinates(1,2));
        white2.display(new Coordinates(8,7));
    }
}
判斷兩顆黑棋是否相同true
判斷兩顆白棋是否相同true
棋子顏色:黑色,棋子位置:1,2
棋子顏色:黑色,棋子位置:3,4
棋子顏色:黑色,棋子位置:5,2
棋子顏色:白色,棋子位置:1,2
棋子顏色:白色,棋子位置:8,7

代理模式

image-20200702153540971
  • Subject:抽象主題類

  • Proxy:代理類

  • RealSubject:真實主題類

  • 客戶端可以通過代理類間接調用真實主題類的方法,同時代理類繼承Subject並且RealSubject作爲Proxy的成員變量。

基於JDK的動態代理實現步:

第一步,創建一個接口(abstractSubject)
第二步,創建實現abstractSubject接口的類(realSubject)
第三步,創建實現InvocationHandler接口的類,在invoke方法中實現業務代理
第四步,利用Proxy類提供的靜態工廠方法創建代理類(Proxy)。
第五步,通過代理類調用接口定義的方法。

代理主要有以下四類

  • 遠程代理(Remote Proxy):控制對遠程對象(不同地址空間)的訪問,它負責將請求及其參數進行編碼,並向不同地址空間中的對象發送已經編碼的請求。
  • 虛擬代理(Virtual Proxy):根據需要創建開銷很大的對象,它可以緩存實體的附加信息,以便延遲對它的訪問,例如在網站加載一個很大圖時,不能馬上完成,可以用虛擬代理緩存圖片的大小信息,然後生成一張臨時圖片代替原始圖片。
  • 保護代理(Protection Proxy):按權限控制對象的訪問,它負責檢查調用者是否具有實現一個請求所必須的訪問權限
  • 智能代理(Smart Reference):取代了簡單的指針,它在訪問對象時執行一些附加操作:記錄對象的引用次數;當第一次引用一個對象時,將它裝入內存;在訪問一個實際對象前,檢查是否已經鎖定了它,以確保其它
    對象不能改變它。

implementation

import java.util.*;

//抽象日誌記錄類:抽象主題
interface AbstractLog
{
	public void method();
}

//業務類:真實主題
class BusinessClass implements AbstractLog
{
	public void method()
	{
		System.out.println("真實業務方法!");
	}
}

//日誌記錄代理類:代理主題
class LoggerProxy implements AbstractLog
{
	private BusinessClass business;
	
	public LoggerProxy()
	{
		business = new BusinessClass();
	}
	
	public void method()
	{
		Calendar calendar = new GregorianCalendar();
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH) + 1;
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		int hour = calendar.get(Calendar.HOUR) + 12;
		int minute = calendar.get(Calendar.MINUTE);
		int second = calendar.get(Calendar.SECOND);
		String dateTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second + "!";
		System.out.println("方法method()被調用,調用時間爲" + dateTime);
		try{
			business.method();
			System.out.println("方法method()調用成功!");
		}
		catch(Exception e)
		{
			System.out.println("方法method()調用失敗!");
		}
	}
}

//客戶端測試類
public class ProxyPattern
{
	public static void main(String args[])
	{
		AbstractLog al;
		al = new LoggerProxy();
		al.method();
	}
}
方法method()被調用,調用時間爲2020-7-2 16:1:4!
真實業務方法!
方法method()調用成功!

設計原則

設計原則名稱 設計原則簡介
單一職責原則(Single Responsibility Principle, SRP) 類的職責要單一,不能將太多的職責放在一個類中
開閉原則(Open-Closed Principle, OCP) 軟件實體對擴展是開放的,但對修改是關閉的,即在不修改一個軟件實體的基礎上去擴展其功能
里氏代換原則(Liskov Substitution Principle, LSP) 在軟件系統中,一個可以接受基類對象的地方必然可以接受一個子類對象
依賴倒轉原則(Dependency Inversion Principle, DIP) 要針對抽象層編程,而不要針對具體類編程
接口隔離原則(Interface Segregation Principle, ISP) 使用多個專門的接口來取代一個統一的接口
合成複用原則(Composite Reuse Principle, CRP) 在系統中應該儘量多使用組合和聚合關聯關係,儘量少使用甚至不使用繼承關係
迪米特法則(Law of Demeter, LoD) 一個軟件實體對其他實體的引用越少越好,或者說如果兩個類不必彼此直接通信,那麼這兩個類就不應當發生直接的相互作用,而是通過引入一個第三者發生間接交互。

色:黑色,棋子位置:3,4
棋子顏色:黑色,棋子位置:5,2
棋子顏色:白色,棋子位置:1,2
棋子顏色:白色,棋子位置:8,7


## 代理模式

<img src="https://gitee.com/yxh99/imagebed/raw/master/img/image-20200702153540971.png" alt="image-20200702153540971" style="zoom:200%;" />

- Subject:抽象主題類

- Proxy:代理類

- RealSubject:真實主題類

- 客戶端可以通過代理類間接調用真實主題類的方法,同時代理類繼承Subject並且RealSubject作爲Proxy的成員變量。

  

### 基於JDK的動態代理實現步:

第一步,創建一個接口(abstractSubject)
第二步,創建實現abstractSubject接口的類(realSubject)
第三步,創建實現InvocationHandler接口的類,在invoke方法中實現業務代理
第四步,利用Proxy類提供的靜態工廠方法創建代理類(Proxy)。
第五步,通過代理類調用接口定義的方法。

### 代理主要有以下四類

- 遠程代理(Remote Proxy):**控制對遠程對象(不同地址空間)的訪問**,它負責將請求及其參數進行編碼,並向不同地址空間中的對象發送已經編碼的請求。
- 虛擬代理(Virtual Proxy):**根據需要創建開銷很大的對象,它可以緩存實體的附加信息,以便延遲對它的訪問**,例如在網站加載一個很大圖時,不能馬上完成,可以用虛擬代理緩存圖片的大小信息,然後生成一張臨時圖片代替原始圖片。
- 保護代理(Protection Proxy):**按權限控制對象的訪問,它負責檢查調用者是否具有實現一個請求所必須的訪問權限**。
- 智能代理(Smart Reference):**取代了簡單的指針,它在訪問對象時執行一些附加操作**:記錄對象的引用次數;當第一次引用一個對象時,將它裝入內存;在訪問一個實際對象前,檢查是否已經鎖定了它,以確保其它
  對象不能改變它。

### implementation

```java
import java.util.*;

//抽象日誌記錄類:抽象主題
interface AbstractLog
{
	public void method();
}

//業務類:真實主題
class BusinessClass implements AbstractLog
{
	public void method()
	{
		System.out.println("真實業務方法!");
	}
}

//日誌記錄代理類:代理主題
class LoggerProxy implements AbstractLog
{
	private BusinessClass business;
	
	public LoggerProxy()
	{
		business = new BusinessClass();
	}
	
	public void method()
	{
		Calendar calendar = new GregorianCalendar();
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH) + 1;
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		int hour = calendar.get(Calendar.HOUR) + 12;
		int minute = calendar.get(Calendar.MINUTE);
		int second = calendar.get(Calendar.SECOND);
		String dateTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second + "!";
		System.out.println("方法method()被調用,調用時間爲" + dateTime);
		try{
			business.method();
			System.out.println("方法method()調用成功!");
		}
		catch(Exception e)
		{
			System.out.println("方法method()調用失敗!");
		}
	}
}

//客戶端測試類
public class ProxyPattern
{
	public static void main(String args[])
	{
		AbstractLog al;
		al = new LoggerProxy();
		al.method();
	}
}
方法method()被調用,調用時間爲2020-7-2 16:1:4!
真實業務方法!
方法method()調用成功!

設計原則

設計原則名稱 設計原則簡介
單一職責原則(Single Responsibility Principle, SRP) 類的職責要單一,不能將太多的職責放在一個類中
開閉原則(Open-Closed Principle, OCP) 軟件實體對擴展是開放的,但對修改是關閉的,即在不修改一個軟件實體的基礎上去擴展其功能
里氏代換原則(Liskov Substitution Principle, LSP) 在軟件系統中,一個可以接受基類對象的地方必然可以接受一個子類對象
依賴倒轉原則(Dependency Inversion Principle, DIP) 要針對抽象層編程,而不要針對具體類編程
接口隔離原則(Interface Segregation Principle, ISP) 使用多個專門的接口來取代一個統一的接口
合成複用原則(Composite Reuse Principle, CRP) 在系統中應該儘量多使用組合和聚合關聯關係,儘量少使用甚至不使用繼承關係
迪米特法則(Law of Demeter, LoD) 一個軟件實體對其他實體的引用越少越好,或者說如果兩個類不必彼此直接通信,那麼這兩個類就不應當發生直接的相互作用,而是通過引入一個第三者發生間接交互。

迪米特法則補充:迪米特法則就是指一個軟件實體應當儘可能少的與其他實體發生相互作用。這樣,當一個模塊修改時,就會盡量少的影響其他的模塊,擴展會相對容易,這是對軟件實體之間通信的限制,它要求限制軟件實體之間通信的寬度和深度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章