设计模式之工厂模式

文章结构
1.工厂模式的分类
2.参考文章与书籍


1.工厂模式的分类

工厂模式分为简单工厂模式、工厂方法、抽象工厂三种。
工厂所产生的对象叫产品。

1.1简单工厂模式

将创建对象的过程放入工厂,工厂根据接受的消息来决定创建的对象。直接上例子(普通写法和简单工厂模式写法):

普通写法
/**
 * 打印机A
 * @author live
 *
 */
public class Printer_A{
    private String date;
    private String user;
    
    public Printer_A(String user,String date){
        this.date = date; 
        this.user = user;
    }
    
    public void print(){
        System.out.println("有人使用了"+user+"在"+date+"年买回来的打印机A");
    }
}

public class Printer_B{
    public void print(){
        System.out.println("有人使用了打印机B");
    }
}
public class Runsim{
    public static void main(String[] args) {
        Printer_A printer_A = new Printer_A("小明","2018");
        printer_A.print();
        
        Printer_B printer_B = new Printer_B();
        printer_B.print();
    }
}

这是普通写法,代码中使用者拥有打印机对象的创建权,造成了代码高耦合。而且业务与逻辑也没有实现分离;其次Printer_A每次创建都需要重复的将打印机的购买者和日期放入,(不要问我为什么不直接将用户和日期写死,我只是为了模拟一下创建对象的复杂操作,因为没想到好例子,只能这么代替,大家就当这是一个特别复杂的流程就好了,/手动捂脸),而且在其他业务中,每次调用都要重新写这个操作流程,造成了代码的冗杂。

简单工厂模式写法
/**
 * 打印机接口
 * @author live
 *
 */
public interface IPrinter{
    void print();
}

public class Printer_AImpl implements IPrinter{
    
    private String date;
    private String user;
    
    public Printer_AImpl(String user,String date){
        this.date = date; 
        this.user = user;
    }
    
    @Override
    public void print(){
        System.out.println("有人使用了"+user+"在"+date+"年买回来的打印机A");
    }
}

public class Printer_BImpl implements IPrinter{
    @Override
    public void print(){
        System.out.println("有人使用了打印机B");
    }
}
/**
 * 简单工厂模式-工厂
 * @author live Printer
 *
 */
public class SimpleFactory {
    // 根据用户输入的不同产生不同的产品
    public static IPrinter createPrinter(String str){
        IPrinter iPrinter = null;
        switch(str){
        case "1" :
            iPrinter = new Printer_AImpl("小明","2018");
            break;
        case "2" :
            iPrinter = new Printer_BImpl();
            break;
        default :
            break;
        }
        return iPrinter;
    }
}

public class RunPrinter{
    public static void main(String[] args) {
        IPrinter iPrinter = SimpleFactory.createPrinter("1");
        iPrinter.print();
    }
}

上面的代码先创建一个公共接口,由打印机A、B来实现接口,将对象的创建放入工厂的静态方法,消费者每次通过向静态方法传入对应信息来获取相应的对象,消费者只需要使用对象(控制反转)。

因为示例简单,所以看不出简单工厂的好处,如果创建打印机A的过程更加复杂,需要很多的赋值操作,就能体现出简单工厂模式的好处了,而简单工厂也是适用于创建对象复杂的情况,如果只是new一个对象,就直接来new就好了。

如果再添加打印机C,只需要让C实现接口,同时在工厂中添加新的分支就行,相应的业务层使用的时候传入C对应的信息即可。但是这样操作的话就违反了开闭原则(对修改关闭,多扩展开放),每次添加新的打印机的时候都需要去修改工厂的静态方法。

优点:耦合度低,代码简洁、隐藏对象创建的细节
缺点:扩展性差,每次添加新的类都要去改工厂的静态方法。

1.2工厂方法

工厂方法定义了工厂的接口,根据具体的工厂子类来决定创建的产品

// 打印机和简单工厂模式的打印机一样,首先有打印机接口,然后实现具体的打印机,在这省去不写

/**
 * 工厂接口
 * @author live
 *
 */
public interface IFactory{
    /**
     * 生产产品
     * @return
     */
    IPrinter createPrinter();
}
/**
 * 生产A打印机的工厂
 */
public class PrinterAFactoryImpl implements IFactory{

    @Override
    public IPrinter createPrinter() {
        // 打印机和简单工厂模式的打印机一样,省去不写
        return new Printer_AImpl("小明","2018");
    }
    
}
/**
 * 生产B打印机的工厂
 */
public class PrinterBFactoryImpl implements IFactory{

    @Override
    public IPrinter createPrinter() {
        return new Printer_BImpl();
    }
    
}
/**
 * 工厂方法
 * @author live
 *
 */
public class RunFactoryMethod {
    public static void main(String[] args) {
        // 使用打印机A
        IFactory factory = new PrinterAFactoryImpl();
        IPrinter iPrinter =  factory.createPrinter();
        iPrinter.print();
    }
}

该模式将工厂抽象,通过创建指定的工厂子类来生产对应的产品。客户端只需要知道工厂即可。每次新增加打印机的时候只需要创建打印机和对应的工厂即可,遵循了开闭原则。但是增加了额外的开发量。每次增加新的产品也需要增加对应的工厂。

其次,在业务层(使用类)中,如果按照简单工厂模式生产了10个打印机B,此时要求我们将打印机B全部改成打印机A,我们需要该10处代码,如果是工厂方法,我们只需要修改创建的工厂子类一处就行。减少了修改量。

优点:克服了简单工厂所违背的开闭原则的缺点,又保持了封装对象创建过程的优点。
缺点:开发量增大。


1.3抽象工厂

工厂方法模式针对的是一个产品等级结构(即所有产品实现同一个接口),而抽象工厂模式针对的是多个产品等级结构。

抽象工厂模式中还有一个产品族的概念,产品族就是位于不同产品等级结构中的一系列相关的产品。是不是很拗口,举个栗子(看下面的UML图,图为抽象工厂的例子的UML),下图中的IUserDao接口的两个实现类OracleUserDAoImpl和MongoUserDaoImpl就是一个产品等级。OracleUserDAoImpl和OracleUserDAoImpl则属于一个产品族(他们都是oracle的操作类)。而抽象工厂的工厂接口对应的每一个实现类用来生产一个产品族的所有产品。图中的OracleFactoryImpl工厂就可以生产Oracle产品族的所有产品,如果某天系统更换数据库的话,直接把工厂换为Mongo的工厂就行,不用修改具体的userDao的实现(用到了多态)。

本例参考自《大话设计模式》

抽象工厂UML图:UML.jpg

具体代码如下,先创建两个产品等级的接口IUserDao和ITitleDao,在创建工厂的抽象,接下来去实现这三个接口。重点关注一下实现工厂接口的时候,每个具体的工厂实现类中生产的产品族的产品与产品等级之间的联系。


/**
 * 用户接口(对应一个产品等级)
 * @author live
 *
 */
public interface IUserDao{
    void insert();
}
/**
 * 头衔接口(对应一个产品等级)
 * @author live
 *
 */
public interface ITitleDao{
    void create();
}
/**
 * 每个工厂实现可以生产一个产品族的所有产品
 */
public interface IFactory{
    /**
     * 获取UserDao
     * @return
     */
    IUserDao createUser();
    /**
     * 获取TitleDao
     * @return
     */
    ITitleDao createTitle();
}
/**
 * 实现工厂接口--Oracle
 * @author live
 *
 */
public class OracleFactoryImpl implements IFactory{
    @Override
    public IUserDao createUser() {
        return new OracleUserDaoImpl();
    }
    @Override
    public ITitleDao createTitle() {
        return new OracleTitleDaoImpl();
    }
    
}
/**
 * 实现工厂接口--MongoDB
 * @author live
 *
 */
public class MongoFactoryImpl implements IFactory{
    @Override
    public IUserDao createUser() {
        return new MongoUserDaoImpl();
    }
    @Override
    public ITitleDao createTitle() {
        return new MongoTitleDaoImpl();
    }
    
}

--------------------具体的产品实现-------------------------------------
/**
 * 用户操作--Oracle
 * @author live
 *
 */
public class OracleUserDaoImpl implements IUserDao{
    @Override
    public void insert() {
        System.out.println("在Oracle中添加了一个新用户");
    }
}
/**
 * 用户操作--MongoDB
 * @author live
 *
 */
public class MongoUserDaoImpl implements IUserDao{
    @Override
    public void insert() {
        System.out.println("在MongoDB中添加了一个新用户");
    }
}
/**
 * 头衔操作类--Oracle
 * @author live
 *
 */
public class OracleTitleDaoImpl implements ITitleDao{
    @Override
    public void create() {
        System.out.println("用户在Oracle中添加了一个头衔");
    }
    
}
/**
 * 头衔操作类--MongoDB
 * @author live
 *
 */
public class MongoTitleDaoImpl implements ITitleDao{
    @Override
    public void create() {
        System.out.println("用户在MongoDB中添加了一个头衔");
    }
    
}
/**
 * 抽象工厂模式
 * @author live
 *
 */
public class AbstractFactory {
    public static void main(String[] args) {
        // 创建工厂
        /**
         * 换数据库的时候只需要修改创建的工厂就行,不需要重新修改每一项
         */
//      IFactory factory = new OracleFactoryImpl();
        IFactory factory = new MongoFactoryImpl();
        // 用户操作类
        IUserDao userDao = factory.createUser();
        // 头衔操作类
        ITitleDao titleDao = factory.createTitle();
        
        userDao.insert();
        titleDao.create();
    }
}

抽象工厂适用于一个继承体系中,存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束的情况。

缺点:每次添加一个新的产品的时候都要修改所有工厂,所以对于抽象工厂来说产品的等级结构划分很重要。

1.4多方法工厂(参考自其他人的博客)

多方法工厂与简单工厂模式类似,只是修改了工厂的实现。将简单工厂的静态方法去掉,在工厂内添加多个静态方法,每一个静态方法对应一个产品,每次新增加产品的时候只需要在工厂中添加新的静态方法即可。避免修改具体方法的逻辑。

工厂修改的代码如下。

/**
 * 多方法工厂
 */
public class SimpleFactory {
    // 生产A
    public static IPrinter createPrinterA(){
        return new Printer_AImpl("小明","2018");
    }
    /**
     * 生产B
     * @return
     */
    public static IPrinter createPrinterB(){
        return new Printer_BImpl();
    }
}

2.参考文章与书籍

设计模式(一) 工厂模式 五种写法总结
《大话设计模式》

csdn发布地址
博客园发布地址
如有错误,请多多指教

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容