工厂模式之简单工厂、工厂方法与抽象工厂_Kaiz‘s Blog的博客-程序员秘密_简单工厂

技术标签: java  设计模式  

1 简单工厂(Simple Factory)

1.1 问题引出

假如有一个披萨店,Pizza的种类有很多,如CheesePizza、VeggiePizza、PepperPizza等。披萨店根据收到的订单制作Pizza,披萨的制作流程有材料的准备材料、烤、切、包装几步。如何设计Pizza的订购呢?按照一般的设计思路:

那么当用户订购时:

    Pizza OrderPizza(String orderType) {
    
        Pizza pizza;

        if (orderType.equals("veggie")) {
    
            pizza = new VeggiePizza();
        } else if (orderType.equals("cheese")) {
    
            pizza = new CheesePizza();
        } else if (orderType.equals("pepper")) {
    
            pizza = new PepperPizza();
        }
        
        // pizza制作过程
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

然而当披萨店增加或者删除披萨类型时,必须修改OrderPizza的代码。例如想要添加一个“中国披萨”,那么就要添加:

        else if (orderType.equals("china")) {
    
            pizza = new ChinaPizza();
        }

也就是说,只要披萨菜单存在改动,这段代码就得一改再改,这种设计明显违反了“开放-关闭原则” ,并没有做到对修改“关闭”。同时,如果有多个订单,那么每个订单也必须依赖每个子类,这样就显得冗杂。如何优化呢?这里得引出“简单工厂模式”了。

1.2 定义与结构

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,简单工厂模式中专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

直白地讲,简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。

简单工厂模式并不在 GoF 23 种设计模式之列,与其说其是设计模式,不如说是一种编程习惯

简单工厂模式中包含如下角色:

  • Factory:工厂角色

    工厂角色负责实现创建所有实例的内部逻辑。

  • Product:抽象产品角色

    抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口。

  • ConcreteProduct:具体产品角色

    具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

类图

时序图

1.3 实现

将简单工厂模式运用到上面的披萨店中,那么就得建造一个SimpleFactory工厂类,它被所有需要进行实例化的客户类调用。

public class SimpleFactory {
    
    public Pizza createPizza(String orderType) {
    
        Pizza pizza = null;

        if (orderType.equals("veggie")) {
    
            pizza = new VeggiePizza();
        } else if (orderType.equals("cheese")) {
    
            pizza = new CheesePizza();
        } else if (orderType.equals("pepper")) {
    
            pizza = new PepperPizza();
        }
        return pizza;
    }
}

客户代码为:

public PizzaStore{
    
    SimpleFactory simpleFactory;
    
    public OrderPizza(SimpleFactory simpleFactory) {
    
        this.simpleFactory = simpleFactory;
    }
    
	public Pizza orderPizza(String orderType) {
    
        Pizza pizza;
        
        pizza = simpleFactory.createPizza(orderType);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    // 其他方法
}

1.4 小结

优点

  • 工厂类含有必要的判断逻辑,可决定创建产品类的实例的实际时刻。工厂和产品的职责区分明确,客户端仅仅“消费”产品。
  • 客户端只需要知道具体产品类所对应的参数即可。
  • 通过引入配置文件,可在不修改任何客户端代码的情况下更换和增加新的具体产品类,提高了系统的灵活性。

缺点

  • 工厂类集中了所有产品创建逻辑,职责过重,一旦发生异常,整个系统将受影响。
  • 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
  • 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

适用场景

对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

  • 如JDK类库中工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

2 工厂方法(Factory Method)

2.1 问题引出

假如披萨店在各个地儿都有,比如Beijing,Shanghai…他们都保有他们城市的风味。要吃到不同城市风味的披萨,如果运用简单工厂模式:

// 这里创建的工厂,全是在北京地区的工厂
BeijingFactory beijingFactory = new BeijingFactory();
// 创建一个披萨店,将北京工厂的引用作为参数
PizzaStore pizzaStore = new PizzaStore(beijingFactory);
// 制作披萨时,就会得到在北京地区的奶酪披萨
pizzaStore.orderPizza("CheesePizza");

同理,上海的披萨店也类似:

ShanghaiFactory shanghaiFactory = new ShanghaiFactory();
PizzaStore pizzaStore = new PizzaStore(shanghaiFactory);
pizzaStore.orderPizza("CheesePizza");

然而,这样使得代码缺乏弹性。如何弹性地制作出具有一定规范但又不失本地特色的披萨呢?这里就得引出工厂方法模式了。

2.2 定义与结构

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

直白地讲,“工厂方法模式”是对简单工厂模式的进一步抽象化,只是工厂方法把产品的实例化操作推迟到子类

工厂方法模式由4个要素构成。

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 createProduct() 来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

类图

时序图

2.3 实现

还是以上面披萨制作为例,那么就可以把createPizza(style)放回PizzaStore中,不过设置为“抽象方法”,然后每个区域设置自己的PizzaStore子类。

public abstract class PizzaStore {
    
	public Pizza orderPizza(String orderType) {
    
        Pizza pizza;
        
        pizza = createPizza(orderType);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    abstract Pizza createPizza(String orderType);
}

那么创造怎么样口味的披萨由子类工厂决定:

public class MyStyleStore extends PizzaStore {
    
    Pizza createPizza(String type) {
    
        if (type.equals("veggie")) {
    
            pizza = new MyStyleVeggiePizza();
        } else if (type.equals("cheese")) {
    
            pizza = new MyStyleCheesePizza();
        } else if (type.equals("pepper")) {
    
            pizza = new MyStylePepperPizza();
        }
    }
}
public class BeijingStore extends PizzaStore {
    
    Pizza createPizza(String type) {
    
        if (type.equals("veggie")) {
    
            pizza = new BeijingVeggiePizza();
        } else if (type.equals("cheese")) {
    
            pizza = new BeijingCheesePizza();
        } else if (type.equals("pepper")) {
    
            pizza = new BeijingPepperPizza();
        }
    }
}

2.4 小结

优点

  • 用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点

  • 类的个数容易过多,增加复杂度。
  • 考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
  • 抽象产品只能生产一种产品。

适用场景

在工厂方法模式中,客户端不需知道具体产品类的类名,只需知道创建具体产品的工厂类;对于抽象工厂类,只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象。

工厂方法模式在JDK类库中运用比较多,例如:

3 抽象工厂(Abstract Factory)

3.1 问题引出

为了更清晰地理解工厂方法模式,先理解两个概念:

  • 产品等级 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  • 产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

电器工厂的产品等级与产品族

3.2 定义与结构

抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建创建一系列相关或相互依赖对象的家族,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

抽象工厂模式与工厂方法模式区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

抽象工厂模式同工厂方法模式一样,也是由4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。

  • AbstractFactory:抽象工厂
  • ConcreteFactory:具体工厂
  • AbstractProduct:抽象产品
  • Product:具体产品

类图

时序图

3.3 实现

public class AbstractProductX {
    
}
public class AbstractProductY {
    
}
public class ConcreteProductAX extends AbstractProductX {
    
}
public class ConcreteProductBX extends AbstractProductX {
    
}
public class ConcreteProductAY extends AbstractProductY {
    
}
public class ConcreteProductBY extends AbstractProductY {
    
}
public abstract class AbstractFactory {
    
    abstract AbstractProductX createProductX();
    abstract AbstractProductY createProductY();
}
public class ConcreteFactoryA extends AbstractFactory {
    
    AbstractProductX createProductX() {
    
        return new ProductAX();
    }

    AbstractProductY createProductY() {
    
        return new ProductAY();
    }
}
public class ConcreteFactoryB extends AbstractFactory {
    
    AbstractProductX createProductX() {
    
        return new ProductBX();
    }

    AbstractProductY createProductY() {
    
        return new ProductBY();
    }
}
public class Client {
    
    public static void main(String[] args) {
    
        AbstractFactory abstractFactory = new ConcreteFactoryA();
        AbstractProductX productX = abstractFactory.createProductX();
        AbstractProductY productY = abstractFactory.createProductY();
        // do something with productX and productY
    }
}

3.4 小结

优点

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建,从具体的产品解耦出来。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点

  • 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)

工厂方法模式在JDK类库中的运用:

4 总结

严格地讲,简单工厂模式并不在GoF23种设计模式之列,更像是一种编程习惯,也可以理解为工厂方法模式的特殊情况之一。

  • 简单工厂方法实现了客户类与其子类的解耦。

  • 工厂方法模式属于类创建型模式,它把产品的实例化操作也推迟到子类,实现了产品类与其子类的解耦。

  • 抽象工厂模式属于对象创建型模式,相对工厂方法模式针对一个产品等级结构,抽象工厂模式则需要面对多个产品等级结构,一个抽象工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

三种设计模式都各有优缺点,在实际开发中,我们应该根据实际业务需求来选择。

参考

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/KAIZ_LEARN/article/details/110863224

智能推荐

登录失败次数限制(原生php代码实现)_罗正波的博客-程序员秘密

登录密码错误次数限制 安全对每个网站的重要性,不言自明。 其中,登陆又是网站中比较容易受到攻击的一个地方,那么我们如何对登陆功能的安全性加强呢? 我们先来看一些知名的网站是如何做的Github Github网站同一个账号在同一个IP地址连续密码输错一定次数后,这个账号是会被锁定30分钟的。如下图所示:Github这么做的主要原因,我觉得主要基于以下考虑:防止用户的账号密码被暴力破解Si

IDEA使用总结_栗超的博客-程序员秘密

IDEA简介借用百度百科的:IDEA 全称IntelliJ IDEA,是Java语言开发的集成环境,IntelliJ在业界被公认为最好的Java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、 创新的GUI设计等方面的功能可以说是超常的。IDEA是JetBrains公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严...

ubuntu20.04 推荐翻译软件_顾城也说唱的博客-程序员秘密_ubuntu 翻译软件

前言在浏览器的情况下,谷歌浏览器的翻译功能是起到了不少作用。再不济还有谷歌翻译网页版,但是总归还是不够,比如我跑到别的软件上只要不是中文版就跟白痴一样——因为个人英语底子不行,大佬告退。正文尝试过了goldendict 这款软件怎么说了,用的还是有道的api,取词翻译的时候弹出来的窗口就跟缩小的网页一样。。体验一般般吧然后就是老一点的软件stardict,这款软件好像自身版权有问题而且好像也不是正常更新了,好像是作者会提供权限和他人一起维护。这款可以增加离线词典。。但是感觉好像不太靠谱。还有

html5/css编码规范摘录_weixin_30932215的博客-程序员秘密

摘录自bootstrap编码规范1.十六进制值应该全部小写,例如,#fff。在扫描文档时,小写字符易于分辨,因为他们的形式更易于区分。2.减少标签的数量编写 HTML 代码时,尽量避免多余的父元素。很多时候,这需要迭代和重构来实现。请看下面的案例:<!-- Not so great --> <span class="avatar"&gt...

spring配置hibernate的sessionFactory的几种方法_米格战斗机的博客-程序员秘密

1、通过配置dataSource来配置sessionFactory 在src 目录下放入applicationContext.xml中: org.hibernate.dialect.MySQLD

一款比XMIND更好用的思维导图_sunhaojie919的博客-程序员秘密_比xmind好用的思维导图

思维导图软件是创建、管理和交流想法的通用标准形式。它的可视化绘图软件具有直观、友好的用户界面和丰富的功能,可以帮助您有序地组织您的思维、资源和项目过程。而今天,小编来介绍比xmind更好用的思维导图process on通过思维导图,可以随时进行头脑风暴,帮助人们快速整理想法。除了总体思维图的结构外,还可以结构化地显示具体内容,如鱼骨图、二维图、树图、逻辑图、组织图等。在绘制思维图时,人们可以始终保持头脑清晰,掌握总体情况。任何时候的计划或任务。它可以帮助人们学习。提高工作效率。高..

随便推点

五、注册机制_tianlan996的博客-程序员秘密

一 Worker注册1. Worker启动之后,主动向Master进行注册。2. Master在receive方法中接收Worker的注册消息。3. 判断Master是否为Standby节点,如果是,则返回消息 MasterInStandby,结束。否则,继续执行下面的流程。4. 根据Worker的id判断Worker是否已经注册过,注册过的话,返回注册失败:"Duplicate ...

服务器集群的虚拟化网络虚拟化,基于服务器虚拟化的网络GIS集群关键技术研究..._Rayzmoon的博客-程序员秘密

摘要:地理信息系统(GIS)与互联网的结合,拓展了GIS的新领域和新途径,极大的促进了地理空间信息的应用推广,让跨地区和跨行业的空间信息共享更加方便,也使基于地理信息的大众化应用得以快速发展.计算机硬件性能的快速提高,软件领域新构架新算法的推出,以及GIS学科的不断进步,都推动了网络GIS的快速发展.网络GIS应用的不断深入,带来了一些新的问题和挑战,表现有:互联网巨大的用户量和GIS海量数据给网...

Ant技术简介_hoonee1987的博客-程序员秘密_ant技术

Ant技术简介Ant是开源组织Apache的一个项目,是一个基于Java语言的生成工具。当一个项目过于庞大时每次编译、打包都会变得比较复杂,Ant就像一个流程脚本引擎,用于自动化的完成项目的编译、打包。Ant的脚本是基于XML的。Ant环境搭建(Windows)第一步安装JDK,并配置好环境变量。第二步安装Ant:1、下载Ant压缩包文件:htt

斐波拉契数列(进阶)——解题报告_风中的微尘的博客-程序员秘密

题目大意:求斐波拉契数列的第n项,1<=n<=10^9。题目链接:https://www.luogu.com.cn/problem/P1962题目分析:1.很容易知道在10^9的数据范围下无法像传统方式那样,在线性时间递推完成这个过程。所以时间复杂度基本在O(logn)。2.当我们需要快速得到一个数的整数幂时我们有快速幂,那么快速得到多次的和呢?3.我们很容易知道,我们设置前后两个状态:An-1和An,An和An-1+An,所以每一次变化的方式都是恒定的,我们可以用矩阵快速幂来解决这个

cin.get()的用法_veryneo的博客-程序员秘密_cin.get

cin.get有三种形式:1.输入单个字符char str = cin.get() /cin.get(str)上述两种形式等级,用于获取一个字符char ch;ch=cin.get(); //或者cin.get(ch);只能获取一个字符cout<<ch<<endl;举例:输入:a+[Enter]输出:a那么问题来了,如果我输入的是空格,或者回车呢?我的方法是,通过将输入的字符char转换为int,即输出它的ascii码来进行判断char ch;cin.ge

【安全漏洞】从补丁追溯漏洞触发路径_IT老涵的博客-程序员秘密

背景操作系统:ubuntu 18.04 64bit漏洞软件:nginx-1.4.0【查看资料】1. 漏洞补丁信息从补丁可以认识一个漏洞的触发源。查看github中的补丁信息Fixed chunk size parsing. · nginx/[email protected] (github.com)如下:if (ctx->size < 0 || ctx->length < 0) { goto invalid; } return rc;可

推荐文章

热门文章

相关标签