工厂模式

简单工厂模式(SimpleFactoryPattern)

1.定义

  • 简单工厂模式又叫静态方法模式(工厂类定义了一个静态方法)。
  • 现实生活中,工厂是负责生产产品的;同样在设计模式中,简单工厂模式我们可以理解为负责生产对象的一个类,称为“工厂类”。

2.主要作用

将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。

即使用者可直接消费产品而不需要知道其生产的细节

3.模式原理

3.1UML类图
简单工厂模式UML.png
3.2模式组成
组成(角色) 关系 作用
抽象产品(Product) 具体产品的父类 描述产品的公共接口
具体产品(Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
工厂(Creator) 被外界调用 根据传入不同参数从而创建不同具体产品类的实例

4.实例讲解

接下来我用一个实例来对简单工厂模式进行更深一步的介绍。

4.1实例概况
  • 背景:小成有一个塑料生产厂,用来做塑料加工生意。
  • 目的:最近推出了3个产品,小成希望使用简单工厂模式实现3款产品的生产。
4.2使用步骤

实现代码如下:

步骤1. 创建抽象产品类,定义具体产品的公共接口
abstract class Product{
    public abstract void Show();
}
步骤2. 创建具体产品类(继承抽象产品类),定义生产的具体产品
//具体产品类A
class  ProductA extends  Product{
    @Override
    public void Show() {
        System.out.println("生产出了产品A");
    }
}

//具体产品类B
class  ProductB extends  Product{
    @Override
    public void Show() {
        System.out.println("生产出了产品B");
    }
}

//具体产品类C
class  ProductC extends  Product{
    @Override
    public void Show() {
        System.out.println("生产出了产品C");
    }
}
步骤3. 创建工厂类,通过创建静态方法从而根据传入不同参数创建不同具体产品类的实例
class  Factory {
    public static Product Manufacture(String ProductName){
    //工厂类里用switch语句控制生产哪种商品;
    //使用者只需要调用工厂类的静态方法就可以实现产品类的实例化。
        switch (ProductName){
            case "A":
                return new ProductA();
            case "B":
                return new ProductB();
            case "C":
                return new ProductC();
            default:
                return null;
        }
    }
}
步骤4. 外界通过调用工厂类的静态方法,传入不同参数从而创建不同具体产品类的实例
//工厂产品生产流程
public class SimpleFactoryPattern {
    public static void main(String[] args){
        Factory mFactory = new Factory();

        //客户要产品A
        try {
        //调用工厂类的静态方法 & 传入不同参数从而创建产品实例
            mFactory.Manufacture("A").Show();
        }catch (NullPointerException e){
            System.out.println("没有这一类产品");
        }

        //客户要产品B
        try {
            mFactory.Manufacture("B").Show();
        }catch (NullPointerException e){
            System.out.println("没有这一类产品");
        }

        //客户要产品C
        try {
            mFactory.Manufacture("C").Show();
        }catch (NullPointerException e){
            System.out.println("没有这一类产品");
        }

        //客户要产品D
        try {
            mFactory.Manufacture("D").Show();
        }catch (NullPointerException e){
            System.out.println("没有这一类产品");
        }
    }
}
结果输出:
生产出了产品A
生产出了产品C
生产出了产品C
没有这一类产品

5.优点

  • 将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
  • 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。

6.缺点

  • 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
  • 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂;
  • 简单工厂模式由于使用了静态工厂方法,静态方法不能被重写,会造成工厂角色无法形成基于继承的等级结构。

7.应用场景

在了解了优缺点后,我们知道了简单工厂模式的应用场景:

  • 客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时;
  • 当工厂类负责创建的对象(具体产品)比较少时。

工厂方法模式(Factory Method)

工厂方法模式产生背景:为了解决简单工厂模式的缺点

1.定义

工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。

2.主要作用

  • 将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。
  • 解决了简单工厂模式工厂一旦需要生产新产品就需要修改工厂类的方法逻辑,违背了“开放 - 关闭”原则的缺点。

之所以可以解决简单工厂的问题,是因为工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点。

3.模式原理

3.1UML类图
工厂方法模式UML.png
3.2模式组成
组成(角色) 关系 作用
抽象产品(Product) 具体产品的父类 描述产品的公共接口
具体产品(Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
抽象工厂(Abstract Factory) 具体工厂的父类 描述具体工厂的公共接口
具体工厂(Concrete Factory) 抽象工厂的子类;被外界调用 描述具体工厂;实现FactoryMethod工厂方法创建产品的实例

4.实例讲解

接下来我用一个实例来对工厂方法模式进行更深一步的介绍。

4.1实例概况
  • 背景:小成有一间塑料加工厂(仅生产A类产品);随着客户需求的变化,客户需要生产B类产品。
  • 冲突:改变原有塑料加工厂的配置和变化非常困难,假设下一次客户需要再发生变化,再次改变将增大非常大的成本。
  • 解决方案:小成决定置办塑料分厂B来生产B类产品。

即工厂方法模式

4.2使用步骤

实现代码如下:

步骤1: 创建抽象产品类 ,定义具体产品的公共接口
abstract class Product{
    public abstract void Show();
}
步骤2: 创建具体产品类(继承抽象产品类), 定义生产的具体产品
//具体产品A类
class  ProductA extends  Product{
    @Override
    public void Show() {
        System.out.println("生产出了产品A");
    }
}

//具体产品B类
class  ProductB extends  Product{
    @Override
    public void Show() {
        System.out.println("生产出了产品B");
    }
}
步骤3: 创建抽象工厂类,定义具体工厂的公共接口
abstract class Factory{
    public abstract Product Manufacture();
}
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法
//工厂A类 - 生产A类产品
class  FactoryA extends Factory{
    @Override
    public Product Manufacture() {
        return new ProductA();
    }
}

//工厂B类 - 生产B类产品
class  FactoryB extends Factory{
    @Override
    public Product Manufacture() {
        return new ProductB();
    }
}
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
//生产工作流程
public class FactoryPattern {
    public static void main(String[] args){
        //客户要产品A
        FactoryA mFactoryA = new FactoryA();
        mFactoryA.Manufacture().Show();

        //客户要产品B
        FactoryB mFactoryB = new FactoryB();
        mFactoryB.Manufacture().Show();
    }
}
结果输出:
生产出了产品A
生产出了产品B

5.优点

  • 更符合开-闭原则,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可(简单工厂模式需要修改工厂类的判断逻辑)。
  • 符合单一职责原则,每个具体工厂类只负责创建对应的产品(简单工厂中的工厂类存在复杂的switch逻辑判断)。
  • 不使用静态工厂方法,可以形成基于继承的等级结构(简单工厂模式的工厂类使用静态工厂方法)。
  • 总结:工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。

6.缺点

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度;
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
  • 一个具体工厂只能创建一种具体产品。

7.应用场景

在了解了优缺点后,我总结了工厂方法模式的应用场景:

  • 当一个类不知道它所需要的对象的类名时,在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
  • 当一个类希望通过其子类来指定创建对象时,在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展;
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

抽象工厂模式(Abstract Factory)

  • 背景:工厂方法模式一个具体工厂只能创建一类产品,而在实际过程中,一个工厂往往需要生产多类产品。为了解决这个问题,抽象工厂模式就产生了。

1.定义

抽象工厂模式,即Abstract Factory Pattern,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。

抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类。

2.主要作用

允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。

3.模式原理

3.1UML类图
抽象工厂模式UML.png
3.2模式组成
组成(角色) 关系 作用
抽象产品族(AbstractProduct) 抽象产品的父类 描述抽象产品的公共接口
抽象产品(Product) 具体产品的父类 描述具体产品的公共接口
具体产品(Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
抽象工厂(Factory) 具体工厂的父类 描述具体工厂的公共接口
具体工厂(Concrete Factory) 抽象工厂的子类;被外界调用 描述具体工厂;实现FactoryMethod工厂方法创建产品的实例
  • 说明:如何理解抽象产品族、抽象产品和具体产品的区别呢?
    举个很简单的例子就可以理解了,手机(抽象产品族),魅族手机、三星手机、华为手机(抽象产品),魅族16th、荣耀10(具体产品)。

4.实例讲解

接下来我用一个实例来对抽象工厂模式进行更深一步的介绍。

4.1实例概况
  • 背景:小成有两间塑料加工厂(A厂仅生产容器类产品;B厂仅生产模具类产品);随着客户需求的变化,A厂所在地的客户也需要模具类产品,B厂所在地的客户也需要容器类产品。
  • 冲突:没有资源(资金+租位)在当地分别开设多一家注塑分厂。
  • 解决方案:在原有的两家塑料厂里增设生产需求的功能,即A厂能生产容器+模具产品;B厂能生产模具+容器产品。

即抽象工厂模式

4.2使用步骤

实现代码如下:

步骤1: 创建抽象产品族类 ,定义抽象产品的公共接口
abstract class AbstractProduct{
    public abstract void Show();
}
步骤2: 创建抽象产品类 ,定义具体产品的公共接口
//容器产品抽象类
abstract class ContainerProduct extends AbstractProduct{
    @Override
    public abstract void Show();
}

//模具产品抽象类
abstract class MouldProduct extends AbstractProduct{
    @Override
    public abstract void Show();
}
步骤3: 创建具体产品类(继承抽象产品类), 定义生产的具体产品
//容器产品A类
class ContainerProductA extends ContainerProduct{
    @Override
    public void Show() {
        System.out.println("生产出了容器产品A");
    }
}

//容器产品B类
class ContainerProductB extends ContainerProduct{
    @Override
    public void Show() {
        System.out.println("生产出了容器产品B");
    }
}

//模具产品A类
class MouldProductA extends MouldProduct{
    @Override
    public void Show() {
        System.out.println("生产出了模具产品A");
    }
}

//模具产品B类
class MouldProductB extends MouldProduct{
    @Override
    public void Show() {
        System.out.println("生产出了模具产品B");
    }
}
步骤4: 创建抽象工厂类,定义具体工厂的公共接口
abstract class Factory{
    public abstract AbstractProduct ManufactureContainer();
    public abstract AbstractProduct ManufactureMould();
}
步骤5:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法
//A厂 - 生产模具+容器产品
class FactoryA extends Factory{
    @Override
    public AbstractProduct ManufactureContainer() {
        return new ContainerProductA();
    }

    @Override
    public AbstractProduct ManufactureMould() {
        return new MouldProductA();
    }
}

//B厂 - 生产模具+容器产品
class FactoryB extends Factory{
    @Override
    public AbstractProduct ManufactureContainer() {
        return new ContainerProductB();
    }

    @Override
    public AbstractProduct ManufactureMould() {
        return new MouldProductB();
    }
}
步骤6: 客户端通过实例化具体的工厂类,并调用其创建不同目标产品的方法创建不同具体产品类的实例
//生产工作流程
public class AbstractFactoryPattern {
    public static void main(String[] args){
        FactoryA mFactoryA = new FactoryA();
        FactoryB mFactoryB = new FactoryB();
        //A厂当地客户需要容器产品A
        mFactoryA.ManufactureContainer().Show();
        //A厂当地客户需要模具产品A
        mFactoryA.ManufactureMould().Show();

        //B厂当地客户需要容器产品B
        mFactoryB.ManufactureContainer().Show();
        //B厂当地客户需要模具产品B
        mFactoryB.ManufactureMould().Show();
    }
}
结果输出:
生产出了容器产品A
生产出了模具产品A
生产出了容器产品B
生产出了模具产品B

5.优点

  • 降低耦合:抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展;
  • 更符合开-闭原则:新增一种产品类时,只需要增加相应的具体产品类和相应的工厂子类即可(简单工厂模式需要修改工厂类的判断逻辑);
  • 符合单一职责原则:每个具体工厂类只负责创建对应的产品(简单工厂中的工厂类存在复杂的switch逻辑判断);
  • 不使用静态工厂方法,可以形成基于继承的等级结构(简单工厂模式的工厂类使用静态工厂方法)。

6.缺点

抽象工厂模式很难支持新种类产品的变化。
这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。

对于新的产品族符合开-闭原则;对于新的产品种类不符合开-闭原则,这一特性称为开-闭原则的倾斜性。

7.应用场景

在了解了优缺点后,我总结了抽象工厂模式的应用场景:

  • 一个系统不要求依赖产品类实例如何被创建、组合和表达的表达,这点也是所有工厂模式应用的前提。
  • 这个系统有多个系列产品,而系统中只消费其中某一系列产品。
  • 系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端不需要依赖具体实现。

说明:本篇博客是基于简书作者Carson_Ho的三篇关于工厂模式的博客,其实就是我把作者博客直接复制梳理成一篇了,除了改了几个小错误外基本没改动,希望大神不要介意啊,毕竟我是觉得自己梳理一遍怎么也比光看一遍强,可以加深不少印象。

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

推荐阅读更多精彩内容

  • 该文章属于刘小壮原创,转载请注明:刘小壮[https://www.jianshu.com/u/2de707c93d...
    刘小壮阅读 12,728评论 29 59
  • 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。通常我们所说的工厂模式是指工厂方法模...
    zfylin阅读 1,309评论 0 7
  • 工厂模式概述 意义 问题引出在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用...
    stoneyang94阅读 484评论 0 0
  • 我热爱回家,更喜欢一个人独自乘车回家; 每个假期里,熙熙攘攘的人群结伴出行远方; 我们方向不同,人们去欣赏风景而我...
    小丫小丫阅读 339评论 0 2
  • 首先,先上代码。 说一下变量:1.SPRING_SECURITY_FORM_USERNAME_KEY:这个静态常量...
    Aysn阅读 1,580评论 0 0