1. 程式人生 > >設計模式講解與程式碼實踐(一)——抽象工廠

設計模式講解與程式碼實踐(一)——抽象工廠

摘要:本文講解了抽象工廠(Abstract Factory)設計模式的使用目的、基本形態及各參與者,並結合示例程式碼,講解了該設計模式在具體業務場景下的使用。

1 目的

抽象工廠(Abstract Factory)用於提供一個建立一組無需指定具體類的物件的介面。

2 基本形態

抽象工廠的基本形態如類圖2-1所示。

圖2-1  抽象工廠類圖
圖2-1 抽象工廠類圖

3 參與者

結合圖2-1,下面介紹各類在抽象工廠設計模式中扮演的角色。
3.1 ProductA/ProducB
ProductA和ProductB都是抽象產品介面,聲明瞭各抽象產品中應包含的方法。
3.2 ProductA1/ProducA2 /ProductB1/ProducB2


ProductA1、ProductA2是實現了抽象產品ProductA介面的具體類,ProductB1、ProductB2是實現了抽象產品ProductB介面的具體類。
3.3 AbstractFactory
AbstractFactory是抽象工廠介面。AbstractFactory中聲明瞭建立各抽象產品(ProductA、ProductB)的方法。
3.4 ConcreteFactory1/ConcreteFactory2
ConcreteFactory1類和ConcreteFactory2類是實現AbstractFactory介面的具體類。它們分別實現AbstractFactory介面中宣告的各建立物件方法。對於具體工廠,其建立物件方法所建立的產品是實現了抽象產品介面的具體產品的物件。即ConcreteFactory1建立的是實現了抽象產品介面ProductA和ProductB的ProductA1和ProductB1類物件,而ConcreteFactory2建立的是實現了抽象產品介面ProductA和ProductB的ProductA2和ProductB2類物件。
3.5 Client

Client是使用抽象工廠的主體。Client根據場景用具體類(ConcreteFactory1或ConcreteFactory2)物件例項化抽象工廠介面(AbstractFactory)。之後,在Client中使用例項化的工廠類物件建立實現了各抽象產品介面的具體產品類物件。

4 程式碼實踐

下面我們用一個業務場景例項來進一步講解抽象工廠的使用。
4.1 場景介紹
在某訂票系統中,使用者註冊時需要提供有效身份證件。證件的種類可以是身份證、護照、軍官證等。現在需要提供身份證件驗證和身份證件資訊解析兩個工具以便後臺管理員對註冊使用者的證件資訊按照統一的業務邏輯進行處理。
以下各節將介紹該場景各類的具體實現及其在抽象工廠設計模式中所對應的參與者角色。
4.2 IIdDocVerifier


IIdDocVerifier是身份證件驗證器介面,聲明瞭對身份證件的長度校驗、校驗位校驗、聯網校驗等方法。對應於抽象工廠模式的參與者,IIdDocVerifier是我們的抽象產品。下面程式碼中給出了IIdDocVerifier的宣告,簡約起見,這裡僅聲明瞭“id號碼長度是否合法”一個方法。

package demo.designpattern.abstractfactory;

/**
 * 身份證件驗證器
 * Created by LiMingzi on 2017/4/26.
 */
public interface IIdDocVerifier {
    /**
     * id號碼長度是否合法
     * @return id號碼長度是否合法
     */
    boolean isIdLengthValid();
}

4.3 IIdDocParser
IIdDocParser是身份證件資訊解析器介面,聲明瞭對身份證件的型別、持有人出生日期、持有人性別、證件有效期等資訊的解析。對應於抽象工廠模式的參與者,IIdDocParser是我們的抽象產品。下面程式碼中給出了IIdDocParser的宣告,簡約起見,這裡僅聲明瞭“獲取生日”和“獲取性別”兩個方法。

package demo.designpattern.abstractfactory;
import java.util.Date;

/**
 * 身份證件資訊解析器
 * Created by LiMingzi on 2017/4/26.
 */
public interface IIdDocParser {
    /**
     * 獲取生日
     * @return 生日
     */
    public Date getBirthday();

    /**
     * 獲取性別
     * @return 性別,“男”或“女”
     */
    public String getGender();
}

4.4 IdCardVerifier
IdCardVerifier類是身份證驗證器,實現了IIdDocVerifier介面。對應於抽象工廠模式的參與者,IdCardVerifier是抽象產品IIdDocVerifier在身份證場景下的具體產品。IdCardVerifier的程式碼如下。

package demo.designpattern.abstractfactory;

/**
 * 身份證驗證器
 * Created by LiMingzi on 2017/4/26.
 */
public class IdCardVerifier implements IIdDocVerifier {
    /**
     * 身份證號碼
     */
    private String id;

    /**
     * 構造方法
     *
     * @param id 身份證號
     */
    public IdCardVerifier(String id) {
        this.id = id;
    }

    /**
     * id號碼長度是否合法
     *
     * @return id號碼長度是否合法
     */
    @Override
    public boolean isIdLengthValid() {
        return id.length() == 18;
    }
}

4.5 PassportVerifier
PassportVerifier類是護照證驗證器,實現了IIdDocVerifier介面。對應於抽象工廠模式的參與者,PassportVerifier是抽象產品IIdDocVerifier在護照場景下的具體產品。PassportVerifier的程式碼如下。

package demo.designpattern.abstractfactory;

/**
 * 護照驗證器
 * Created by LiMingzi on 2017/4/27.
 */
public class PassportVerifier implements IIdDocVerifier {
    /**
     * 護照編號
     */
    private String id;

    /**
     * 構造方法
     *
     * @param id 護照編號
     */
    public PassportVerifier(String id) {
        this.id = id;
    }

    /**
     * id號碼長度是否合法
     *
     * @return id號碼長度是否合法
     */
    @Override
    public boolean isIdLengthValid() {
        return id.length() == 44;
    }
}

4.6 IdCardParser
IdCardParser類是身份證資訊解析器,實現了IIdDocParser介面。對應於抽象工廠模式的參與者,IdCardParser是抽象產品IIdDocParser在身份證場景下的具體產品。IdCardParser的程式碼如下。

package demo.designpattern.abstractfactory;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 身份證資訊解析器
 * Created by LiMingzi on 2017/4/26.
 */
public class IdCardParser implements IIdDocParser {
    /**
     * 身份證號碼
     */
    private String id;
    /**
     * 構造方法
     * @param id 身份證號
     */
    public IdCardParser(String id){
        this.id=id;
    }
    /**
     * 獲取生日
     *
     * @return 生日
     */
    @Override
    public Date getBirthday() {
        // 日期格式器
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        // 生日
        Date birthday = null;
        try {
            birthday = dateFormat.parse(id.substring(6,14));
        } catch (ParseException e) {
            throw new RuntimeException("日期解析失敗");
        }
        return birthday;
    }

    /**
     * 獲取性別
     *
     * @return 性別,“男”或“女”
     */
    @Override
    public String getGender() {
        return Integer.parseInt(id.substring(14,17))%2==0?"女":"男";
    }
}

4.7 PassportParser
PassportParser類是護照資訊解析器,實現了IIdDocParser介面。對應於抽象工廠模式的參與者,PassportParser是抽象產品“IIdDocParser”在護照場景下的具體產品。PassportParser的程式碼如下。

package demo.designpattern.abstractfactory;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 護照資訊解析器
 * Created by LiMingzi on 2017/4/26.
 */
public class PassportParser implements IIdDocParser {
    /**
     * 護照編號
     */
    private String id;
    /**
     * 構造方法
     * @param id 護照編號
     */
    public PassportParser(String id){
        this.id=id;
    }
    /**
     * 獲取生日
     *
     * @return 生日
     */
    @Override
    public Date getBirthday() {
        // 日期格式器
        DateFormat dateFormat = new SimpleDateFormat("yyMMdd");
        // 生日
        Date birthday = null;
        try {
            birthday = dateFormat.parse(id.substring(13,19));
        } catch (ParseException e) {
            throw new RuntimeException("日期解析失敗");
        }
        return birthday;
    }

    /**
     * 獲取性別
     *
     * @return 性別,“男”或“女”
     */
    @Override
    public String getGender() {
        return id.substring(20,21).equals("F")?"女":"男";
    }
}

4.8 IIdDocToolFactory
IIdDocToolFactory身份證件工具建立介面,聲明瞭“建立身份證件驗證器”、“建立身份證件解析器”等方法。對應於抽象工廠模式的參與者,IIdDocToolFactory是我們的抽象工廠。IIdDocToolFactory的程式碼如下。

package demo.designpattern.abstractfactory;


/**
 * 身份證件工具抽象工廠
 * Created by LiMingzi on 2017/4/26.
 */
public interface IIdDocToolFactory {
    /**
     * 建立身份證件資訊解析器
     * @param id 證件號碼
     * @return 身份證件資訊解析器物件
     */
    IIdDocParser createIdDocParser(String id);

    /**
     * 建立身份證件驗證器
     * @param id 證件號碼
     * @return 身份證件驗證器物件
     */
    IIdDocVerifier createIdDocVerifier(String id);
}

上面的程式碼中,14行聲明瞭“建立身份證件資訊解析器”方法,它的返回值型別IIdDocParser 是“身份證件資訊解析器”介面,是抽象產品,而非具體產品;21行聲明瞭“建立身份證件驗證器”方法,它的返回值型別IIdDocVerifier 是“身份證件驗證器”介面,也是抽象產品,而非具體產品。
4.9 IdCardToolFactory
IdCardToolFactory類是身份證工具工廠,實現了IIdDocToolFactory介面,用於建立身份證的各種工具類物件。對應於抽象工廠模式的參與者,IdCardToolFactory是我們的具體工廠。IdCardToolFactory的程式碼如下。

package demo.designpattern.abstractfactory;

/**
 * 身份證工具工廠
 * Created by LiMingzi on 2017/4/26.
 */
public class IdCardToolFactory implements IIdDocToolFactory {
    /**
     * 建立身份證資訊解析器
     *
     * @param id 證件號碼
     * @return 身份證資訊解析器物件
     */
    @Override
    public IIdDocParser createIdDocParser(String id) {
        return new IdCardParser(id);
    }

    /**
     * 建立身份證驗證器
     *
     * @param id 證件號碼
     * @return 身份證驗證器物件
     */
    @Override
    public IIdDocVerifier createIdDocVerifier(String id) {
        return new IdCardVerifier(id);
    }
}

上面的程式碼中,15行聲明瞭“建立身份證資訊解析器”方法,16行返回的是身份證資訊解析器類IdCardParser物件,是具體產品;26行聲明瞭“建立身份證驗證器”方法,27行返回的是身份證驗證器類IdCardVerifier物件,也是具體產品。

4.10 PassportToolFactory
PassportToolFactory類是護照工具工廠,實現了IIdDocToolFactory介面,用於建立護照的各種工具類物件。對應於抽象工廠模式的參與者,PassportToolFactory是我們的具體工廠。PassportToolFactory的程式碼如下。

package demo.designpattern.abstractfactory;

/**
 * 護照工具工廠
 * Created by LiMingzi on 2017/4/27.
 */
public class PassportToolFactory implements IIdDocToolFactory {
    /**
     * 建立護照資訊解析器
     *
     * @param id 證件號碼
     * @return 護照資訊解析器物件
     */
    @Override
    public IIdDocParser createIdDocParser(String id) {
        return new PassportParser(id);
    }

    /**
     * 建立護照驗證器
     *
     * @param id 證件號碼
     * @return 護照驗證器物件
     */
    @Override
    public IIdDocVerifier createIdDocVerifier(String id) {
        return new PassportVerifier(id);
    }
}

上面的程式碼中,15行聲明瞭“建立護照資訊解析器”方法,16行返回的是護照資訊解析器類PassportParser物件,是具體產品;26行聲明瞭“建立護照驗證器”方法,27行返回的是護照驗證器類PassportVerifier物件,也是具體產品。

4.11 UserInfoViewer
UserInfoViewer是使用者資訊檢視器類。對應於抽象工廠模式的參與者,UserInfoViewer作為抽象工廠的使用者,是客戶Client。UserInfoViewer的程式碼如下。

package demo.designpattern.abstractfactory;

/**
 * 使用者資訊檢視器
 * Created by LiMingzi on 2017/4/27.
 */
public class UserInfoViewer {
    /**
     * 獲取使用者證件號(demo樣例)
     * @param userId 使用者id
     * @return 證件號資訊陣列,其中[0]為證件型別,1為身份證,2為護照;[1]為證件號碼
     */
    private String[] getUserIdDocCode(String userId){
        // 證件資訊陣列
        String [] idDoc = new String[2];
        if(userId.equals("001")){
            idDoc[0]="1";
            idDoc[1]="210102198505105335";
        }
        else if (userId.equals("002")){
            idDoc[0]="2";
            idDoc[1]="G222222224CHN8510105F180101952525252<<<<<<85";
        }
        return idDoc;
    }

    /**
     * 輸出使用者資訊
     * @param userId 使用者id
     */
    public void outputUserInfo(String userId){
        // 證件資訊陣列
        String [] idDoc = getUserIdDocCode(userId);
        // 身份證件工具抽象工廠
        IIdDocToolFactory idDocToolFactory = null;
        // 身份證
        if(idDoc[0].equals("1")){
            idDocToolFactory = new IdCardToolFactory();
        }
        // 護照
        else if(idDoc[0].equals("2")){
            idDocToolFactory = new PassportToolFactory();
        }
        if(idDocToolFactory==null){
            return;
        }
        System.out.println("證件號碼:"+idDoc[1]);
        // 身份證件校驗器
        IIdDocVerifier idDocVerifier = idDocToolFactory.createIdDocVerifier(idDoc[1]);
        if(!idDocVerifier.isIdLengthValid()){
            System.out.println("證件資訊非法");
            return;
        }
        // 身份證件資訊解析器
        IIdDocParser idDocParser= idDocToolFactory.createIdDocParser(idDoc[1]);
        System.out.println("性別:"+idDocParser.getGender());
        System.out.println("出生日期:"+idDocParser.getBirthday());
    }
}

上面的程式碼中,13行聲明瞭獲取使用者證件號方法getUserIdDocCode,該方法根據使用者id返回使用者的身份證件型別及號碼。本示例中只是簡單的通過列舉id的形式給出對應的使用者證件資訊,在實際專案中,該資訊應在資料庫中查詢獲取。
在輸出使用者資訊方法outputUserInfo中,35行聲明瞭抽象工廠物件idDocToolFactory 。36-43行,根據證件型別用對應的具體工廠類例項化抽象工廠物件。在例項化抽象工廠後,程式碼邏輯不再區分證件型別,對身份證件資訊統一處理。49行和55行,分別呼叫例項化後的idDocToolFactory 物件的物件建立方法createIdDocVerifier和createIdDocParser例項化抽象產品物件idDocVerifier 和idDocParser。
50、56、57行呼叫在抽象產品中宣告的各方法實現對應的業務功能。
4.12 測試程式碼
為了測試本文中的程式碼,我們可以編寫如下測試程式碼。

package demo.designpattern;

import demo.designpattern.abstractfactory.UserInfoViewer;

/**
 * Created by LiMingzi on 2017/4/26.
 */
public class Main {
    public static void main(String[] args) {
        abstractFactoryTest();
    }

    /**
     * 抽象工廠測試
     */
    public static void  abstractFactoryTest(){
        // 使用者資訊顯示器
        UserInfoViewer userInfoViewer = new UserInfoViewer();
        userInfoViewer.outputUserInfo("001");
        System.out.println("-----------------------------------------------------------------");
        userInfoViewer.outputUserInfo("002");
    }
}

編譯執行後,得到如下測試結果:

證件號碼:210102198505105335
性別:男
出生日期:Fri May 10 00:00:00 CST 1985

證件號碼:G222222224CHN8510105F180101952525252<<<<<<85
性別:女
出生日期:Thu Oct 10 00:00:00 CST 1985