设计模式讲解与代码实践(一)——抽象工厂

来源:互联网 发布:python 数组减法 编辑:程序博客网 时间:2024/04/26 05:17

本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!

摘要:本文讲解了抽象工厂(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

0 0