关闭

《设计模式》学习笔记3——工厂模式

标签: 设计模式工厂模式
185人阅读 评论(0) 收藏 举报
分类:

定义

工厂模式实际上有广义和狭义的分别,广义的工厂模式指的是简单工厂模式、工厂方法模式、抽象工厂模式三个,而狭义的工厂模式就是这里的工厂方法模式,一般情况下如果有人直接说工厂模式,多半指的就是工厂方法模式。工厂方法模式引用书中的定义如下:

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个
类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式
(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式
(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式

理解

对于工厂方法模式的定义,我觉得首先需要注意的是几个概念上的东西,也就是这个模式的几个别称,如虚拟构造器模式、多态工厂模式,根据以往面试的经验,如果考察知识面,某些公司是极有可能用这些别称来代替大家熟知的工厂模式的。
那么要理解工厂方法模式,结合之前的简单工厂模式应该就很容易了。
在之前的简单工厂模式中,有一个抽象产品类、若干个具体产品类、一个工厂类和一个静态工厂方法、还有一个消费者类。
之前说到工厂类的工厂方法是提供给外部获取产品的方法,而实际上这个方法内部也负责了产品的创建,所以也有一些地方说这个工厂方法是创建产品对象的方法。
而我认为这些都不重要,重要的是我的目的只是想要产品的创建和消费者分离,实现松耦合。所以不论对那个工厂方法的描述和理解是什么,只要知道这个方法的作用是不再需要消费者自己创建产品对象就够了。
但是,简单工厂模式中的这个工厂却有这一定的缺陷存在,并不适用于所有的场合。
之前的例子中以一个生产儿童牙膏和普通牙膏的工厂作为例子,现在我的实现依然要停留在这个制造牙膏的工厂中。
如果这个工厂的经营一直一成不变,永远都只生产这两种牙膏,那么之前的简单工厂完全没有问题,足以胜任,但是这只是一个假设,现实中的工厂总是要发展壮大的。
随着时间的推移和利润的增长,工厂又引进了各种产品,例如特效美白牙膏、超级环保牙膏,不止是牙膏,还有儿童牙刷、成年人牙刷、普通刷牙杯、创意刷牙杯等等,那么之前的工厂方法将变成这样:

    /**
     * 外部获取产品的工厂方法
     */
    public static MyProduct getProduct(int type) {
        MyProduct product = null;
        if (0 == type) {
            // 普通牙膏
            product = new MyProduct1();
        } else if (1 == type) {
            // 儿童牙膏
            product = new MyProduct2();
        } else if (2 == type) {
            // 特效美白牙膏
        } else if (3 == type) {
            // 超级环保牙膏
        } else if (4 == type) {
            // 儿童牙刷
        } else if (5 == type) {
            // 成年人牙刷
        } else if (6 == type) {
            // 普通刷牙杯
        } else if (7 == type) {
            // 创意刷牙杯
        }
        return product;
    }

上边的代码基本没有太具体的处理,已经有点让人眼花,如果再加上更具体的处理,可能每个if里边都会占满一个屏幕,那么这样的代码查找问题以及后期维护将会是一场噩梦。
如果把这个类回归实际生产工厂,每一个产品是一条生产线,那么如此多的if和else就会是如此多的生产线。
这么多的生产线放在一个工厂车间中,且不说空间的限制能否容得下,单说管理方面,就会是一种剪不断理还乱。
如果上边的工厂遇到这样的问题,势必会增加分厂,增加车间。然后会有一个管理所有分厂的总厂,不负责实际的生产。
而考虑到之前的产品发展时间长,需求量太大,因此只是单个的产品可能就需要一个单独的工厂来生产,因此分厂的设计便是一个分厂只负责一个产品。
代码的逻辑本身就来源于现实中,那么上边的工厂就需要改变,需要增加工厂类和工厂方法,犹如分厂。
还需要有一个抽象的工厂类和抽象工厂方法作为总厂,便是所谓的抽象父类。
那么优化后的工厂方法就需要改成如下,首先需要有一个抽象的工厂父类,可以使抽象类,可以使普通类,也可以是接口,但是通常可能就声明为接口或抽象类,我这里就声明为一个接口:

package patterntest.factorypattern;
import patterntest.product.MyProduct;
public interface MyFactory {
    /**
     * 外部获取产品的工厂方法,父类工厂接口
     */
    public MyProduct getProduct(int type);
}

这里定义了一个提供给外部获取产品的统一接口,基本和简单工厂方法里的定义一样,不同的是这个方法不再是静态方法,也没有了方法实体。
至于为什么没有方法实体,这个很好理解,是因为这个接口中这个方法并不需要具体的生产产品,具体的生产工作需要分厂来做,所以他就应该是抽象的没有实体的方法。
而为什么简单工厂模式里的工厂方法是static静态的,这里却去掉了static呢?那是因为虽然static的方法也可以被继承,但是却“不能被覆盖”。
对于非static的方法,如果子类重写了这个方法,当我们用父类调用这个方法的时候,会调用实际子类的这个方法。
但是如果是static的方法,子类也“重写”了这个方法,那么再使用父类调用这个方法的时候,调用的还是父类的这个方法,也就是并不能实现多态性。
为什么上边的重写要打引号?是因为这个“重写”,不是我们平常理解的那个重写,看起来是和父类一模一样的方法,但是不能使用@Override注解,相当于就是互相独立没有关系的方法。
有了接口和抽象方法,我然后就需要有实际工厂类和方法,但是为了演示,首先还需要提供抽象产品类和实际产品类,这里只演示三个:

package patterntest.product;

/**
 * 产品类父类
 * 
 * @author tzx
 *
 */
public abstract class MyProduct {
    /**
     * 产品名称
     */
    protected String productName;
    /**
     * 产品规格
     */
    protected String productSize;

    /**
     * 添加必要成分
     */
    public void addIngredient() {

    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductSize() {
        return productSize;
    }

    public void setProductSize(String productSize) {
        this.productSize = productSize;
    }

    @Override
    public String toString() {
        return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]";
    }

}
package patterntest.product;

/**
 * 普通牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct1 extends MyProduct {
    public MyProduct1() {
        addIngredient();
    }
}
package patterntest.product;

/**
 * 儿童牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct2 extends MyProduct {
    public MyProduct2() {
        addIngredient();
        delead();
    }

    /**
     * 去铅
     */
    public void delead() {

    }
}
package patterntest.product;

/**
 * 特效美白牙膏
 * 
 * @author tzx
 *
 */
public class MyProduct3 extends MyProduct {

    public MyProduct3() {
        addIngredient();
        addWhitening();
    }

    /**
     * 增加特效美白成分
     */
    public void addWhitening() {

    }
}

有了具体的三个产品,根据上边的理念,每个工厂生产一种产品,也就是每个产品要对应一个实际工厂,所以我们需要有三个对应的工厂,这些工厂均实现工厂接口的抽象方法:

package patterntest.factorypattern;

import patterntest.product.MyProduct;
import patterntest.product.MyProduct1;

/**
 * 生产普通牙膏的分厂
 * 
 * @author tzx
 *
 */
public class MyFactory1 implements MyFactory {

    @Override
    public MyProduct getProduct() {
        return new MyProduct1();

    }

}
package patterntest.factorypattern;

import patterntest.product.MyProduct;
import patterntest.product.MyProduct2;

/**
 * 生产儿童牙膏的分厂
 * 
 * @author tzx
 *
 */
public class MyFactory2 implements MyFactory {

    @Override
    public MyProduct getProduct() {
        return new MyProduct2();
    }

}
package patterntest.factorypattern;

import patterntest.product.MyProduct;
import patterntest.product.MyProduct3;

/*
 * 生产特效美白牙膏的分厂
 */
public class MyFactory3 implements MyFactory {

    @Override
    public MyProduct getProduct() {
        return new MyProduct3();
    }

}

这样一来,工厂就可以正常开始运行了,接下来是消费者找工厂购买商品,只需要根据实际需要创建时间的工厂,然后调用生产方法,就可以得到想要的产品:

package patterntest.consumer;

import patterntest.factorypattern.MyFactory;
import patterntest.factorypattern.MyFactory1;
import patterntest.factorypattern.MyFactory2;
import patterntest.factorypattern.MyFactory3;

/**
 * 消费者
 * 
 * @author tzx
 *
 */
public class Consumer {

    public static void main(String[] args) {
        MyFactory factory = null;
        // 我需要普通牙膏
        factory = new MyFactory1();
        factory.getProduct();
        System.out.println(factory.getProduct().getClass());
        // 我需要儿童牙膏
        factory = new MyFactory2();
        factory.getProduct();
        System.out.println(factory.getProduct().getClass());
        // 我需要特效美白牙膏
        factory = new MyFactory3();
        factory.getProduct();
        System.out.println(factory.getProduct().getClass());
    }

}

要点

那么,根据上边的实例可以归纳出工厂方法的要点基本如下:
1. 需要有一个抽象产品类
2. 需要有若干个具体的产品类
3. 需要有一个抽象工厂和抽象工厂方法
4. 需要每个具体产品对应一个具体的工厂和工厂方法
5. 抽象工厂中的抽象工厂方法需要是非static的
6. 消费者需要自己根据实际需要创建实际的工厂

总结

工厂方法模式相对于简单工厂模式,就是减轻了工厂类和工厂方法的工作,把简单工厂模式中的一个工厂类根据具体产品拆分成不同的工厂,并提供一个父类接口。
这样做不仅实现了产品和消费者的分离,也实现了具体工厂的轻负荷,使用面向对象的多态性在运行期选择具体的工厂。
但是这种模式中,缺点也是显而易见的,每一个产品都需要有一个产品工厂,就相当于每增加一个产品,都至少要增加两个类,大大增加了类的个数。
另外,根据简单工厂模式那里的理解,自己new一个工厂,相当于为了一个牙膏创建一个工厂,这似乎也是不合理的。
但是需要明白的是,现在的场景不一样了,每个工厂创造的是一批牙膏而不是一支,所以这里new一个工厂,其实可以理解成我需要一批牙膏,于是我找了个工厂代理生产了一批。

注:书中实际上还提到了对工厂方法模式的优化,那就是在使用的时候使用xml配置文件和反射来得到具体的工厂类,而不是直接new,是否要这样优化可以根据每个人的实际需要选择。

demo源码可在github下载:https://github.com/tuzongxun/mypattern

0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

浅谈设计模式之简单工厂模式与策略模式

设计模式
  • hong_jing
  • hong_jing
  • 2016-09-01 01:28
  • 270

设计模式(一) 工厂模式 五种写法总结

转载请标明出处: http://blog.csdn.net/zxt0601/article/details/52703280本文出自:【张旭童的博客】系列开篇瞎BB设计模式相关的文章学习与总结,一...
  • zxt0601
  • zxt0601
  • 2016-10-12 16:47
  • 10224

23种设计模式—之简单工厂模式

简单工厂模式(SimpleFactory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实...
  • JavaWebRookie
  • JavaWebRookie
  • 2015-11-06 13:46
  • 1995

Spring中的设计模式--工厂方法模式

关于工厂模式的误会这是个老生常谈的问题,很多新手或者不太在意设计模式的同事会对工厂模式有极深的误解,总会把“工厂模式”与“静态工厂方法”混为一谈,什么是静态工厂方法呢?可以看一个非常简单的例子:pub...
  • zstu_cc
  • zstu_cc
  • 2017-01-02 19:40
  • 3861

设计模式【工厂方法模式】

本文主要是分享自己在学习设计模式过程中的心得与记录,有什么不当之处,望各位有心关注的看官,给予指证与说明。本文主要讲述【工厂方法模式】,望各位细细品读。【转载使用,请注明出处:http://blog....
  • ma_hoking
  • ma_hoking
  • 2014-09-23 22:34
  • 1205

【设计模式】HeadFirst设计模式(四):工厂模式

设计模式要求我们不应该针对实现编程,为了降低耦合度,提高可维护性。当程序中出现“new”的时候,就证明程序在实例化一个具体类,所以用的是实现,而不是接口。如果代码绑着具体的类会导致代码更加脆弱,缺乏弹...
  • u010800530
  • u010800530
  • 2015-05-31 22:47
  • 978

大话设计模式 读书笔记

大话设计模式 读书笔记 着重从c#代码角度分析 学习心得: 学设计模式,不需要是否能立刻理解和记忆,无需着力首先是UML图 再从设计模式到UML图 从UML图到代码 其次知道各种模式的应用场景即可...
  • jiangdmdr
  • jiangdmdr
  • 2017-03-01 18:50
  • 327

设计模式在cocos2d-x中的使用--简单工厂模式(Simple Factory)

什么是简单工厂模式? 从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个...
  • oktears
  • oktears
  • 2014-06-29 15:40
  • 2162

3. php的设计模式:从支付开始谈工厂模式

单纯的讲设计模式非常的没有意思,而且不利于理解与实践使用。我通过将工厂模式与开发中集成支付的场景进行结合讲解,相信大家能够很好的后面工作中实践工厂模式。
  • hel12he
  • hel12he
  • 2016-06-18 02:05
  • 933

Unity3d与设计模式(三)工厂模式

unity3d与设计模式(三) 简单工厂 工厂方法 抽象工厂
  • yuechuzhao
  • yuechuzhao
  • 2015-07-24 22:34
  • 4636
    有问题要问?点击QQ联系我!
    有需要,qq联系我
    1160569243
    公告栏
    个人资料
    • 访问:791095次
    • 积分:10012
    • 等级:
    • 排名:第2015名
    • 原创:230篇
    • 转载:38篇
    • 译文:0篇
    • 评论:411条