【设计模式深度剖析】【2】【创建型】【工厂方法模式】

👈️上一篇:单例模式    |   下一篇:抽象工厂模式👉️

目录

  • 工厂方法模式
    • 概览
    • 工厂方法模式的定义
      • 英文原话
      • 直译
    • 工厂方法模式的4个角色
      • 抽象工厂(Creator)角色
      • 具体工厂(Concrete Creator)角色
      • 抽象产品(Product)角色
      • 具体产品(Concrete Product)角色
      • 类图
      • 代码示例
        • 抽象工厂Creator.java
        • 具体工厂ConcreteCreator.java
        • 抽象产品Product.java
        • 具体产品ConcreteProduct.java
        • 应用代码ClientFactoryMethodDemo.java
    • 工厂方法模式的应用
      • 工厂方法模式的优点
      • 工厂方法模式的使用场景
    • 工厂方法模式的示例解析
      • 类图
      • 1. 抽象工厂类:FruitGardener.java(果子园接口,生产各类水果的)
      • 2. 具体工厂类:
        • 2.1 具体的某果园:AppleGardener.java (苹果园类,生产苹果)
        • 2.2 具体的某果园:GrapeGardener.java(葡萄园类,生产葡萄)
      • 3. 抽象产品类:Fruit.java(水果接口,抽象的水果类)
      • 4. 具体产品类:
        • 4.1 具体的某类水果:Apple.java(苹果类,是一种具体的水果)
        • 4.2 具体的某类水果:Grape.java(葡萄类,是一种具体的水果)
      • 5. 测试类

工厂方法模式

工厂方法模式(Factory Method Pattern)又叫虚拟构造函数(Virtual Constructor)模式
或者多态性工厂(PolymorphicFactory)模式
。工厂方法模式的用意是定义一个创建产品对象
的工厂接口,将实际创建性工作推迟到子类中

工厂模式可分为简单工厂、工厂方法和抽象工厂模式。

  • 简单工厂模式中一个工厂类处于对产品类实例化的中心位置上,它知道每一个产品,它决定哪一个产品类应当被实例化

这个模式的优点是允许客户端相对独立于产品创建的过程,并且在系统引入新产品的时候无须修改客户端,即在某种程度上支持“开-闭”原则(一个软件实体应当对扩展开放,对修改关闭)。
这个模式的缺点是对 “开-闭” 原则的支持不够,因为如果有新的产品加入到系统中,则需要修改工厂类,将必要的逻辑加入到工厂类中。

  • 工厂方法模式简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,且克服了它的缺点。

首先,在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做。这个核心类则成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪个产品类应当被实例化的细节。此种进一步抽象化的结果,使这种工厂方法模式可以用来允许系统在不修改具体工厂角色的情况下引进新的产品,这一特点使得工厂模式具有超过简单工厂的优越性。

  • 抽象工厂模式是所有形态的工厂模式中最为抽象和最具有一般性的一种形态

    抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构

概览

  • 工厂方法模式的定义

  • 工厂方法模式的4个角色

  • 工厂方法模式的应用

    • 工厂方法模式的优点
    • 工厂方法模式的使用场景
  • 工厂方法模式的示例解析

  • 待办:工厂方法模式的使用场景 第三条/第四条

工厂方法模式的定义

英文原话

Define an interface for creating an object, but let subclasses decide which class to instantiate. FactoryMethod lets a class defer instantiation to subclasses.

直译

意思是:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式的4个角色

抽象工厂(Creator)角色

该角色是工厂方法模式的核心,与应用系统无关,任何在创建对象的工厂类必须实现这个接口。

具体工厂(Concrete Creator)角色

该角色实现了抽象工厂接口,含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。

抽象产品(Product)角色

该角色负责定义产品的共性,实现对产品最抽象的定义。

具体产品(Concrete Product)角色

该角色实现抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。

类图

工厂方法模式类图

代码示例

示例代码

抽象工厂Creator.java
public interface Creator {
  //工厂方法
  //创建一个产品对象,其输入参数类型可自行设置
  public <T extends Product> T factory(Class<T> c);
}
具体工厂ConcreteCreator.java
public class ConcreteCreator implements Creator {

    @Override
    public <T extends Product> T factory(Class<T> c) {
        Product product = null;

        try {
            product= (Product) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
抽象产品Product.java
public interface Product {
    //产品类的公共方法
    public void method1();
    public void method2();
}
具体产品ConcreteProduct.java
public class ConcreteProduct implements Product {

    @Override
    public void method1() {
        //业务逻辑处理代码
        System.out.println("test method1()");
    }

    @Override
    public void method2() {
        //业务逻辑处理代码
    }
}
应用代码ClientFactoryMethodDemo.java
public class ClientFactoryMethodDemo {
    public static void main(String[] args) {
        Creator creator = new ConcreteCreator();
        Product product = creator.factory(ConcreteProduct.class);
        //继续业务处理
        product.method1();
    }
}

[注]:上述通用代码是一个比较实用的、易扩展的框架,可以根据实际项目需要进行扩展应用。

工厂方法模式的应用

工厂方法模式的优点

  1. 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如果一个调用者需要一个具体的产品对象
    只要知道这个产品的类名或约束字符串即可,不用知道创建对象的过程如何,降低了模块间的耦合。
  2. 优秀的可扩展性在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以适应变化
  3. 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,而只需要关心产品的接口,只要接口保持不变,
    系统的上层模块就不需要发生变化
    。因为产品的实例化是由工厂类负责的,具体生产何种产品对象是由不同的工厂类
    决定的。
  4. 工厂方法模式是典型的解耦框架高层模块只需要知道产品的抽象类,其他的实现类都不用关心。
    工厂方法模式符合迪米特法则 (又叫最少知识原则(Least Knowledge Principle,LKP).
    只与你直接的朋友们通信(Only talk to your immediatefriends);
    不要跟“陌生人”说话(Don’t talk to strangers);),也符合依赖倒置原则
    (高层模块不应该依赖低层模块,两者都依赖其抽象;抽象不依赖细节 细节应该依赖于抽象。),只依赖产品类的抽象;
    另外还符合里氏替换原则,可以使用产品子类替换产品父类。

工厂方法模式的使用场景

工厂方法模式在项目中使用得非常频繁,在很多框架的代码中都可以发现工厂方法模式的应用。可使用工厂方法模式的典型场景如下。

  • 工厂方法模式是new一个对象的替代品,因此在所有需要生成对象的地方都可以使用,
    但是需要慎重考虑是否需要增加一个工厂类进行管理,增加代码的复杂度。
  • 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
  • 工厂方法模式可以用在异构项目中,例如,通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围的耦合。
  • 工厂方法模式可以使用在测试驱动开发的框架下。例如,测试一个类 A,就需要将与类A关联的类B也同时产生出来,使用工厂方法模式可以将类B虚拟出来,避免类A与类B的耦合。
  • [注]工厂方法模式还可以与其他模式混合使用(如模板方法模式、单例模式、原型模式等),从而构造出扩展性更好的设计。

工厂方法模式的示例解析

代码示例

通过农场系统演示工厂方法模式的应用(但是工厂方法模式只能针对一个产品等级结构如A产品B产品。但是如果是A产品与B产品的第一等级第二等级,这种情况要抽象工厂模式。)

类图

FuitProductFactory.png

1. 抽象工厂类:FruitGardener.java(果子园接口,生产各类水果的)

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 水果园丁FruitGardener接口是抽象工厂
 *
 * @author Polaris 2024/5/16
 */
public interface FruitGardener {
    Fruit factory();
}

2. 具体工厂类:

2.1 具体的某果园:AppleGardener.java (苹果园类,生产苹果)

该具体工厂-苹果园工厂,封装了实例化具体产品-苹果对象的过程

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 苹果园丁AppleGardener实现FruitGardener接口,是一个用于生产苹果的具体工厂
 *
 * @author Polaris 2024/5/16
 */
public class AppleGardener implements FruitGardener {

    @Override
    public Fruit factory() {
        return new Apple();
    }
}
2.2 具体的某果园:GrapeGardener.java(葡萄园类,生产葡萄)

该具体工厂-葡萄园工厂,封装了实例化具体产品-葡萄对象的过程

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 葡萄园丁GrapeGardener实现FruitGardener接口,是一个用于生产葡萄的具体工厂
 *
 * @author Polaris 2024/5/16
 */
public class GrapeGardener implements FruitGardener {

    @Override
    public Fruit factory() {
        return new Grape();
    }
}

3. 抽象产品类:Fruit.java(水果接口,抽象的水果类)

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 水果Fruit接口是抽象产品
 *
 * @author Polaris 2024/5/16
 */
public interface Fruit {
    //生长
    void grow();

    //收获
    void harvest();

    //栽种
    void plant();
}

4. 具体产品类:

4.1 具体的某类水果:Apple.java(苹果类,是一种具体的水果)

由于苹果是一类具体的水果类,因此它对抽象的水果类进行了实现,当然同时它也有独自属于它自己的方法。

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 苹果Apple实现Fruit接口,是一个具体产品
 *
 * @author Polaris 2024/5/16
 */
public class Apple implements Fruit {

    private int treeAge;

    public int getTreeAge() {
        return treeAge;
    }

    public void setTreeAge(int treeAge) {
        this.treeAge = treeAge;
    }

    @Override
    public void grow() {
        System.out.println("苹果树在生长");
    }

    @Override
    public void harvest() {
        System.out.println("收获苹果");
    }

    @Override
    public void plant() {
        System.out.println("栽种苹果");
    }
}
4.2 具体的某类水果:Grape.java(葡萄类,是一种具体的水果)

由于葡萄是一类具体的水果类,因此它对抽象的水果类进行了实现,当然同时它也有独自属于它自己的方法。

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 葡萄Grape实现Fruit接口,是一个具体产品
 *
 * @author Polaris 2024/5/16
 */
public class Grape implements Fruit {

    private boolean seedless;
    public boolean isSeedless(){
        return seedless;
    }
    public void setSeedless(boolean seedless){
        this.seedless=seedless;
    }
    @Override
    public void grow() {
        System.out.println("葡萄正在生长...");
    }

    @Override
    public void harvest() {
        System.out.println("收获葡萄");
    }

    @Override
    public void plant() {
        System.out.println("栽种葡萄");
    }
}

5. 测试类

具体某类水果工厂来生产对应的某水果对象,

该水果对象是某具体水果类的实例,

而该具体水果实例实现了抽象的水果类,

里氏替换原则,父类的引用可以指向子类实例,因为子类类型一定是父类类型(或者实现的接口的类型)

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 下面是一个应用场景代码
 *
 * @author Polaris 2024/5/16
 */
public class ClientDemo {
    public static void main(String[] args) {
        //苹果园丁工厂
        FruitGardener fruitGardener = new AppleGardener();
        //通过工厂生产苹果
        Fruit apple = fruitGardener.factory();
        apple.plant();
        apple.grow();
        apple.harvest();
        fruitGardener = new GrapeGardener();
        //通过工厂生产葡萄
        Fruit grape = fruitGardener.factory();
        grape.plant();
        grape.grow();
        grape.harvest();
    }
}

👈️上一篇:单例模式    |   下一篇:抽象工厂模式👉️

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

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

相关文章

Celery教程

一、什么是Celery 1.1、celery是什么 Celery是一个简单、灵活且可靠的&#xff0c;处理大量消息的分布式系统&#xff0c;专注于实时处理的异步任务队列&#xff0c;同时也支持任务调度。 Celery的架构由三部分组成&#xff0c;消息中间件&#xff08;message broker&#x…

从零开始学Vue3--环境搭建

1.搭建环境 下载nodejs nodejs下载地址 更新npm npm install -g npm 设置npm源&#xff0c;加快下载速度 npm config set registry https://registry.npmmirror.com 使用脚手架创建项目 npm create vuelatest 根据你的需要选择对应选项 进入新建的项目下载依赖 npm in…

大模型时代,掌握Event Stream技术提升Web响应速度

大模型时代,每天搜索都可能会用到一种或多种大模型,在大文本输出的时候,页面是一字一字,一段一段的慢慢输出出来的,这背后是如何实现的呢?我们以KIMI为例 先抓个请求 我们发现界面展示是一句话,但是接口返回的时候是一个字一个字的。 普通请求 多了Event Stream的处理 …

机器人非线性控制方法——线性化与解耦

机器人非线性控制方法是针对具有非线性特性的机器人系统所设计的一系列控制策略。其中&#xff0c;精确线性化控制和反演控制是两种重要的方法。 1. 非线性反馈控制 该控制律采用非线性反馈控制的方法&#xff0c;将控制输入 u 分解为两个部分&#xff1a; α(x): 这是一个与…

更新web文件40秒后生效

服务器web服务使用的是nginx。 经测试&#xff0c;上传文件后大约40秒后生效。 更新文件不立即生效。 网上资料说根nginx中sendfile选项有关。 在nginx配置文件中&#xff0c;http区域里将sedfile设置为off&#xff0c;重启nginx服务。 谷歌浏览器强制刷新一次&#xff0c;…

用ControlNet+Inpaint实现stable diffusion模特换衣

用ControlNetInpaint实现stable diffusion模特换衣 ControlNet 训练与架构详解ControlNet 的架构用于文本到图像扩散的 ControlNet训练过程Zero卷积层的作用解释 inpaintInpaint Anything 的重要性Inpaint Anything 的功能概述 在现代计算机视觉领域&#xff0c;稳定扩散&#…

【计算机视觉(3)】

基于Python的OpenCV基础入门——图形与文字的绘制 图形与文字的绘制&#xff1a;画线画矩形画圆画多边形加文字 图形与文字绘制的代码实现&#xff1a; 图形与文字的绘制&#xff1a; 画线 img cv2.line(img, pt1, pt2, color, thickness) 参数&#xff1a; img&#xff1a;…

Android 构建时:Manifest merger failed : Attribute application@name value

在AndroidStudio 构建时发现此问题&#xff1a; Manifest merger failed : Attribute applicationname value解决方案&#xff1a;在主Manifest中增加replace <applicationandroid:name".MyApp"android:allowBackup"false"tools:replace"android…

儿童卧室灯品牌该如何挑选?几款专业儿童卧室灯品牌分享

近视在儿童中愈发普遍&#xff0c;许多家长开始认识到&#xff0c;除了学业成绩之外&#xff0c;孩子的视力健康同样重要。毕竟&#xff0c;学业的落后可以逐渐弥补&#xff0c;而一旦孩子近视&#xff0c;眼镜便可能成为长期伴随。因此&#xff0c;专业的护眼台灯对于每个家庭…

工作站虚拟化:RTX A5000的图形工作站实现多用户独立运行Siemens NX 设计软件

一、背景 Siemens NX 是由西门子数字工业软件&#xff08;Siemens Digital Industries Software&#xff09;开发的一款先进的集成计算机辅助设计&#xff08;CAD&#xff09;、计算机辅助制造&#xff08;CAM&#xff09;和计算机辅助工程&#xff08;CAE&#xff09;软件。它…

Python代码实现代价函数

最小二乘法 最小二乘法是一种在统计学、数学、工程学和计算机科学等领域广泛使用的优化方法。 基本原理 最小二乘法的主要目的是找到一组模型参数&#xff0c;使得根据这些参数所预测的数据与实际观测数据之间的差异&#xff08;即残差&#xff09;的平方和最小。 数学表达…

【LeetCode刷题】三数之和、四数之和

【LeetCode刷题】Day 6 题目1&#xff1a;LCR 7.三数之和思路分析&#xff1a;思路1&#xff1a;排序暴力枚举set去重思路2&#xff1a;单调性双指针细节处理去重 题目2&#xff1a;18.四数之和思路分析&#xff1a;思路1&#xff1a;排序暴力枚举set去重思路2&#xff1a;单调…

浅析智能体开发(第二部分):智能体设计模式和软件架构

大语言模型&#xff08;LLM&#xff09;驱动的智能体&#xff08;AI Agent&#xff09;展现出许多传统软件所不具备的特征。不仅与传统软件的设计理念、方法、工具和技术栈有显著的差异&#xff0c;AI原生&#xff08;AI Native&#xff09;的智能体还融入了多种新概念和技术。…

SparkSQL入门

1、SparkSQL是什么&#xff1f; 结论&#xff1a;SparkSQL 是一个即支持 SQL 又支持命令式数据处理的工具 2、SparkSQL 的适用场景&#xff1f; 结论&#xff1a;SparkSQL 适用于处理结构化数据的场景&#xff0c;而Spark 的 RDD 主要用于处理 非结构化数据 和 半结构化数据 …

【撸源码】【ThreadPoolExecutor】线程池的工作原理深度解析——上篇

1. 前言 线程池这块&#xff0c;作为高频面试题&#xff0c;并且实际使用场景巨多&#xff0c;所以出了这篇文章&#xff0c;一块来研究一下线程池的实现原理&#xff0c;运行机制&#xff0c;从底层深挖&#xff0c;不再局限于面试题。 2. 线程池概览 2.1. 构造器 线程池总…

Leecode热题100---55:跳跃游戏(贪心算法)

题目&#xff1a; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 贪心算…

数据采集与AI分析,亮数据+通义千问助力跨境电商前行

文章目录 前言工具介绍数据采集工具亮数据Web Scraper IDE亮点 AI数据分析工具 实战电商数据采集与AI分析电商平台选取数据采集完全托管数据集自定义数据集 AI分析 价格总结 前言 随着信息技术的飞速发展&#xff0c;数据采集与AI分析在跨境电商中扮演着越来越重要的角色。通过…

Langchain:数据连接封装、缓存封装和LCEL学习和探索

&#x1f335; 目录 &#x1f335; &#x1f60b; 数据连接封装 &#x1f354; 文档加载器&#xff1a;Document Loaders 文档处理器&#xff1a;TextSplitter 向量数据库与向量检索 总结 &#x1f349; 缓存封装&#xff1a;Memory &#x1f3d6;️ 对话上下文&#xf…

urllib_post请求_百度翻译

打开百度翻译&#xff0c;并打开控制台&#xff0c;输入spider&#xff0c;然后在网络中找到对应的接口&#xff0c;可以看出&#xff0c;该url是post请求 在此案例中找到的接口为sug&#xff0c;依据为&#xff1a; 可以看到&#xff0c;传递的数据为kw : XXX&#xff0c; 所…

Hadoop3:HDFS的Fsimage和Edits文件介绍

一、概念 Fsimage文件&#xff1a;HDFS文件系统元数据的一个永久性的检查点&#xff0c;其中包含HDFS文件系统的所有目 录和文件inode的序列化信息。 Edits文件&#xff1a;存放HDFS文件系统的所有更新操作的路径&#xff0c;文件系统客户端执行的所有写操作首先 会被记录到Ed…