Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [Android基礎系列]設計模式(一)

[Android基礎系列]設計模式(一)

編輯:關於Android編程

前言

這篇文章可以說是java基礎的范疇,為了下一篇Android開發中的常用設計模式做一下鋪墊,也順便反思一下。

正文

設計模式分類

分類方式是多樣的,這裡我們按照功能類型進行分類:

創建型

工廠方法 Factory Method 抽象工廠 Abstract Factory 建造者 Builder 原型 Prototype 單例 Singleton

結構型

適配器 Adapter Class/Object 橋接 Bridge 組合 Composite 裝飾 Decorator 外觀 Facade 享元 Flyweight 代理 Proxy

行為型

解釋器 Interpreter 模板方法 Template Method 責任鏈 Chain of Responsibility 命令 Command 迭代器 Iterator 中介者 Mediator 備忘錄 Memento 觀察者 Observer 狀態 State 策略 Strategy 訪問者 Visitor

一共23種設計模式。

設計模式概述

創建型

創建型的五種設計模式,其目的都是為了創建對象(生成實例)

工廠方法

簡單工廠模式就不談了,工廠方法模式的UML圖:
工廠方法

工廠必然是用來流水化生產的,“風格迥異”的產品不適合使用工廠模式<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KICAKICA8cD65pLOnt723qMSjyr3T67zytaW5pLOnxKPKvbK7zay1xMrHOr2r1K3T0LXEuaSzp8Dgo6y7rrfWzqogs+nP87mks6cgus0gvt/M5bmks6cgoaPG5NbQs+nP87mks6fM4bmpwcvSu7j2vdO/2qOsvt/M5bmks6e4utTwyrXP1rbU06a1xL7fzOW1xLL6xrehozwvcD4KPC9ibG9ja3F1b3RlPgoKPHA+ZGVtb6O6IDxicj4Ks+nP87mks6e907/aPC9wPgoKCgo8cHJlIGNsYXNzPQ=="brush:java;">package factroy_method; /** * @ClassName: ProductFactoryInterface * @Description: 抽象工廠接口 * @date 2016年5月18日 下午2:03:04 * * @author leobert.lan * @version 1.0 */ public interface ProductFactoryInterface { public Product createProduct(); }

實際工廠(這裡我舉例用兩個)

package factroy_method;

/**
 * @ClassName: AProductFactroy
 * @Description: A產品具體工廠
 * @date 2016年5月18日 下午2:06:28
 * 
 * @author leobert.lan
 * @version 1.0
 */
public class AProductFactory implements ProductFactoryInterface {

    @Override
    public Product createProduct() {
        return new AProduct();
    }
}


package factroy_method;
/** 
 * @ClassName: BProductFactory 
 * @Description: B產品具體工廠
 * @date 2016年5月18日 下午2:13:59
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class BProductFactory implements ProductFactoryInterface{

    @Override
    public Product createProduct() {
        return new BProduct();
    }

}

“風格類似”的產品的API

package factroy_method;

/**
 * @ClassName: Product
 * @Description: “產品”的API
 * @date 2016年5月18日 下午2:04:36
 * 
 * @author leobert.lan
 * @version 1.0
 */
public interface Product {
    void use();
}

實際產品類

package factroy_method;
/** 
 * @ClassName: AProduct 
 * @Description: 具體產品A
 * @date 2016年5月18日 下午2:10:02
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class AProduct implements Product{

    @Override
    public void use() {
        System.out.println("use A");
    }

}


package factroy_method;
/** 
 * @ClassName: BProduct 
 * @Description: 具體產品“B”
 * @date 2016年5月18日 下午2:12:36
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class BProduct implements Product{

    @Override
    public void use() {
        System.out.println("use B");
    }

}

測試一下:

package factroy_method;
/** 
 * @ClassName: Test 
 * @Description: TODO
 * @date 2016年5月18日 下午2:15:19
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class Test {

    public static void main(String[] args) {
        ProductFactoryInterface productFactoryInterface1 = new AProductFactory();
        productFactoryInterface1.createProduct().use();

        ProductFactoryInterface productFactoryInterface2 = new BProductFactory();
        productFactoryInterface2.createProduct().use();
    }

}

輸出結果自然是:

use A
use B

我們可以體會到,工廠模式是為了“風格相同”的東西而產生的,我們去使用必然是因為有“風格相同”的東西,每種東西應該是對應了不同的業務場景,可以大膽推測,這些業務也較為類似,潛台詞就是:業務類可以進行抽象。
父類只需要拿到抽象工廠接口實例(具體業務子類提供),就可以完成共有的邏輯,不必關心也不應當關心使用的是哪種實例。

有這樣一句話:

Factory Method 使一個類的實例化延遲到其子類。

適用性:

當一個類不知道它所必須創建的對象的類的時候。 當一個類希望由它的子類來指定它所創建的對象的時候。 當類將創建對象的職責委托給多個幫助子類中的某一個,並且你希望將哪一個幫助子類是代理者這一信息局部化的時候。

抽象工廠

抽象工廠

抽象工廠模式是一種比工廠模式抽象程度更高的模式。在工廠方法模式中,一個具體的工廠類負責創建一個單獨的、具體的產品。有多少種產品,就需要多少個對應的工廠類,即使這些產品之間有某些必要聯系。

我們可以這樣理解:抽象工廠是對工廠方法的進一步抽象,所以他的層面會高一層。通俗但是不是很准確的說就是:工廠模式是干某件事情的,抽象工廠就是干這一類事情的。

上面我們體會到工廠方法模式用來生產“風格類似”的產品,用一個更常見的詞:一類產品簇(也有叫產品族的,我習慣用簇這個詞),那麼抽象工廠就是指導生產多類產品簇的。

這樣理解產品簇:碳酸飲料是一個簇,可以有可口可樂、雪碧等等。

而且我們不可否認,同一個產品也有不同屬性,例如可口可樂有不同的容量、不同的產地、不同的生產日期(用工廠方法模式也能去做這件事情,上文沒有提及,我們可以在工廠接口中定義生產方法的時候約定形參。之所以在這裡補充,是有一些文章將這一點作為工廠方法和抽象工廠的不同,這不是很恰當,但抽象工廠確實可以給產品加上每個工廠自己特有的信息)

demo(有些地方偷了點懶)

抽象工廠接口

package abstract_factory;
/** 
 * @ClassName: AbstractFactory 
 * @Description: TODO
 * @date 2016年5月18日 下午2:47:19
 *  
 * @author leobert.lan
 * @version 1.0
 */
public interface AbstractFactory {

    public IProductClusterOne createClusterOne();

    public IProductClusterTwo createClusterTwo();

}

實際的工廠

package abstract_factory;
/** 
 * @ClassName: AmericanFactory 
 * @Description: 美國的具體工廠
 * @date 2016年5月18日 下午3:02:32
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class AmericanFactory implements AbstractFactory{

    @Override
    public IProductClusterOne createClusterOne() {
        return new AProductOfClusterOne(getClass().getSimpleName());
    }

    @Override
    public IProductClusterTwo createClusterTwo() {
        return new AProductOfClusterTwo(getClass().getSimpleName());
    }

}


package abstract_factory;
/** 
 * @ClassName: ChinaFactory 
 * @Description: TODO
 * @date 2016年5月18日 下午3:02:06
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class ChinaFactory implements AbstractFactory{

    @Override
    public IProductClusterOne createClusterOne() {
        //同一個產品可以有不同的屬性,我直接打上工廠特有的信息,但這不是抽象工廠提出的主要目的,可以這樣寫而已,在某些情況下確實方便。使用抽象工廠,這裡應該生成BProduct而非AProduct
        return new AProductOfClusterOne(getClass().getSimpleName());
    }

    @Override
    public IProductClusterTwo createClusterTwo() {
        //實際目的就是生產產品簇中的另一個產品
        return new BProductOfClusterTwo(getClass().getSimpleName());
    }

}

產品簇API

package abstract_factory;

/**
 * @ClassName: IProductClusterOne
 * @Description: TODO
 * @date 2016年5月18日 下午2:59:33
 * 
 * @author leobert.lan
 * @version 1.0
 */
public interface IProductClusterOne {
    void readInfo();

    /**
     * @Title: getYieldly
     * @Description: 獲取生產地信息
     * @author: leobert.lan
     * @return
     */
    String getYieldly();

}


package abstract_factory;
/** 
 * @ClassName: IProductClusterTwo 
 * @Description: 第二類產品簇的API,當然,我只是偷懶和第一類寫的一樣
 * @date 2016年5月18日 下午2:59:40
 *  
 * @author leobert.lan
 * @version 1.0
 */
public interface IProductClusterTwo {

    void readInfo();

    String getYieldly();

}

實際產品

package abstract_factory;
/** 
 * @ClassName: AProductOfClusterOne
 * @Description: TODO
 * @date 2016年5月18日 下午3:13:41
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class AProductOfClusterOne implements IProductClusterOne{

    private final String yieldly;

    public AProductOfClusterOne(String yieldly) {
        this.yieldly = yieldly;
    }

    @Override
    public void readInfo() {
        System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
    }

    @Override
    public String getYieldly() {
        return yieldly;
    }

}


package abstract_factory;
/** 
 * @ClassName: AProductOfClusterOne
 * @Description: TODO
 * @date 2016年5月18日 下午3:13:41
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class AProductOfClusterTwo implements IProductClusterTwo{

    private final String yieldly;

    public AProductOfClusterTwo(String yieldly) {
        this.yieldly = yieldly;
    }

    @Override
    public void readInfo() {
        System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
    }

    @Override
    public String getYieldly() {
        return yieldly;
    }

}


package abstract_factory;
/** 
 * @ClassName: BProductOfClusterOne 
 * @Description: TODO
 * @date 2016年5月18日 下午3:15:38
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class BProductOfClusterOne implements IProductClusterOne{

    private final String yieldly;

    public BProductOfClusterOne(String yieldly) {
        this.yieldly = yieldly;
    }

    @Override
    public void readInfo() {
        System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
    }

    @Override
    public String getYieldly() {
        return yieldly;
    }

}

package abstract_factory;
/** 
 * @ClassName: BProductOfClusterOne 
 * @Description: TODO
 * @date 2016年5月18日 下午3:15:38
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class BProductOfClusterTwo implements IProductClusterTwo{

    private final String yieldly;

    public BProductOfClusterTwo(String yieldly) {
        this.yieldly = yieldly;
    }

    @Override
    public void readInfo() {
        System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
    }

    @Override
    public String getYieldly() {
        return yieldly;
    }

}

測試一下

package abstract_factory;
/** 
 * @ClassName: Test 
 * @Description: TODO
 * @date 2016年5月18日 下午3:27:29
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class Test {

    public static void main(String[] args) {
        AbstractFactory factory1 = new AmericanFactory();
        AbstractFactory factory2 = new ChinaFactory();

        factory1.createClusterOne().readInfo();
        factory2.createClusterOne().readInfo();

        factory1.createClusterTwo().readInfo();
        factory2.createClusterTwo().readInfo();


    }

}

結果:(抱歉沒處理好空格。。。)

i amAProductOfClusterOnemade in AmericanFactory
i amAProductOfClusterOnemade in ChinaFactory
i amAProductOfClusterTwomade in AmericanFactory
i amBProductOfClusterTwomade in ChinaFactory

-

意圖
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
適用性
一個系統要獨立於它的產品的創建、組合和表示時。
一個系統要由多個產品系列中的一個來配置時。

對比一下兩者:

工廠方法 :用來生產同一等級結構中的固定產品。(支持增加任意產品,一個工廠對應一個產品,加產品就加工廠)
抽象工廠 :用來生產不同產品簇的全部產品。(對於增加新的產品,無能為力[這裡指在某個具體的工廠中生產一個產品簇中的多個產品];支持增加產品簇)

建造者

建造者
對於復雜的創建,我們可以使用這個
可以想象一下,有一個類有很多個屬性,嗯,看到構造器真是屎一樣的代碼,你無法避免這種情況:這麼多屬性都是final修飾的!好吧,客觀的說你是可以使用大量的重載構造器方法以填寫默認值,你也可以不使用final修飾在構造後不停的set,但是無法否認你寫的時候心裡都難受。

對於建造者模式的實現,略有差異,但我更喜歡在預設建造過程的時候返回建造者對象

demo
建造者接口

package builder;

/**
 * @ClassName: IHeHeBuilder
 * @Description: TODO
 * @date 2016年5月18日 下午4:24:55
 * 
 * @author leobert.lan
 * @version 1.0
 */
public interface IHeHeBuilder {
    IHeHeBuilder setAttrA(String attra);

    IHeHeBuilder setAttrB(String attrb);

    IHeHeBuilder setAttrC(String attrc);

    IHeHeBuilder setAttrD(String attrd);

    //....

    HeHe build(); //build or create

}

想要建造的類

package builder;

/**
 * @ClassName: HeHe
 * @Description: TODO
 * @date 2016年5月18日 下午4:24:11
 * 
 * @author leobert.lan
 * @version 1.0
 */
public class HeHe {

    private final String attrA;

    private final String attrB;

    private final String attrC;

    private final String attrD;

    public HeHe(String attrA, String attrB, String attrC, String attrD) {
        super();
        this.attrA = attrA;
        this.attrB = attrB;
        this.attrC = attrC;
        this.attrD = attrD;
    }

    public void readInfo() {
        String s = "a:" + attrA + ",b:" + attrB + ",c" + attrC + ",d:" + attrD;
        System.out.println(s);
    }

    public static class Builder implements IHeHeBuilder {
        private String attrA = "defaulta";

        private String attrB = "defaultb";

        private String attrC = "defaultc";

        private String attrD = "defaultd";

        public Builder() {
            super();
        }
        // 省略N種情況,強迫症患者最好只寫上面那個,也就是一個都不寫

        public Builder(String attrA, String attrB, String attrC, String attrD) {
            super();
            this.attrA = attrA;
            this.attrB = attrB;
            this.attrC = attrC;
            this.attrD = attrD;
        }

        public IHeHeBuilder setAttrA(String attrA) {
            this.attrA = attrA;
            return this;
        }

        public IHeHeBuilder setAttrB(String attrB) {
            this.attrB = attrB;
            return this;
        }

        public IHeHeBuilder setAttrC(String attrC) {
            this.attrC = attrC;
            return this;
        }

        public IHeHeBuilder setAttrD(String attrD) {
            this.attrD = attrD;
            return this;
        }

        @Override
        public HeHe build() {
            return new HeHe(attrA, attrB, attrC, attrD);
        }

    }
}

測試一下

package builder;

/** 
 * @ClassName: Test 
 * @Description: TODO
 * @date 2016年5月18日 下午4:36:36
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class Test {

    public static void main(String[] args) {
        IHeHeBuilder builder = new HeHe.Builder();
        builder.setAttrA("this is test attr a").setAttrC("hehe").build().readInfo();

    }

}

寫起來還是挺舒服的

來一段高大上的

意圖
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
適用性
當創建復雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
當構造過程必須允許被構造的對象有不同的表示時。

原型

原型

原型模式有兩種表現形式(本質還是一致的):簡單形式,登記形式,上面的圖是簡單形式的。

值得一提的是,這是一種“復制”的工作方式。復制有淺復制和深復制之分。我們說對象的字段可以分為值類型和引用類型的。淺復制和深復制對值類型的都是拷貝值,是相互獨立的,而淺復制的引用類型字段是指向了同一個內存地址,一個變了就都變了,但深復制還是將依賴對象的“內容”進行復制,相互獨立。

我們知道java中的Object類中定義了clone方法,Cloneable接口也是一個空的接口。我們拋開這些東西,重新實現一下。

我們主要看一下深淺復制的差別,就不建造多個原型類來展示它的真正意義
demo

package prototype;
/** 
 * @ClassName: Prototype 
 * @Description: TODO
 * @date 2016年5月19日 上午8:47:38
 *  
 * @author leobert.lan
 * @version 1.0
 */
public interface Prototype {

    public Object shallowClone();

    public Object deepClone();

}

原型類(真正使用的時候會建立多個原型類來當作“模板”,參數基本都是固定的,制式的風格)

package prototype;

/**
 * @ClassName: APrototype
 * @Description: TODO
 * @date 2016年5月19日 上午10:45:19
 * 
 * @author leobert.lan
 * @version 1.0
 */
public class APrototype implements Prototype {

    private String s;

    // 一個default 的引用
    private Reliant reliant = new Reliant(0);

    public APrototype(String s) {
        this.s = s;
    }

    public Reliant getReliant() {
        return reliant;
    }

    public void setReliant(Reliant reliant) {
        this.reliant = reliant;
    }

    public void readInfo() {
        System.out.println(s+",reliant info:"+reliant.getI());
    }

    public String getS() {
        return s;
    }

    public void setS(String s) {
        this.s = s;
    }

    @Override
    public Object shallowClone() {
        APrototype ret = new APrototype(s);
        ret.setReliant(reliant);

        return ret;
    }

    @Override
    public Object deepClone() {
        APrototype ret = new APrototype(s);

        ret.setReliant(new Reliant(reliant.getI()));

        return ret;
    }

}

測試一下深淺復制的區別

package prototype;

/**
 * @ClassName: Test
 * @Description: TODO
 * @date 2016年5月19日 上午10:52:24
 * 
 * @author leobert.lan
 * @version 1.0
 */
public class Test {

    public static void main(String[] args) {
        Prototype prototype = new APrototype("1");

        APrototype deepCloneTest1 = (APrototype) prototype.deepClone();

        APrototype deepCloneTest2 = (APrototype) prototype.deepClone();

        deepCloneTest1.setS("deepCloneTest1");
        deepCloneTest2.setS("deepCloneTest2");

        deepCloneTest1.getReliant().setI(1);
        deepCloneTest2.getReliant().setI(2);

        deepCloneTest1.readInfo();
        deepCloneTest2.readInfo();

        System.out.println("========================");

        APrototype shallowCloneTest1 = (APrototype) prototype.shallowClone();

        APrototype shallowCloneTest2 = (APrototype) prototype.shallowClone();

        shallowCloneTest1.setS("shallowCloneTest1");
        shallowCloneTest2.setS("shallowCloneTest2");

        shallowCloneTest1.getReliant().setI(1);
        shallowCloneTest2.getReliant().setI(2);

        shallowCloneTest1.readInfo();
        shallowCloneTest2.readInfo();

    }

}

我修改了一下分割,忘了這是markdown

deepCloneTest1,reliant info:1
deepCloneTest2,reliant info:2
!************
shallowCloneTest1,reliant info:2
shallowCloneTest2,reliant info:2


意圖
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
適用性
當要實例化的類是在運行時刻指定時,例如,通過動態裝載;
或者為了避免創建一個與產品類層次平行的工廠類層次時;
或者當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。

另外,深復制往往采用序列化、反序列化手段處理,而且牽涉到一個深度問題,也會有相互依賴的問題,需要注意

單例

單例
單例模式還是用的比較多的,沒有必要給demo了

意圖
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
適用性
當類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時。
當這個唯一實例應該是通過子類化可擴展的,並且客戶應該無需更改代碼就能使用一個擴展的實例時。

後記

本想一次性全部寫完,但這是奢望啊,這兩天已經算比較閒散的了,慢慢來吧,爭取分三次寫完。demo源碼等寫完了再上傳到下載區吧。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved