天天看點

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

本文來自李明子csdn部落格(http://blog.csdn.net/free1985),商業轉載請聯系部落客獲得授權,非商業轉載請注明出處!

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

1 目的

抽象工廠(Abstract Factory)用于提供一個建立一組無需指定具體類的對象的接口。

2 基本形态

抽象工廠的基本形态如類圖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() == ;
    }
}
           

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() == ;
    }
}
           

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(,));
        } catch (ParseException e) {
            throw new RuntimeException("日期解析失敗");
        }
        return birthday;
    }

    /**
     * 擷取性别
     *
     * @return 性别,“男”或“女”
     */
    @Override
    public String getGender() {
        return Integer.parseInt(id.substring(,))%==?"女":"男";
    }
}
           

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(,));
        } catch (ParseException e) {
            throw new RuntimeException("日期解析失敗");
        }
        return birthday;
    }

    /**
     * 擷取性别
     *
     * @return 性别,“男”或“女”
     */
    @Override
    public String getGender() {
        return id.substring(,).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[];
        if(userId.equals("001")){
            idDoc[]="1";
            idDoc[]="210102198505105335";
        }
        else if (userId.equals("002")){
            idDoc[]="2";
            idDoc[]="G222222224CHN8510105F180101952525252<<<<<<85";
        }
        return idDoc;
    }

    /**
     * 輸出使用者資訊
     * @param userId 使用者id
     */
    public void outputUserInfo(String userId){
        // 證件資訊數組
        String [] idDoc = getUserIdDocCode(userId);
        // 身份證件工具抽象工廠
        IIdDocToolFactory idDocToolFactory = null;
        // 身份證
        if(idDoc[].equals("1")){
            idDocToolFactory = new IdCardToolFactory();
        }
        // 護照
        else if(idDoc[].equals("2")){
            idDocToolFactory = new PassportToolFactory();
        }
        if(idDocToolFactory==null){
            return;
        }
        System.out.println("證件号碼:"+idDoc[]);
        // 身份證件校驗器
        IIdDocVerifier idDocVerifier = idDocToolFactory.createIdDocVerifier(idDoc[]);
        if(!idDocVerifier.isIdLengthValid()){
            System.out.println("證件資訊非法");
            return;
        }
        // 身份證件資訊解析器
        IIdDocParser idDocParser= idDocToolFactory.createIdDocParser(idDoc[]);
        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