設計模式你真的懂了嗎?

綾曉路發表於2019-01-23

設計模式你真的懂了嗎?

前言

設計模式這個老生常談的東西,在以往的面試題以及面試中會經常問到,關於設計模式,網上有很多文章對其進行闡述。

但是看了那麼多設計模式你真的懂了嗎?

在專案中除了單例模式,你還用到了哪些設計模式呢?

各個設計模式又有什麼利弊呢?如何去正確的考量呢?

在這裡,我只是通過自己的理解,把我所知道的寫出來,文章很長,建議先收藏起來,慢慢看(ps:_(:з」∠)_輕噴)。

—、設計模式的分類

總的來說,設計模式分為三種:建立模式、結構模式、行為模式

1. 建立模式

建立模式:提供例項化的方法,為適合的狀況提供相應的物件建立方法。 其中屬於建立模式的是:單例模式、建造者模式、工廠方法模式、抽象工廠模式、原型模式。

1.單例模式(Singleton Pattern)

定義:確保一個類中有且只有一個例項,而且自行例項化。 這裡可以去看我之前寫的如何寫出一個好的單例模式

2.建造者模式 (Builder Pattern)

定義:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。 更多內容請看 淺析builder模式

3.工廠方法模式(Factory Method Pattern)

定義:定義一個用於建立物件的介面,讓子類決定例項化哪一個類,工廠方法使一個類的例項化延遲到其子類。 下面引入一個簡單工廠模式: 肉製品加工廠加工不同的肉製品。

//定一個抽象方法用來生產肉製品
abstract class Product {
	public abstract void method();
}
class MuttonProduct extends Product {
	@Override
	public void mothod() {
		System.out.println("生產出來了羊肉....");
	}
}
class PorkProduct extends Product {
	@Override
	public void mothod() {
		System.out.println("生產出來了豬肉....");
	}
}
abstract class Creator {
	public abstract <T extends Product> T createProduce(Class<T> product);
}

class OneCreator extends Creator{
	/**
	 * 傳入的引數,是Product類的子類。
	 */
	@Override
	public <T extends Product> T createProduce(Class<T> product) {
		Product p = null;
		try {
			//Class類建立一個 工廠類例項
			p = (Product) Class.forName(product.getName()).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return (T) p;
	}
}

public class Test {
	public static void main(String[] args) {
		//通過建立子類物件,將介面例項化。
		Creator cFactory = new OneCreator();
		//通過子類的方法,建立一個需要的物件。
		ForkProduct food  = cFactory.createProduce(ForkProduct.class);
		food.mothod();
	}
}
複製程式碼

Product, 抽象類,表示不同的肉製品。

PorkProduct,實現類,繼承Product。

MuttonProduct,同上。

Creator抽象工廠類,提供加工方法。

OneCreator實現類,加工不同的肉製品。

工廠模式的優點:

  1. 程式碼機構清晰,有很好的封裝性。降低了模組間的耦合性。
  2. 有很好的擴充套件性。
  3. 遮蔽產品類,產品類對外提供的是一個介面,只要介面不改變系統中的上層模組就不會發生改變。

#####擴充套件

1.替代單例
class SingtonFactory {
	private static Singleton single;
	static {
		try {
			Class c = Class.forName(Singleton.class.getName());
			//獲取無參構造
			Constructor<Singleton> constractor = c.getDeclaredConstructor();
			//設定無參構造是可訪問的
			constractor.setAccessible(true);
			//產生一個例項物件
			single = constractor.newInstance();
		} catch (Exception e) {
			System.out.println("建立失敗。。。")
		}
	}
	public Singleton getSingle() {
		return single;
	}
}

class Singleton {
	//構造私有化
	private Singleton {
	}
	public void method () {
		System.out.println("sington。。。。")
	}
}
複製程式碼
2.延遲初始化

一個物件被使用完畢,並不立刻釋放,保持其初始狀態,等待再次被使用

interface Product {
	void method();
}
class MuttonProduct implements Product {
	@Override
	public void mothod() {
		System.out.println("生產出來了羊肉....");
	}
}
class PorkProduct implements Product {
	@Override
	public void mothod() {
		System.out.println("生產出來了豬肉....");
	}
}
abstract class Creator {
	public abstract <T extends Product> T createProduce(String type);
}

class OneCreator extends Creator{
	private static Map<String,Product> map = new HashMap<String,Product>();
	@Override
	public <T extends Product> T createProduce(String type) {
		Product p = null;
		if(map.containsKey(type)) {
			p = map.get(type);
		} else {
			if(type.equals("pork")) {
				p = new PorkProduct();
			} else {
				p = new MuttonProduct();
			}
		}
		map.put(type, p);
		return p;
	}
}

public class Test {
	public static void main(String[] args) {
		for(int i=0;i<5;i++){
			Product p = new OneCreator().createProduce("pork");
			p.mothod();
			System.out.println("------------------------------");
		}
	}
}
複製程式碼
4.抽象工廠模式(Abstract Factory)

定義:為建立一組相關或相互依賴的物件提供一個介面,而且無須指定他們的具體類。

抽象工廠模式是工廠模式的升級版,針對的是多業務型別。

通用模式:

設計模式你真的懂了嗎?

public class MyClass {
    public static void main(String [] args) {
        IProduct p1 = new Creator1();
        ProductA1 a1 = (ProductA1) p1.createA();
        a1.method();
        IProduct p2 = new Creator();
        ProductB1 b1 = (ProductB1) p2.createB();
        b1.method();
    }
}

abstract class AbstractProductA {
    public void shareMethod (){
        System.out.println("生產產品共同的方法");
    }
    abstract void method();
}
abstract class AbstractProductB {
    public void shareMethod () {
        System.out.println("生產產品共同的方法");
    }
    abstract void method();
}
interface IProduct {
    AbstractProductA createA();
    AbstractProductB createB();
}
class ProductA1 extends AbstractProductA {

    @Override
    void method() {
        System.out.println("生產A1");
    }
}
class ProductA2 extends AbstractProductA {

    @Override
    void method() {
        System.out.println("生產A2");
    }
}
class ProductB1 extends AbstractProductB{

    @Override
    void method() {
        System.out.println("生產B1");
    }
}
class ProductB2 extends  AbstractProductB {

    @Override
    void method() {
        System.out.println("生產B2");
    }
}

class Creator1 implements IProduct{

    @Override
    public AbstractProductA createA() {
        return new ProductA1();
    }

    @Override
    public AbstractProductB createB() {
        return new ProductB2();
    }
}
class Creator implements IProduct {

    @Override
    public AbstractProductA createA() {
        return new ProductA2();
    }

    @Override
    public AbstractProductB createB() {
        return new ProductB1();
    }
}
複製程式碼

抽象工廠模式 一般是 一個介面,多個抽象類,n個實現類,從上面的程式碼可以看出它除了有工廠方法模式的優點外,還可以在類的內部對產品族進行約束。所謂產品族,是指位於不同產品等級結構中功能相關聯的產品組成的家族。

抽象工廠模式的缺點也是顯而易見的,就是產品族的擴充套件比較困難。

5.原型模式

定義:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。

java提供了一個Cloneable介面,來標示這個類是可拷貝的,通過該介面中的clone方法對物件進行拷貝。

例子:某電商平臺雙十一搞活動,需要群發郵件通知。

class MyClass {
    public static void main(String [] args) {
        Mail mail = new Mail(new Template());
        for (int i = 0 ;i < 100; i++) {
            try {
                Mail cMail = (Mail) mail.clone();
                cMail.setReceiver(getReceiver(6) + ".com");
                sendMail(cMail);
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void sendMail(Mail cMail) {
        System.out.println("收件人" + cMail.getReceiver() + "傳送成功");
    }

    private static String getReceiver(int i) {
        String s = "abcdefghijklmnopqrstuvwxz";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int j =0 ;j <= i; j++) {
            sb.append(s.charAt(random.nextInt(s.length())));
        }
        return sb.toString();
    }
}

class Template {
    private String subject = "雙十一優惠活動";
    private String context = "雙十一優惠活動,全場滿99減5元";

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getContext() {
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }
}
class Mail implements Cloneable {
    // 收件人
    private String receiver;
    //標題
    private String subject;
    // 內容
    private String context;

    public Mail(Template template) {
        this.subject = template.getSubject();
        this.context = template.getContext();
    }

    public String getReceiver() {
        return receiver;
    }

    public void setReceiver(String receiver) {
        this.receiver = receiver;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getContext() {
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }
    // 重寫 clone
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Mail mail = (Mail) super.clone();
        return mail;
    }
}
複製程式碼

原型模式是記憶體二進位制流的法拷貝,比new一個新的物件好的多。

原型模式是從記憶體中拷貝,建構函式是不會執行的。拷貝分為淺拷貝和深拷貝,淺拷貝只是拷貝物件,其物件內部的陣列、引用物件都不會拷貝。

原型模式需要注意的是:出現final的物件或變數,不能clone

深拷貝的例子:

public class Prototype {
	public static void main(String[] args) {
			//建立一個物件
			MyClone myClone = new MyClone();
			myClone.setArrayList("abs");
			//將該物件clone。
			MyClone clone2 = (MyClone) myClone.clone();
			clone2.setArrayList("gh");
			//輸出原物件的結果。
			System.out.println("原物件...."+myClone.getArrayList());
			//輸出拷貝後的結果。
			System.out.println("拷貝結果...."+clone2.getArrayList());
	}
}
class MyClone implements Cloneable{
	private ArrayList<String> arrayList = new ArrayList<String>();
	@SuppressWarnings("unchecked")
	@Override
	public MyClone clone(){
		MyClone myClone =null;
		try {
			myClone = (MyClone) super.clone();
			//把私有物件也進行拷貝。做到深拷貝的效果
			myClone.arrayList = (ArrayList<String>) this.arrayList.clone();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return myClone;
	}
	public ArrayList<String> getArrayList() {
		return arrayList;
	}
	public void setArrayList(String name) {
		this.arrayList.add(name);
	}
	
}
複製程式碼

輸出結果:

原物件....[abs]
拷貝結果....[abs, gh]
複製程式碼

2.結構模式

結構模式:通常用來處理實體之間的關係,使得這些實體能夠更好地協同工作。

其中屬於結構模式的是:裝飾模式、介面卡模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

1.代理模式(Proxy Pattern)

定義: 為其他物件提供一種代理以控制對這個物件的訪問。

例子:明星拍廣告,要和經紀人溝通。

public class MyClass {
    public static void main(String [] args) {
        Agent mAgent = new Agent(new Star());
        mAgent.shootAdvertisement();
    }
}

interface Subject {
    public void shootAdvertisement();
}

class Star implements Subject {
    @Override
    public void shootAdvertisement() {
        System.out.println("拍廣告。。。");
    }
}

class Agent implements Subject {
    // 經紀人 ,代理類
    private Subject star = null;
    // 設定被代理類
    public Agent(Subject star) {
        this.star = star;
    }

    @Override
    public void shootAdvertisement() {
        before();
        star.shootAdvertisement();
        after();
    }

    private void after() {
        System.out.println("處理拍廣告之前的各項工作");
    }

    private void before() {
        System.out.println("處理拍廣告之後的善後工作");
    }
}
複製程式碼

代理模式分為:

  • 靜態代理模式

    • 普通代理模式
    • 強制帶離模式
  • 動態代理模式

普通代理模式:要求客戶端只能訪問代理角色,而不能訪問真實角色。

例子:遊戲代練

public class MyClass {
    public static void main(String [] args) {
        GameProxy gameProxy = new GameProxy("Miss");
        gameProxy.login();
        gameProxy.upgrade();
    }
}

interface IGamePlayer {
    public void login();
    public void upgrade();
}

class GamePlayer implements IGamePlayer {
    public String name = "";

    public GamePlayer(IGamePlayer gamePlayer, String name) {
        if (gamePlayer == null) {
            try {
                throw new Exception("不能建立物件");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            this.name = name;
        }
    }

    @Override
    public void login() {
        System.out.println(this.name + "-登入成功");
    }

    @Override
    public void upgrade() {
        System.out.println("升級了!!!!");
    }
}

class GameProxy implements IGamePlayer {
    private GamePlayer mGamePlayer = null;

    public GameProxy(String name) {
        this.mGamePlayer = new GamePlayer(this, name);

    }

    @Override
    public void login() {
        mGamePlayer.login();
    }

    @Override
    public void upgrade() {
        mGamePlayer.upgrade();
    }
}
複製程式碼
強制代理

必須通過真實角色找到代理角色,否則無法訪問

public class MyClass {
    public static void main(String [] args) {
        GamePlayer player = new GamePlayer("Miss");
        player.login();
        player.upgrade();
        GameProxy proxy = new GameProxy(player.getPlayer());
        proxy.login();
        proxy.upgrade();
    }
}

interface IGamePlayer {
    public void login();
    public void upgrade();
    IGamePlayer getPlayer();
}

class GamePlayer implements IGamePlayer {
    private IGamePlayer player;
    private String name;
    public GamePlayer(String name) {
        this.name = name;
    }

    private boolean isProxy() {
        if (this.player == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public void login() {
        if (this.isProxy()) {
            System.out.println(this.name + "-登入成功");
        } else {
            System.out.println("請使用代理訪問");
        }
    }

    @Override
    public void upgrade() {
        if (this.isProxy()) {
            System.out.println("升級了!!!!");
        } else {
            System.out.println("請使用代理訪問");
        }
    }

    @Override
    public IGamePlayer getPlayer() {
        this.player = new GameProxy(this);
        return this.player;
    }
}

class GameProxy implements IGamePlayer {
    private IGamePlayer gamePlayer;

    public GameProxy(IGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }

    @Override
    public void login() {
        this.gamePlayer.login();
    }

    @Override
    public void upgrade() {
        this.gamePlayer.upgrade();
    }

    @Override
    public IGamePlayer getPlayer() {
        return this;
    }
}
複製程式碼

輸出結果:

請使用代理訪問
請使用代理訪問
Miss-登入成功
升級了!!!!
複製程式碼
動態代理

動態代理:實現階段不用關係代理是哪個,而在執行階段指定具體哪個代理。

public class MyClass {
    public static void main(String [] args) {
        IGamePlayer mGamePlayer = new GamePlayer("Miss");
        InvocationHandler handler = new GameProxy(mGamePlayer);
        //生成一個代理者
        // ClassLoader: 指定當前目標物件使用類載入器
        // Class<?> [] interface: 目標物件實現介面的型別
        // InvocationHandler: 事件處理,執行目標物件的方法時,會觸發事件處理器的方法。
        IGamePlayer player = (IGamePlayer) Proxy.newProxyInstance(mGamePlayer.getClass().getClassLoader(), new Class[]{IGamePlayer.class}, handler);
        player.login("Miss", "123456a");
        player.upgrade();
    }
}

interface IGamePlayer {
    public void login(String name, String password);
    public void upgrade();
}

class GamePlayer implements IGamePlayer {
    private String name = "";

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

    @Override
    public void login(String name, String password) {
        System.out.println(name + "登入成功");
    }

    @Override
    public void upgrade() {
        System.out.println(this.name + "升級了");
    }
}

class GameProxy implements InvocationHandler {
	// 被代理的物件
    private Object obj;
	// 將需要代理的例項通過構造方法傳遞給代理
    public GameProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        if (method.getName().equalsIgnoreCase("login")) {
        // 在這個方法不收影響的情況下,在方法的前後新增新的功能
        // 從側面切入從而達到擴充套件的效果的程式設計,就是面向切面程式設計(AOP Aspect Oriented Programming)				
            System.out.println("代理登入了");
            Object result = method.invoke(this.obj, objects);
            return  result;
        }
        Object result = method.invoke(this.obj, objects);
        return  result;
    }
}
複製程式碼

動態代理模式的一般模式:

public class MyClass {
    public static void main(String [] args) {
        Subject subject = new MySubject();
        InvocationHandler handler = new MyInvocationHandler(subject);
        ClassLoader loader = subject.getClass().getClassLoader();
        Class <?>[] in = subject.getClass().getInterfaces();
        Subject s = DynamicProxy.newProxyInstance(loader, in, handler);
        s.doSomething();
    }
}

interface Subject {
    void doSomething();
}
class MySubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("吃飯睡覺打豆豆");
    }
}
interface Advice {
    public void exec();
}
class BeforeAdvice implements Advice {
    @Override
    public void exec() {
        System.out.println("前置方法");
    }
}
class AfterAdvice implements Advice {
    @Override
    public void exec() {
        System.out.println("後置方法");
    }
}
class MyInvocationHandler implements InvocationHandler {
    private Subject mySubject;

    public MyInvocationHandler(Subject mySubject) {
        this.mySubject = mySubject;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        Advice beforeAdvice = new BeforeAdvice();
        beforeAdvice.exec();
        Object result = method.invoke(this.mySubject, objects);
        Advice afterAdvice = new AfterAdvice();
        afterAdvice.exec();
        return result;
    }
}
class DynamicProxy {
    public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler){
        @SuppressWarnings("unchecked")
        T t = (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
        return t;
    }
}
複製程式碼
2.裝飾模式(Decorator Pattern)

定義:動態的給一個物件新增一些額外的職責。就增加功能來說,裝飾模式相比生成子類更佳靈活

模式中的角色
  • 抽象構建(Component):定義個個抽象介面,用於給物件動態的新增職責。
  • 具體構建(ConcreteComponent):定義一個具體的物件,也可以給這個物件新增一些職責。
  • 裝飾類(Decorator):裝飾抽象類。繼承了Component,從外類來擴充套件Component類的功能。
  • 具體裝飾類:(ConcretorDecorator):負責給構建物件新增職責。

模式的一般模式:

public class MyClass {
    public static void main(String [] args) {
        Component component = new ConcreteComponent();
        ConcreteDecorator decorator = new ConcreteDecorator(component);
        decorator.doSomething();
    }
}

abstract class Component {
   public abstract void doSomething();
}
class ConcreteComponent extends Component {

    @Override
    public void doSomething() {
        System.out.println("具體實現的方法");
    }
}
abstract class Decorator extends Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void doSomething() {
        this.component.doSomething();
    }
}
class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void doSomething() {
        System.out.println("方法前要執行的動作");
        super.doSomething();
        System.out.println("方法後要執行的動作");
    }
}
複製程式碼
裝飾模式的優點
  • 裝飾類和被裝飾類可以獨立的發展,不會相互耦合。
  • 裝飾模式是繼承關係的一種替代方案,Decorator不管修飾多少層,返回的物件還是Component,實現的還是is-a的關係。
  • 裝飾模式可以動態的擴充套件一個實現類的功能。
裝飾模式的缺點
  • 多層的裝飾模式是比較複雜的。
裝飾模式的使用場景
  • 擴充套件一個類的功能,或給一個類增加附加的功能。
  • 動態的給一個物件增加功能,這些功能可以動態的撤銷。
  • 為一些類進行改裝或者增肌功能,首選裝飾模式。
3. 介面卡模式

定義:將一個類的介面程式設計客戶端所期待的另一個介面,從而使原本因介面不匹配而無法在一起工作的兩個類能夠在一起工作。

介面卡模式的一般模式:

public class MyClass {
    public static void main(String [] args) {
        Target target = new ConcreteTarget();
        target.doSomething();
        Target adapter = new Adapter();
        adapter.doSomething();
    }
}

interface Target {
    void doSomething();
}
class ConcreteTarget implements Target {

    @Override
    public void doSomething() {
        System.out.println("我是目標角色,現在要使用我的...");
    }
}
abstract class Adaptee {
    public void doSome () {
        System.out.println("我是源角色,從這裡轉變。。。");
    }
}
class Adapter extends Adaptee implements Target {

    @Override
    public void doSomething() {
        super.doSome();
    }
}
複製程式碼

介面卡模式在android中比較常見,比如ListView的介面卡。 例子:有兩種插座:兩孔的和三孔的。在一個賓館裡 只有兩孔的插座,所有我們要做一些操作,讓我們可以用三孔的插座

public class MyClass {
    public static void main(String [] args) {
        TSocketInterface socket = new ThreeSocket();
        Hotel hotel = new Hotel();
        SocketAdapter adapter = new SocketAdapter(socket);
        hotel.setSocket(adapter);
        hotel.charge();
    }
}

interface SocketInterface {
    void TwoRound();
}

class TwoSocket implements SocketInterface {

    @Override
    public void TwoRound() {
        System.out.println("使用兩孔插座");
    }
}
interface TSocketInterface {
    void ThreeRound();
}
class ThreeSocket implements TSocketInterface {

    @Override
    public void ThreeRound() {
        System.out.println("使用三孔插座");
    }
}
class SocketAdapter implements SocketInterface {
    private TSocketInterface socket;

    public SocketAdapter(TSocketInterface socket) {
        this.socket = socket;
    }

    @Override
    public void TwoRound() {
        this.socket.ThreeRound();
    }
}
class Hotel {
    private SocketInterface socket;

    public void setSocket(SocketInterface socket) {
        this.socket = socket;
    }
    public void charge () {
        socket.TwoRound();
    }
}
複製程式碼
介面卡模式的特點
  • 介面卡物件實現原有介面
  • 介面卡物件組合一個實現新介面的物件
  • 對介面卡原有介面方法的呼叫被委託給新介面的例項的特定方法。
4.外觀模式

定義: 要求一個子系統的外部與其內部的通訊必須通過一個統一的物件進行。

例子:進門分為幾個步驟:拿鑰匙、開門、進房間。而關門的話,也需要差不多的步驟,這時就需要一個外觀模式,通過一個合理的方式將這些動作包裝起來。

public class MyClass {
    public static void main(String [] args) {
        Facade facade = new Facade(new Key(), new Door(), new House());
        facade.in();
        facade.out();
    }
}

class Key {
    public void takekey () {
        System.out.println("拿鑰匙");
    }
}
class Door {
    public void openDoor () {
        System.out.println("開門");
    }
    public void closeDoor () {
        System.out.println("關門");
    }
}
class House {
    public void goIn () {
        System.out.println("進入房間");
    }
    public void goOut () {
        System.out.println("出房間");
    }
}
class Facade {
    private Key key;
    private Door door;
    private House house;

    public Facade(Key key, Door door, House house) {
        this.key = key;
        this.door = door;
        this.house = house;
    }
    public void in () {
        key.takekey();
        door.openDoor();
        house.goIn();
    }
    public void out () {
        key.takekey();
        house.goOut();
        door.closeDoor();
    }
}
複製程式碼
外觀模式的優點
  • 減少系統的相互依賴
  • 提高靈活性
  • 提高安全性
外觀模式的缺點
  • 不符合開閉原則
使用場景
  • 為一個複雜的模組後子系統提供一個供外界訪問的介面
  • 子系統相互獨立,外界對子系統的訪問只要黑箱操作就可以
  • 預防低水平人員帶來的風險擴散
5.組合模式(Composite Pattern)

定義:將物件組合成樹形結構表示“部分-整體”的層次結構。Composite模式使得使用者對單個物件和組合物件的使用具有一致性。

組成
  • 抽象構件(Component): 定義參與組合物件的共有方法和屬性,可以定義一些預設行為或屬性。
  • 樹葉構件(Leaf):組合中葉子物件,其下沒有其他分支,是最小的單位。
  • 樹枝構件(Composite):在組合中表示分直接點物件,有子節點,儲存子部件。

一般模式:

public class MyClass {
    public static void main(String [] args) {
        /**
         *           composite1
         *           /      \
         *        leaf1   composite2
         *                  /   \
         *               leaf2  leaf3
         *
         * */
        Component leaf1=new Leaf();
        Component leaf2=new Leaf();
        Component leaf3=new Leaf();
        Composite composite1=new Composite();
        Composite composite2=new Composite();

        composite2.add(leaf2);
        composite2.add(leaf3);
        composite1.add(leaf1);
        composite1.add(composite2);

        composite1.doSomething();
    }
}

interface Component {
    public void doSomething();
}

class Leaf implements Component {

    @Override
    public void doSomething() {
        System.out.println("Leaf doSomething");
    }
}
class Composite implements Component {
    List<Component> childs = new ArrayList<>();
    public void add (Component child) {
        this.childs.add(child);
    }
    public void remove(Component component) {
        this.childs.remove(component);
    }
    public Component getChild(int i) {
        return  this.childs.get(i);
    }
    @Override
    public void doSomething() {
        for (Component child : childs) {
            child.doSomething();
        }
    }
}
複製程式碼
實現方式
  • 透明式的組合模式:將管理子構件的方法定義在Component介面中,這樣Leaf類就需要處理這些對其意義不大的方法,在介面層次上Leaf和Composite沒有區別,這就是透明性。
  • 安全式的組合模式:將管理子構件的方法定義在Composite中,這樣編譯時任何從Leaf中增加或刪除物件的嘗試都將會被發現。
適用場景
  • 忽略組合物件和單個物件的不同,使用者將統一地使用組合結構中所有物件。
  • 想表示物件的部分-整體層次結構。
6.橋接模式

定義: 將抽象和實現解耦,使得兩者之間可以獨立的變化

橋接的用意是將抽象化和結構化解耦,使得二者可以獨立變化,就像JDBC橋DriverManage一樣,JDBC進行連線資料庫的時候,在各個資料庫之間進行切換,基本不用改動太多程式碼,因為JDBC提供統一介面,每個資料庫提供各自的實現,用一個叫做資料庫驅動的程式來橋接就行。

一般模式:

public class MyClass {
    public static void main(String [] args) {
        Sourceable sourceable = new ConcreteSource1();
        Bridge bridge = new MyBridge(sourceable);
        bridge.method();
    }
}

interface Sourceable {
    public void doSomething();
    public void doAnything();
}
class ConcreteSource1 implements Sourceable {
    @Override
    public void doSomething() {
        System.out.println("this is first source doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("this is first source doAnything");
    }
}
class ConcreteSource2 implements Sourceable {

    @Override
    public void doSomething() {
        System.out.println("this is second doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("this is second doAnything");
    }
}

abstract class Bridge {
    private Sourceable sourceable;

    public Bridge(Sourceable sourceable) {
        this.sourceable = sourceable;
    }

    public Sourceable getSourceable() {
        return sourceable;
    }
    public void method () {
        sourceable.doSomething();
    }
}
class MyBridge extends Bridge {

    public MyBridge(Sourceable sourceable) {
        super(sourceable);
    }

    @Override
    public void method() {
        super.getSourceable().doAnything();
    }
}
複製程式碼
橋接模式的優點:
  • 抽象和實現分離
  • 優秀的擴充能力
  • 實現細節對客戶透明
橋接模式的使用場景
  • 不希望或不適用使用繼承的場景
  • 介面或抽象不穩定的場景
  • 重用性要求比較高的場景
7.享元模式

定義:使用共享物件可有效的支援大量的細粒度的物件。

細粒度物件由於物件數量多且性質相近,我們將其分為兩個部分:內部狀態和外部狀態。

  • 內部狀態:內部狀態是物件可共享出來的資訊,儲存在享元物件內部並且不會隨環境而改變。如 id,adress等。
  • 外部狀態:物件得以依賴的一個標記,是隨環境改變而改變的、不可共享的狀態。

一般模式:

  • Flyweight: 抽象享元角色,定義出物件的外部狀態和北部狀態的介面或介面實現。
  • ConcreteFlyweight 具體的享元角色,實現抽象角色定義的的義務。
  • FlyweightFactory: 構造一個物件池,從池中提供物件。
public class MyClass {
    public static void main(String [] args) {
        FlyweightFactory.getFlyweight("Miss");
        FlyweightFactory.getFlyweight("Boss");
        FlyweightFactory.getFlyweight("Miss");
    }
}
abstract class Flyweight {
    //內部狀態
   private String intrinsic;
    // 外部狀態
    private String extrinsic;
    // 構造方法提供外部字串
    public Flyweight(String extrinsic) {
        this.extrinsic = extrinsic;
    }

    public void setIntrinsic(String intrinsic) {
        this.intrinsic = intrinsic;
    }

    public String getIntrinsic() {
        return intrinsic;
    }
    public abstract void operate ();
}

class ConcreteFlyweight extends Flyweight {
    public ConcreteFlyweight(String extrinsic) {
        super(extrinsic);
    }

    @Override
    public void operate() {
        System.out.println("this is first operate  ");
    }
}
class  ContreteFlyweight2 extends Flyweight {

    public ContreteFlyweight2(String extrinsic) {
        super(extrinsic);
    }

    @Override
    public void operate() {
        System.out.println("this is second operate");
    }
}
// 享元工廠
class FlyweightFactory {
    private static Map<String, Flyweight> list = new HashMap<>();
    public static Flyweight getFlyweight (String extrinsic) {
        Flyweight flyweight = null;
        if (list.containsKey(extrinsic)) {
            System.out.println("從物件池中取物件。。");
            flyweight = list.get(extrinsic);
        } else {
            flyweight = new ConcreteFlyweight(extrinsic);
            list.put(extrinsic, flyweight);
            System.out.println("建立新物件。。");
        }
        return flyweight;
    }
}
複製程式碼
享元模式優點:

減少應用建立的物件,降低使用記憶體,增強程式效能。

適用場景

系統中存在很多相似物件,細粒度的物件都具備較接近的外部狀態,而且內部狀態與環境無關。

3.行為模式

行為模式:用於在不同的實體之間進行通訊,為實體之間的通訊提供更容易,更靈活的通訊方法。

其中屬於行為模式的是:觀察者模式、策略模式、訪問者模式、中介者模式、狀態模式、備忘錄模式、責任鏈模式、模版方法模式、迭代子模式、命令模式、直譯器模式。

1.觀察者模式

定義:定義物件間一種一對多的依賴關係,使得每當一個物件發生變化,其他依賴它的物件都會得到通知並自動更新。

觀察者模式又叫釋出-訂閱模式,在Android中用的還是比較多的,推薦理解掌握。

一般模式
public class MyClass {
    public static void main(String [] args) {
        // 建立被觀察者
        Subject subject = new ConcreteSubject();
        // 新增觀察者
        subject.add(new ConcreteObserver("徐曉路"));
        subject.add(new ConcreteObserver("小埋"));
        // 更新資訊
        subject.notify("部落格系統更新了");

    }
}
// 觀察者介面
interface Observer {
    public void update(String message);
}
// 具體觀察者
class ConcreteObserver implements Observer {
    private String name = "";

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

    @Override
    public void update(String message) {
        System.out.println(this.name +"-"+ message);
    }
}

// 抽象被觀察者
interface Subject {
    /*
    * 增加觀察者
    * */
     void add(Observer ob);
    /*
    * 刪除觀察者
    * */
    void remove(Observer ob);
    /*
    * 更新資訊
    * */
    void notify(String message);
}
class ConcreteSubject implements Subject {
    private List<Observer> list = new ArrayList<>();
    @Override
    public void add(Observer ob) {
        list.add(ob);
    }

    @Override
    public void remove(Observer ob) {
        list.remove(ob);
    }

    @Override
    public void notify(String message) {
        for (Observer observer : list) {
            observer.update(message);
        }
    }
}
複製程式碼
  • Observer:抽象觀察者,提供一個介面,在得到通知時更新自己。
  • ConcreteObserver: 具體觀察者,實現抽象觀察者,在得到通知時更新自己的狀態
  • Subject: 抽象被觀察者,將所有的觀察者儲存在一個集合裡,並提供增加、刪除觀察者的介面
  • ConcreteSubject: 具體被觀察者,實現抽象被觀察者介面,得以在更新時通知所有觀察者。
觀察者模式的優點
  • 觀察者和被觀察者之間是抽象耦合,變換互不影響
  • 一套觸發機制
觀察者模式的缺點
  • 觀察者模式是,一個被觀察者,多個觀察者,一旦 一個觀察者卡頓,就會影響其他觀察者。
適用場景
  • 關聯行為場景,關聯行為是可查分的,而不是“組合”關係
  • 事件多出發場景
  • 跨系統的訊息交換,如 訊息佇列

觀察者模式在Android原始碼中使用還是蠻多的,比如OnClickListenerContentObserverandroid.database.Observable,第三方元件像RxJavaRxAndroidEventBus都用到了觀察者模式,這裡就不過多分析了。

2.策略模式

定義:定義一組演算法,把每一個演算法封裝起來,並且使它們之間可以相互切換。

一般模式
public class MyClass {
    public static void main(String [] args) {
        Context context = new Context(new ConcreteStrategy());
        context.method();
    }
}
interface Strategy {
    public void method();
}

class ConcreteStrategy implements Strategy {

    @Override
    public void method() {
        System.out.println("this is first method");
    }
}
class ConcreteStrategy2 implements Strategy {

    @Override
    public void method() {
        System.out.println("this is second method");
    }
}
class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void method () {
        this.strategy.method();
    }
}
複製程式碼
  • Strategy: 抽象策略角色,提供一個介面,定義所有策略要實現的方法和屬性
  • ConcreteStrategy:具體策略角色,實現Strategy,是想具體的方法
  • Context 封裝角色,遮蔽高層模組策略,演算法的直接訪問。
優點
  • 可以動態的改變物件的行為,擴充套件性良好。
缺點
  • 策略類數量多
  • 所有策略必須可知
適用場景
  • 演算法需要自由切換的場景
  • 需要遮蔽演算法規則的場景
  • 多個類只在行為或演算法上稍有不懂的場景
3.訪問者模式

定義:封裝某些作用於某種資料結構中各元素的操作,它可以在不改變資料結構的前提下定義作用於這些元素的新的操作。

一般模式
public class MyClass {
    public static void main(String [] args) {
        List<Element> list = ObjectStruture.createElement();
        for (Element element: list) {
            element.accept(new ConcreteVisitor());
        }
    }
}
// 抽象元素類
abstract class Element {
    public abstract void accept(Visitor visitor);
    public abstract void doSomething();
}
// 抽象訪問者
interface Visitor {
    public void visit(ConcreteElement1 element1);
    public void visit(ConcreteElement2 element2);
}
class ConcreteElement1 extends Element {

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

    @Override
    public void doSomething() {
        System.out.println("this is first element");
    }
}
class ConcreteElement2 extends Element {

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

    @Override
    public void doSomething() {
        System.out.println("this is second element");
    }
}
class ConcreteVisitor implements Visitor {

    @Override
    public void visit(ConcreteElement1 element1) {
        element1.doSomething();
    }

    @Override
    public void visit(ConcreteElement2 element2) {
        element2.doSomething();
    }
}
class ObjectStruture {
    public static List<Element> createElement () {
        List<Element> list = new ArrayList<>();
        Random random = new Random();
        for (int i = 0;i< 5; i++) {
            int j = random.nextInt(50);
            if (j > 25) {
                list.add(new ConcreteElement1());
            } else {
                list.add(new ConcreteElement2());
            }
        }
        return  list;
    }
}
複製程式碼
  • Element:抽象元素類,一般提供兩類方法,一種是自身的業務邏輯,另外就是允許接收哪類訪問者來訪問
  • Visitor:抽象訪問者,抽象類或者介面,宣告訪問哪些元素
  • ConcreteElement: 訪問者,實現抽象訪問者所宣告的方法
  • ConcreteVistor:元素類,是想抽象元素的所宣告的accpect方法。
優點
  • 符合單一職責原則
  • 擴充套件性良好,靈活度高
缺點
  • 具體元素變更比較困難
  • 違背了依賴倒置原則
適用場景
  • 一個物件中存在著一些與本物件不相干的操作,為了避免這些操作汙染這個物件。可以使用訪問者模式
  • 一組物件中,尋在這相似的操作,為了避免大量重複程式碼,可以使用訪問者模式
4.中介者模式

定義:用一箇中介物件封裝一系列的物件互動,中介者使各物件不需要顯示地相互互動,從而使期耦合鬆散,並且可以獨立地改變它們之間的互動。

中介者模式其實就是將網狀的使用者關係模型改編成星形的使用者關係模型。

一般模式
public class MyClass {
    public static void main(String [] args) {
        ConcreteColleagueA coA = new ConcreteColleagueA();
        ConcreteColleagueB coB = new ConcreteColleagueB();
                Mediator mediator = new ConcreteMediator(coA, coB);
        coA.setNumber(1000, mediator);
        System.out.println("----coA--" + coA.getNumber());
        System.out.println("----coB--"+ coB.getNumber());
        coB.setNumber(1000, mediator);
        System.out.println("----coA--" + coA.getNumber());
        System.out.println("----coB--"+ coB.getNumber());
    }
}
abstract class Colleague {
    protected int number = 0;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
        abstract public void setNumber(int number, Mediator mediator);
}
class ConcreteColleagueA extends Colleague {

    @Override
    public void setNumber(int number, Mediator mediator) {
        this.number = number;
        mediator.AaffectB();
    }
}
class ConcreteColleagueB extends Colleague {

    @Override
    public void setNumber(int number, Mediator mediator) {
        this.number = number;
        mediator.BaffectA();
    }
}
abstract class Mediator {
    protected ConcreteColleagueA coA;
    protected ConcreteColleagueB coB;

    public Mediator(ConcreteColleagueA coA, ConcreteColleagueB coB) {
        this.coA = coA;
        this.coB = coB;
    }
    public abstract void AaffectB();
    public abstract void BaffectA();
}

class ConcreteMediator extends Mediator {

    public ConcreteMediator(ConcreteColleagueA coA, ConcreteColleagueB coB) {
        super(coA, coB);
    }

    @Override
    public void AaffectB() {
        int number = coA.getNumber();
        coB.setNumber(number * 100);
    }

    @Override
    public void BaffectA() {
        int number = coB.getNumber();
        coA.setNumber(number * 2);
    }
}
複製程式碼
  • Colleague: 同事類,提供一個方法,使得其屬性的變化和中介者相關聯
  • Mediator: 抽象中介者,定義統一的介面,用於各同事直接通訊
  • ConcreteMediator: 通過協調各同事實現協同行為,因此必須依賴各個同事角色。
  • Colleague: 同事類,提供一個方法,使得其屬性的變化和中介者相關聯
  • Mediator: 抽象中介者,定義統一的介面,用於各同事直接通訊
  • ConcreteMediator: 通過協調各同事實現協同行為,因此必須依賴各個同事角色。
5.狀態模式

定義:當一個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類。

一般模式
public class MyClass {
    public static void main(String [] args) {
        Context context = new Context();
        context.setState(new ConcreteState1());
        context.method2();
    }
}
abstract class State {
    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    public abstract void method1();
    public abstract void method2();
}
class Context {
    private State currState;

    protected static final ConcreteState1 state1 = new ConcreteState1();
    protected static final ConcreteState2 state2 = new ConcreteState2();
    public void setState(State state) {
        this.currState = state;
        this.currState.setContext(this);
    }
    public void method1() {
        this.currState.method1();
    }
    public void method2() {
        this.currState.method2();
    }
}

class ConcreteState1 extends State {

    @Override
    public void method1() {
        System.out.println("this is first method");
    }

    @Override
    public void method2() {
        super.context.setState(Context.state2);
        super.context.method2();
    }
}

class ConcreteState2 extends State {

    @Override
    public void method1() {
        super.context.setState(Context.state1);
        super.context.method1();
    }

    @Override
    public void method2() {
        System.out.println("this is second method");
    }
}
複製程式碼
  • State: 抽象狀態類,負責物件狀態定義,並且封裝環境角色以實現狀態切換。
  • ConcreteState:具體狀態角色,完成本狀態下要做的事情以及如何過渡到其他狀態
  • Context:環境角色,負責具體的狀態切換
優點
  • 結構清晰,封裝性好
缺點
  • 環境類中角色自類太多,不好管理
適用場景
  • 行為隨狀態改變而改變的場景
  • 條件、分支判斷語句的替代者
6.備忘錄模式

定義:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態。

一般模式
public class MyClass {
    public static void main(String [] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();
        originator.setState(" 你好嗎");
        caretaker.setMemento(originator.createMemento());
        // 改變狀態
        originator.setState("你還好嗎");
        // 回覆原來狀態
        originator.restoreMementor(caretaker.getMemento());
        System.out.println(originator.getState());
    }
}
/*
* 備忘錄角色
* 備份、儲存原有資料
* */
class Memento {
    private String state = "";

    public Memento(String state) {
        this.state = state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}
/*
*  備忘錄發起類
*  備忘錄中儲存的就是該類的內容
* */
class Originator {
    private String state = "";

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
    public Memento createMemento() {
        return new Memento(this.state);
    }
    public void  restoreMementor(Memento memento) {
        this.setState(memento.getState());
    }
}
/*
*  備忘錄管理類
*  對備忘錄進行管理,儲存
* */
class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}
複製程式碼
多狀態多備份備忘錄

通常情況下,Originator類一般是一個JavaBean,而需要儲存的狀態不止一個,需要備份數量也不止一個,這就是所謂的多狀態多備份備忘錄,就像下面程式碼:

public class MyClass {
   public static void main(String [] args) {
       Originator originator = new Originator();
       Caretaker caretaker = new Caretaker();
       originator.setState1("我是誰");
       originator.setState2("我在哪");
       originator.setState3("我在幹嘛");
       caretaker.setMemento("01", originator.createMemento());
       originator.setState1("hahhaha");
       originator.setState2(" 我在家");
       originator.setState3("ouh;");
       originator.restoreMementor(caretaker.getMemento("01"));
       System.out.println("------恢復之後的狀態--" + originator);
   }
}
/*
* 備忘錄角色
* 備份、儲存原有資料
* */
class Memento {
   private Map<String, Object> stateMap;

   public Memento(Map<String, Object> stateMap) {
       this.stateMap = stateMap;
   }

   public Map<String, Object> getStateMap() {
       return stateMap;
   }

   public void setStateMap(Map<String, Object> stateMap) {
       this.stateMap = stateMap;
   }
}
class BeanUtils {
   public static Map<String, Object> backupProp(Object bean) {
       Map<String, Object> result = new HashMap<>();
       try {
           BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
           //返回PropertyDescriptor型別的javaBean描述
           PropertyDescriptor[] descriptors = (PropertyDescriptor[]) beanInfo.getPropertyDescriptors();
           for (PropertyDescriptor des : descriptors) {
               String fieldName = des.getName();
               // 讀取屬性的方法
               Method method = des.getReadMethod();
               // 讀取屬性值
               Object fieldValue = method.invoke(bean, new Object[]{});
               if (!fieldName.equalsIgnoreCase("class")) {
                   result.put(fieldName, fieldValue);
               }
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
       return result;
   }

   public static void restoreProp(Object bean, Map<String, Object> propMap) {
       try {
           BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
           //獲取PropertyDescriptor的物件陣列
           PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
           //增強for迴圈,遍歷所有的屬性,設定到bean中
           for(PropertyDescriptor des: descriptors){
               //獲取key值物件
               String fieldName = des.getName();
               if(propMap.containsKey(fieldName)){
                   Method setter = des.getWriteMethod();
                   setter.invoke(bean, new Object[]{propMap.get(fieldName)});
               }
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}
/*
*  備忘錄發起類
*  備忘錄中儲存的就是該類的內容
* */
class Originator {
   private String state1 = "";
   private String state2 = "";
   private String state3 = "";

   public String getState1() {
       return state1;
   }

   public String getState2() {
       return state2;
   }

   public String getState3() {
       return state3;
   }

   public void setState1(String state1) {
       this.state1 = state1;
   }

   public void setState2(String state2) {
       this.state2 = state2;
   }

   public void setState3(String state3) {
       this.state3 = state3;
   }

   public Memento createMemento() {
       return new Memento(BeanUtils.backupProp(this));
   }
   public void  restoreMementor(Memento memento) {
       BeanUtils.restoreProp(this, memento.getStateMap());
   }

   @Override
   public String toString() {
       return "Originator{" +
               "state1='" + state1 + '\'' +
               ", state2='" + state2 + '\'' +
               ", state3='" + state3 + '\'' +
               '}';
   }
}
/*
*  備忘錄管理類
*  對備忘錄進行管理,儲存
* */
class Caretaker {
   private Map<String, Memento>  map = new HashMap<String, Memento>();
   public Memento getMemento(String index){
       return map.get(index);
   }

   public void setMemento(String index, Memento memento){
       this.map.put(index, memento);
   }
}
複製程式碼
優點
  • 可以回滾操作
  • 發起人不用分別對每個備份狀態進行管理
缺點
  • 對資源的消耗比較大
適用場景
  • 在需要回滾操作的情況下
7.責任鏈模式

定義:使多個物件都有機會處理請求,從而避免了傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有物件處理該請求為止。

舉個例子:比如我們去部門去辦理某項手續,你去的地方不能辦理,他會轉交給下個部門,如果下個部門也不能辦理的話,還會轉交給其他部門,直到給能辦理的部門。

一般模式
public class MyClass {
    public static void main(String [] args) {
        Handler handler1 = new ConcreteHandler();
        Handler handler2 = new ConcreteHandler();
        handler1.setSuccessor(handler2);
        handler1.handleRequest();
    }
}

abstract class Handler {
    protected Handler successor;

    public Handler getSuccessor() {
        return successor;
    }

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public abstract void handleRequest();
}

class ConcreteHandler extends Handler {

    @Override
    public void handleRequest() {
        if (getSuccessor() != null) {
            getSuccessor().handleRequest();
        } else {
            System.out.println("我來處理");
        }
    }
}
複製程式碼
  • 抽象處理者角色(Handler):定義出一個處理請求的介面。如果需要,介面可以定義 出一個方法以設定和返回對下家的引用。這個角色通常由一個Java抽象類或者Java介面實現。
  • 具體處理者角色(ConcreteHandler):具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
優點
  • 靈活度高,兩者解耦
缺點
  • 除錯不方便
8.模版方法模式

定義:定義一個操作中的演算法框架,而將一些步驟延遲到子類中,子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。

舉個例子:比如我們去銀行取辦理業務,一般的流程是取號,然後辦理業務,然後給辦理員做出評價。取號和評價都是基本要做的,但是其中的辦理業務就會出現各種不同。

public class MyClass {
    public static void main(String [] args) {
        AbstractBank b1 = new ConcreteDeposit();
        b1.process();
        AbstractBank b2 = new ConcreteTrancfer();
        b2.process();
    }
}
abstract class AbstractBank {
    public void takeNumber() {
        System.out.println("取號");
    }
    public void evaluateHook () {
        System.out.println("評價操作員");
    }
    public abstract void transact();
    public void process (){
        this.takeNumber();
        this.transact();
        this.evaluateHook();
    }
}
class ConcreteDeposit extends AbstractBank {

    @Override
    public void transact() {
        System.out.println("存款");
    }
}
class ConcreteTrancfer extends AbstractBank {

    @Override
    public void transact() {
        System.out.println("轉賬");
    }
}
複製程式碼
優點
  • 形式定義演算法,具體細節由子類實現
  • 程式碼複用
適用場景
  • 多個子類有共有的方法,並且邏輯基本相同
  • 重複複雜的演算法,可以把核心演算法設計為模版方法,具體細節由子類去實現
  • 控制子類擴充套件
9.迭代器模式

定義:提供一種方法訪問一個容器物件中各個元素,而不是暴露該物件的內容細節。

一般模式
public class MyClass {
    public static void main(String [] args) {
        Aggregate aggregate = new ConcreteAggregate();
        aggregate.add("王二");
        aggregate.add("李五");
        aggregate.add("趙四");
        Iterator iterator = aggregate.iterator();
        while (iterator.hasNext()) {
            String object = (String) iterator.next();
            System.out.println(object);
        }
    }
}

interface Iterator {
    public Object next();
    public boolean hasNext();
}

class ConcreteIterator implements Iterator {
    private List list = new ArrayList();
    private int cursor = 0;
    public ConcreteIterator(List list) {
        this.list = list;
    }

    @Override
    public Object next() {
        Object object = null;
        if (hasNext()) {
            object = list.get(cursor ++);
        }
        return object;
    }

    @Override
    public boolean hasNext() {
        if (list.size() == cursor) {
            return false;
        }
        return true;
    }
}

interface Aggregate {
    public void add(Object object);
    public void remove(Object object);
    public Iterator iterator();
}

class ConcreteAggregate implements Aggregate {
    private List list = new ArrayList();

    @Override
    public void add(Object object) {
        list.add(object);
    }

    @Override
    public void remove(Object object) {
        list.remove(object);
    }

    @Override
    public Iterator iterator() {
        return new ConcreteIterator(list);
    }
}
複製程式碼
  • Iterator: 抽象迭代器,定義遍歷元素所需要的方法,
  • ConcreteIterator:迭代器的實現類,實現定義的方法,完成集合的迭代。
  • Aggregate:抽象容器,提供iterator()方法。
  • ConcreteAggregate: 抽象容器實現類。
優點
  • 簡化遍歷方式
  • 封裝性良好
缺點
  • 對於簡單的遍歷,可能使用起來會比較繁瑣。
10.命令模式

定義: 將一個請求封裝成一個物件,從而讓你使用不同的請求把客戶端引數化,對請求排隊或記錄請求日誌,可以提供命令的撤銷和恢復功能。

一般模式
public class MyClass {
    public static void main(String [] args) {
        Invoker invoker = new Invoker(new ConcreteCommand(new ConcreteReceiver()));
        invoker.action();
    }
}
/*
*   接收者,真正執行命令的物件
* */
abstract class Receiver {
    public abstract void method();
}
class ConcreteReceiver extends Receiver {

    @Override
    public void method() {
        System.out.println("this is a method");
    }
}
/*
*  定義命令的介面。宣告執行的方法
* */
interface Command {
    public void execute();
}
class ConcreteCommand implements Command {
    private Receiver receiver ;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        this.receiver.method();
    }
}
/*
*  要求命令執行請求,持有命令物件
* */
class Invoker {
    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }
    public void action() {
        this.command.execute();
    }
}
複製程式碼

命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的職責分隔開。

11.直譯器模式

定義:給定一個語言,定義它的文法的一種表示,並表示一個直譯器,這個直譯器使用該表示來解釋語言中的句子。

直譯器模式,在Android中用到的不多,但是也可以簡單瞭解一下。 舉一個加減乘除的例子

public class MyClass {
    public static void main(String [] args) {
        Expression ex ;

        Context con ;
        con = new Context();

        //設定變數、常量

        Variable a = new Variable();

        Variable b = new Variable();

        Constant c = new Constant(4);

//為變數賦值

        con.addValue(a , 8);

        con.addValue(b , 9);

//運算,對句子的結構由我們自己來分析,構造

        ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c));

        System.out.println("運算結果為:"+ex.interpret(con));
    }
}

class Context

{

    private Map valueMap = new HashMap();

    public void addValue(Variable x , int y)

    {

        Integer yi = new Integer(y);

        valueMap.put(x , yi);

    }

    public int LookupValue(Variable x)

    {

        int i = ((Integer)valueMap.get(x)).intValue();

        return i ;

    }

}

abstract class Expression {
    public abstract int interpret(Context con);
}
//終結符表示式角色

class Constant extends Expression

{

    private int i ;

    public Constant(int i)

    {
        this.i = i;
    }

    public int interpret(Context con)

    {
        return i ;
    }

}
class Variable extends Expression

{

    public int interpret(Context con)

    {
        //this為呼叫interpret方法的Variable物件

        return con.LookupValue(this);

    }

}
//非終結符表示式角色

class Add extends Expression

{

    private Expression left ,right ;

    public Add(Expression left , Expression right)

    {

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con)

    {

        return left.interpret(con) + right.interpret(con);

    }

}
class Subtract extends Expression

{

    private Expression left , right ;

    public Subtract(Expression left , Expression right)

    {

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con)

    {

        return left.interpret(con) - right.interpret(con);

    }

}
class Multiply extends Expression

{

    private Expression left , right ;

    public Multiply(Expression left , Expression right)

    {

        this.left = left ;

        this.right= right ;

    }

    public int interpret(Context con)

    {

        return left.interpret(con) * right.interpret(con);

    }

}
class Division extends Expression

{

    private Expression left , right ;

    public Division(Expression left , Expression right)

    {
        this.left = left ;
        this.right= right ;
    }

    public int interpret(Context con)

    {
        try{
            return left.interpret(con) / right.interpret(con);

        }catch(ArithmeticException ae)

        {
            System.out.println("被除數為0!");
            return -11111;
        }

    }

}
複製程式碼
  • 抽象表示式(Expression)角色:宣告一個所有的具體表示式角色都需要實現的抽象介面。這個介面主要是一個interpret()方法,稱做解釋操作。

  • 終結符表示式(Terminal Expression)角色:實現了抽象表示式角色所要求的介面,主要是一個interpret()方法;文法中的每一個終結符都有一個具體終結表示式與之相對應。比如有一個簡單的公式X=Y+Z,在裡面R1和R2就是終結符,對應的解析Y和Z的直譯器就是終結符表示式。

  • 非終結符表示式(Nonterminal Expression)角色:文法中的每一條規則都需要一個具體的非終結符表示式,非終結符表示式一般是文法中的運算子或者其他關鍵字,比如公式X=Y-Z中,“-"就是非終結符,解析“+”的直譯器就是一個非終結符表示式。

  • 環境(Context)角色:這個角色的任務一般是用來存放文法中各個終結符所對應的具體值,比如X=Y+Z,我們給Y賦值2,給Y賦值3。這些資訊需要存放到環境角色中,很多情況下我們使用Map來充當環境角色就足夠了

二、總結

設計模式是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。在Android的原始碼中也會經常會出現設計模式的影子,在這裡我把所有的設計模式都簡單的一一介紹了,並沒有深入的去探究,有需要或者有興趣的可以自己去深入研究一下,者對我們來說也是有益無害的。

參考資料

設計模式(五)觀察者模式

《Android原始碼設計模式》

《大話設計模式》

推薦閱讀

如何正確地使用設計模式

相關文章