【设计模式】之工厂模式(三种)

系列文章目录

  • 【设计模式】之单例模式

  • 【设计模式】之模板方法模式

  • 【设计模式】之责任链模式

  • 【设计模式】之策略模式

前言

今天给大家介绍23种设计模式中的工厂模式,学过Spring的小伙伴应该不陌生,今天给大家详细介绍一下它。🌈

注意:文章若有错误的地方,欢迎评论区里面指正 🍭 


1、工厂模式的分类

工厂模式可以分为三类:

  • 简单工厂模式(Simple Factory)

  • 工厂方法模式(Factory Method)

  • 抽象工厂模式(Abstract Factory)

2、什么是工厂模式

        工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,使得代码更加灵活可维护可扩展。在工厂模式中,对象的创建不再由调用者直接负责,而是通过一个专门的工厂类来负责创建。这样,调用者就无需关心对象的创建细节,只需向工厂类请求所需的对象即可。

3、工厂模式的角色

  1. 产品(Product):定义了产品的接口或抽象类,描述了产品的主要功能和特性。
  2. 具体产品(Concrete Product):实现了产品接口或继承了产品抽象类的具体类,是工厂类创建的目标。
  3. 工厂(Factory):负责创建产品的工厂接口或抽象类,其中包含了创建产品的方法。
  4. 具体工厂(Concrete Factory):实现了工厂接口或继承了工厂抽象类的具体类,负责具体创建产品的实例。

4、工厂模式的优点在哪里

  • 解耦:将对象的创建与使用分离,降低了代码之间的耦合度。
  • 灵活性:可以很容易地创建不同的产品实例,只需修改工厂类的实现即可。
  • 可扩展性:当需要添加新产品时,只需添加新的具体工厂和具体产品类,无需修改原有代码。
  • 易于维护:将对象的创建封装在工厂类中,使得代码更加清晰和易于维护。

一、简单工厂模式

1、简单工厂模式的角色

简单工厂的角色如下:

抽象产品定义了产品的接口或抽象类,描述了产品的主要功能和特性。它是所有具体产品的基类,为它们提供了统一的接口
具体产品实现了产品接口或继承了产品抽象类的具体类,是简单工厂模式创建的目标
简单工厂负责创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。

2、简单工厂模式的示例

定义一个抽象的手机产品Phone

public interface Phone {
    void showBrand();
}

定义具体手机产品HuaWeiThreeStarApple

public class HuaWei implements Phone{
    @Override
    public void showBrand() {
        System.out.println("这是一个华为手机。");
    }
}


public class ThreeStar implements Phone{
    @Override
    public void showBrand() {
        System.out.println("这是一个三星的手机。");
    }
}

public class Apple implements Phone{
    @Override
    public void showBrand() {
        System.out.println("这是一个苹果手机。");
    }
}

定义手机工厂PhoneFactory

public class PhoneFactory {
    public static Phone showPhone(String brandName){
        if ("HuaWei".equals(brandName)){
            return new HuaWei();
        }
        if ("ThreeStar".equals(brandName)){
            return new ThreeStar();
        }
        if ("Apple".equals(brandName)){
            return new Apple();
        }
        return null;
    }
}

测试:

public class PhoneTest {
    public static void main(String[] args) {
        Phone huaWei = PhoneFactory.showPhone("HuaWei");
        if (null != huaWei) huaWei.showBrand();
        else System.out.println("无法生成该品牌的手机。");

    }
}
//测试结果:
    /*
        这是一个华为手机。    
    */

在简单工厂模式中,抽象产品既可以是各个具体产品类实现的共同的接口,也可以是各个具体产品类继承的抽象父类。

3、简单工厂模式使用场景

  1. 创建对象不涉及复杂的逻辑:当创建对象时,不需要考虑太多的逻辑和条件判断,只是简单地根据传入的参数返回对应类型的实例时,可以使用简单工厂模式。

  2. 减少客户端代码对类的依赖:在客户端代码中,如果直接依赖于多个具体的类,会导致代码变得复杂且难以维护。使用简单工厂模式,客户端只需要依赖于工厂接口,从而降低了代码之间的耦合度。

  3. 需要动态地选择具体的类:在某些情况下,客户端可能需要根据运行时的情况动态地选择创建哪个类的实例。简单工厂模式可以根据传入的参数或条件来选择并创建相应的对象。

  4. 创建对象需要消耗较多资源:如果创建对象需要消耗较多的资源或执行复杂的初始化操作,使用简单工厂模式可以将这些操作封装在工厂类中,从而简化了客户端代码,并提高了代码的可读性和可维护性。

  5. 创建对象需要遵守一些约束或规则:在某些情况下,创建对象需要遵守一些约束或规则,例如,需要确保在单例模式下只创建一个实例,或者在创建对象前需要进行一些验证操作。简单工厂模式可以在工厂类中实现这些约束或规则,从而确保创建的对象符合预期的要求。

4、简单工厂模式总结

优点:

        简单工厂模式通过把对象的创建过程封装在简单工厂类中,使得客户端与具体产品类解耦,提高了系统的灵活性和可扩展性

缺点:

        当需要添加新产品时,需要修改简单工厂类的代码,这违反了开闭原则(对扩展开放,对修改关闭)

二、工厂方法模式

工厂方法模式:它定义了一个用于创建对象的接口,让子类决定实例化哪一个类。

1、工厂方法模式的角色:

抽象产品这是定义产品的接口,是工厂方法模式所创建的对象的超类型,也就是产品对象的公共父类。
具体产品它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
抽象工厂在抽象工厂类中声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
具体工厂它是抽象工厂的实现类,实现了在抽象工厂中声明的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

2、工厂方法模式的示例

抽象工厂PhoneFactory 

public interface PhoneFactory {
    Phone CreatePhone();
}

抽象产品Phone 

public interface Phone {
    void showBrand();
}

具体产品工厂HuaWeiFactory、ThreeStarFactory、AppleFactory 

public class HuaWeiFactory implements PhoneFactory{
    @Override
    public Phone CreatePhone() {
        System.out.println("这是华为手机工厂,这里只生产华为手机。");
        return new HuaWei();
    }
}

public class ThreeStarFactory implements PhoneFactory{
    @Override
    public Phone CreatePhone() {
        System.out.println("这是三星手机工厂,这里只生产三星手机。");
        return new ThreeStar();
    }
}

public class AppleFactory implements PhoneFactory{
    @Override
    public Phone CreatePhone() {
        System.out.println("这是苹果手机工厂,这里只生产苹果手机。");
        return new Apple();
    }
}

具体产品跟简单工厂的一样,还是HuaWei、ThreeStar、Apple那三个。

测试:

public class test {
    public static void main(String[] args) {
        HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
        Phone phone = huaWeiFactory.CreatePhone();
        phone.showBrand();
    }
}
//测试结果:
    /*
    这是华为手机工厂,这里只生产华为手机。
    这是一个华为手机。
     */

3、工厂方法模式使用场景

  • 重复代码:当创建对象需要使用大量重复的代码时,工厂方法模式可以通过定义一个单独的创建实例对象的方法,来解决这个问题。
  • 不关心创建过程:如果客户端不依赖产品类,不关心实例如何被创建、实现等细节,那么工厂方法模式是一个好选择。客户端只需要知道所对应的工厂,而具体的产品对象,由对应的工厂创建。
  • 子类指定创建对象:当一个类希望由子类来指定它所创建的对象时,工厂方法模式也是一个不错的选择。
  • 需要提供默认值和允许自定义值:如果一个类需要提供一些默认值,同时允许用户自定义这些值,工厂方法模式同样适用。

4、工厂方法模式总结

优点

  1. 解耦:使用工厂方法可以让用户的代码和某个特定类的子类的代码解耦。
  2. 无需关心创建细节:用户不必知道它使用的对象是怎么样被创建的,只需知道该对象有哪些方法即可。
  3. 符合“开放-封闭”原则:工厂方法模式改变了我们直接用new创建对象的方式,使得在添加新产品时,只是扩展的变化,而不是修改的变化,这完全符合“开放-封闭”原则。

缺点

  1. 增加类的数量:随着具体产品数量的增加,工厂类的数量也会增加,导致类的数量增多。
  2. 增加系统复杂度:由于需要定义多个工厂类,所以增加了系统的复杂度。对于系统的设计和维护都会带来很多困扰。
  3. 增加代码量:工厂方法模式需要定义抽象工厂类和抽象产品类,并且每个具体产品都需要一个对应的具体工厂类和具体产品类。这导致了代码的数量增加,增加了开发量和维护成本。
  4. 违背开闭原则:在添加新产品时,需要新增具体产品类和对应的具体工厂类。这违背了开闭原则,对于已有代码的修改会对系统稳定性造成一定的影响,需要进行大量的修改和测试。
  5. 运行效率低:由于工厂方法模式每次都需要通过具体工厂类来创建具体产品类的实例,这就需要实例化具体工厂类和具体产品类,可能导致运行效率较低。

三、抽象工厂模式

1、抽象工厂模式角色

抽象工厂声明创建抽象产品对象的一个接口(有几个产品组,则声明几个方法。比如对于上述的场景,需要声明一个用于生产篮球类产品的方法,还需要声明一个用于生产足球类产品的方法)
具体工厂实现了抽象工厂接口,负责实例化具体产品类。每个具体工厂都对应一组相互关联或相互依赖的产品对象。
抽象产品定义了产品的接口,是工厂方法所创建对象的超类型,或者是所创建对象的接口。
具体产品实现了抽象产品接口,是某种类型的具体产品对象

2、抽象工厂模式示例

抽象工厂:

public interface AbstractFactory {
    PhoneFactory CreateApplePhone();
    BookFactory CreateBookPhone();
}

抽象产品:

public interface Book {
    void showDetail();
}
public interface Phone {
    void showDetail();
}

具体工厂:

//AppleFactory
public class AppleFactory implements AbstractFactory{
    @Override
    public PhoneFactory CreateApplePhone() {
        return new Iphone15Phone();
    }

    @Override
    public BookFactory CreateBookPhone() {
        return new  M3ProBook();
    }
}
//AppleFactory
public class HuaWeiFactory implements AbstractFactory{
    @Override
    public Phone CreateApplePhone() {
        return new HuaWeiP60Phone();
    }

    @Override
    public Book CreateBookPhone() {
        return new  MateBookXProBook();
    }
}

具体产品:

//Phone
public class HuaWeiP60Phone implements Phone {

    @Override
    public void showDetail() {
        System.out.println("这是华为P60手机。");
    }
}

public class Iphone15Phone implements Phone {
    @Override
    public void showDetail() {
        System.out.println("这是苹果15手机。");
    }
}

//Book
public class MateBookXProBook implements Book {
    @Override
    public void showDetail() {
        System.out.println("这是华为MateBook X Pro笔记本。");
    }
}

public class M3ProBook implements Book {
    @Override
    public void showDetail() {
        System.out.println("这MacBook Pro M3 Pro笔记本电脑。");
    }
}

测试:

public class client {
    public static void main(String[] args) {
        HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
        huaWeiFactory.CreateApplePhone().showDetail();
        huaWeiFactory.CreateBookPhone().showDetail();
        System.out.println("===========================");
        AppleFactory appleFactory = new AppleFactory();
        appleFactory.CreateApplePhone().showDetail();
        appleFactory.CreateBookPhone().showDetail();
    }
}

/*
这是华为P60手机。
这是华为MateBook X Pro笔记本。
===========================
这是苹果15手机。
这MacBook Pro M3 Pro笔记本电脑。
*/

3、抽象工厂模式使用场景

  • 系统独立于产品创建、组合和表示:当系统需要独立于它的产品的创建、组合和表示时,可以使用抽象工厂模式。
  • 多个产品系列配置:如果系统需要由多个产品系列中的一个来配置,那么抽象工厂模式可以很好地满足这个需求。
  • 强调产品对象联合使用:当需要强调一系列相关的产品对象的设计以便进行联合使用时,抽象工厂模式非常适用。
  • 只想显示产品接口:如果只想提供一个产品类库,而只想显示它们的接口而不是实现时,抽象工厂模式是一个很好的选择。

4、抽象工厂模式总结

优点

  1. 封装性:抽象工厂模式将一组具有共同主题的单个工厂封装在一起,客户端只需要知道抽象工厂和产品族的抽象类型,而无需关心具体的实现细节。

  2. 产品族内的约束:抽象工厂模式有助于确保产品族内对象之间的兼容性。由于一个具体工厂负责创建一组产品,因此这组产品之间能够相互协作。

  3. 易于交换产品族:客户端代码只需与抽象工厂接口交互,因此可以轻松地更改产品族,而无需修改客户端代码。

  4. 可扩展性:当需要添加新的产品族时,只需添加新的具体工厂类,并在其中实现新的产品创建逻辑,而无需修改已有的代码。

  5. 降低耦合度:通过抽象工厂模式,客户端代码与具体产品类之间的耦合度被降低了,因为客户端代码只与抽象工厂接口交互。

缺点

  1. 难以支持新种类的产品:抽象工厂模式难以支持增加新的产品种类,因为每增加一个新的产品种类,就需要修改抽象工厂接口以及所有具体工厂类,这可能会违反开闭原则(对扩展开放,对修改关闭)。

  2. 系统复杂性增加:抽象工厂模式增加了系统的抽象性和复杂性,因为需要定义更多的接口和类。这可能会使得系统的理解和维护变得更加困难。

  3. 难以维护:如果产品族中的产品数量过多,或者产品之间的关联关系复杂,那么抽象工厂模式的实现和维护可能会变得非常困难。

  4. 客户端可能需要知道太多:在某些情况下,客户端可能需要知道抽象工厂的具体类型,以便能够创建正确的产品族。这可能会增加客户端代码的复杂性。

  5. 可能导致代码冗余:如果不同的产品族之间存在许多相似的产品,那么可能会导致在多个具体工厂类中出现相似的代码,从而产生代码冗余。


总结

好了,工厂模式就介绍到这里,我们下期见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/591459.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++第七课-string用法】

这里写自定义目录标题 string的初步介绍sring的构造函数string的构造函数-重点掌握无参的构造函数用常量字符串来初始化拷贝构造 string的构造函数-非重点掌握拷贝字符串str从pos位置开始的len个字符拷贝字符串s的前n个字符用n个c去初始化 string的赋值string的遍历和访问下标[…

docker如何生成springboot镜像

1、在springboot的jar包所在的目录下创建Dockerfile文件,此案例的目录为/usr/java Dockerfile的文件内容如下: FROM openjdk:8 LABEL author"zengyanhui" LABEL email"1181159889qq.com" WORKDIR /usr/java/springbootdemo COPY s…

TCP的特性(4)

TCP特性 拥塞控制(可靠性机制)延迟应答(效率机制)捎带应答(效率机制)面向字节流(粘包问题)TCP异常机制小结 拥塞控制(可靠性机制) 虽然TCP引入了滑动窗口,能够高效可靠的传输大量数据,但是在开始阶段就发送大量数据,可能引起一系列问题. TCP引入了慢启动机制,先发少量的数据,判…

PDF Shaper Ultimate 免安装中文破姐版 v14.1

软件介绍 PDF Shaper是一套完整的多功能PDF编辑工具,可实现最高的生产力和文档安全性。它允许你分割,合并,水印,署名,优化,转换,加密和解密您的PDF文件,也可插入和移动页&#xff0…

RabbitMQ知识点总结和复习

之前项目中用到RabbitMQ的场景主要是订单信息的传递,还有就是利用RabbitMQ的死信队列属性设置,实现延迟队列效果,实现超时支付取消功能,以及在两个不同项目中传递数据等场景。 最近几年的工作中都是一直用的RabbitMQ,…

【C++】深入剖析C++11 initializer_list 新的类功能 可变模板参数

目录 一、std::initializer_list 1、std::initializer_list是什么类型 2、std::initializer_list 的应用场景 ①给自定义容器赋值 ② 传递同类型的数据集合 二、新的类功能 1、默认成员函数 2、关键字default 3、关键字delete 三、可变参数模板 一、std::initialize…

关于Linux的“三十年河东,三十年河西”的思考

目录 一、何为“三十年河东,三十年河西”? 二、Linux系统的发展历程简介 三、Linux家族 四、Linux发展分支 五、关于对Linux发展的回顾 一、何为“三十年河东,三十年河西”? “三十年河东,三十年河西”原义是三十…

OpenWRT有线桥接部署教程

前言 之前咱们讲到OpenWRT部署WAN实现PPPoE拨号上网和自动获取IP模式上网的办法: OpenWRT设置PPPoE拨号教程 OpenWRT设置自动获取IP,作为二级路由器 这一次,咱们尝试用OpenWRT有线桥接上一级路由器的教程。 可能有小伙伴敏锐地发现了&am…

20232803 2023-2024-2 《网络攻防实践》实践八报告

目录 1. 实践内容2. 实践过程2.1 动手实践任务一2.2 动手实践任务二:分析Crackme程序2.2.1 crackme1.exe2.2.2 crackme2.exe 2.3 分析实践任务一2.4 分析实践任务二 3. 学习中遇到的问题及解决4. 学习感悟、思考等 1. 实践内容 动手实践任务一:对提供的r…

【Python编程实践1/3】模块

目录 目标 模块 import ​编辑 代码小结 题目 from...import 随机模块 代码小结 randint函数 骰子大战 choice函数 总结 目标 拧一颗螺丝,只会用到螺丝刀;但是修一台汽车,需要一整套汽修的工具。函数就像螺丝刀,可以帮…

Go实战训练之Web Server 与路由树

Server & 路由树 Server Web 核心 对于一个 Web 框架,至少要提供三个抽象: Server:代表服务器的抽象Context:表示上下文的抽象路由树 Server 从特性上来说,至少要提供三部分功能: 生命周期控制&…

FIFO Generate IP核使用——Native读写接口信号详解

Native FIFO接口信号是用于FIFO IP核与外部电路进行通信的信号。当FIFO支持独立的写和读时钟时,这些信号可以包括标准端口和可选端口。 1 当FIFO具有独立时钟时的接口信号 当FIFO具有独立的时钟时,其接口信号会相应地有所变化。特别是关于复位信号rst…

Hibernate入门学习

目录 1、ORM思想概述 2、自定义ORM框架 3、第一个Hibernate程序开发步骤(重要) 1)下载完整包 2)创建项目,导入所需jar包 3)建立student表 4)创建和student表对应的Student实体类 5&…

postman中百度preview无法加载的解决方案

问题 在使用postman关联时,百度接口与天气接口已使用glb_city关联,但在百度接口发送请求时,发现preview无法加载 解决方案 1、进入百度 百度全球领先的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中…

基于Springboot的民航网上订票系统(有报告)。Javaee项目,springboot项目。

演示视频: 基于Springboot的民航网上订票系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构…

vue3 + ts 快速入门(全)

文章目录 学习链接1. Vue3简介1.1. 性能的提升1.2.源码的升级1.3. 拥抱TypeScript1.4. 新的特性 2. 创建Vue3工程2.1. 基于 vue-cli 创建2.2. 基于 vite 创建(推荐)vite介绍创建步骤项目结构安装插件项目结构总结 2.3. 一个简单的效果Person.vueApp.vue …

11个2024年热门的AI编码助手

大家好,人工智能(AI)领域的大型语言模型(LLMs)已经逐渐发展成熟,并且深入到了我们日常的工作当中。在众多AI应用中,编码助手尤为突出,是开发人员编写更高效、准确无误代码的必备辅助…

docker原理

Docker原理 在前面我们学习了Docker,接下来我们探究一下Docker的底层技术原理 Linux 命名空间(namespace)、控制组(cgroups)和 联合文件系统(UnionFS) 三大技术支撑了目前 Docker 的实现&…

STM32入门学习之DMA

1.直接存储访问DMA(Direct Memory Access):DMA传输不需要CPU的参与,直接在内存和I/O设备间开辟了一条新的数据传输通道,不仅提高数据传输的速率,还因为不需要CPU的干预,从而提高了CPU的利用率。(注:文中的资…

OpenCV如何在图像中寻找轮廓(60)

返回:OpenCV系列文章目录(持续更新中......) 上一篇:OpenCV如何模板匹配(59) 下一篇 :OpenCV检测凸包(61) 目标 在本教程中,您将学习如何: 使用 OpenCV 函数 cv::findContours使用 OpenCV 函数 cv::d rawContours …
最新文章