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

news2024/11/24 22:46:47

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

目录

  • 工厂方法模式
    • 概览
    • 工厂方法模式的定义
      • 英文原话
      • 直译
    • 工厂方法模式的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();
    }
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2001-2022年全国31省份互联网发展47个指标合集各省电信业务信息化软件信息技术服务业

全国31省份互联网发展47个指标合集各省电信业务信息化软件信息技术服务业&#xff08;2001-2022年&#xff09;插值填补无缺失 整理了各省电信业务、从业人员、电信通信、互联网发展、企业信息化、软件和信息技术服务业等47个互联网主要发展指标&#xff0c;内含原始数据、线性…

Web前端一套全部清晰 ⑨ day5 CSS.4 标准流、浮动、Flex布局

我走我的路&#xff0c;有人拦也走&#xff0c;没人陪也走 —— 24.5.24 一、标准流 标准流也叫文档流&#xff0c;指的是标签在页面中默认的排布规则&#xff0c;例如:块元素独占一行&#xff0c;行内元素可以一行显示多个。 二、浮动 作用: 让块级元素水平排列。 属性名:floa…

LeetCode1161最大内层元素和

题目描述 给你一个二叉树的根节点 root。设根节点位于二叉树的第 1 层&#xff0c;而根节点的子节点位于第 2 层&#xff0c;依此类推。请返回层内元素之和 最大 的那几层&#xff08;可能只有一层&#xff09;的层号&#xff0c;并返回其中 最小 的那个。 解析 在上一题&…

微信小程序报错:notifyBLECharacteristicValueChange:fail:nodescriptor的解决办法

文章目录 一、发现问题二、分析问题二、解决问题 一、发现问题 微信小程序报错&#xff1a;notifyBLECharacteristicValueChange:fail:nodescriptor 二、分析问题 这个提示有点问题&#xff0c;应该是该Characteristic的Descriptor有问题&#xff0c;而不能说nodescriptor。 …

【传知代码】Modnet 人像抠图-论文复现

文章目录 概述原理介绍核心逻辑ModNet 的结构 环境配置WebUI 小结 论文地址 论文GitHub 本文涉及的源码可从Modnet 人像抠图该文章下方附件获取 概述 人像抠图技术在多个领域有着广泛的应用场景&#xff0c;包括但不限于&#xff1a; 展馆互动拍照&#xff1a;展馆中使用的抠…

二叉树的递归实现及例题

目录 遍历方式 示例 原理 前序遍历示例 二叉树的节点个数 原理 层序遍历 原理 这样做的目的是 判断完全二叉树 例题 ​编辑 思路 代码 遍历方式 二叉树的遍历方式可分为&#xff1a; 前序遍历&#xff1a;先访问根&#xff0c;访问左子树&#xff0c;在访问右子…

2024.05.24 学习记录

1、面经复习&#xff1a; js基础、知识深度、js垃圾回收 2、代码随想录刷题&#xff1a;动态规划 完全背包 all 3、rosebush 完成 Tabs、Icon、Transition组件

C++中获取int最大与最小值

不知道大家有没有遇到过这种要求&#xff1a;“返回值必须是int&#xff0c;如果整数数超过 32 位有符号整数范围 [−2^31, 2^31 − 1] &#xff0c;需要截断这个整数&#xff0c;使其保持在这个范围内。例如&#xff0c;小于 −2^31 的整数应该被固定为 −2^31 &#xff0c;大…

【Ubuntu查看硬盘和网络配置信息】解决方案

1.查看硬盘序列号 搜索系统中有应用&#xff0c;打开“磁盘”&#xff0c;因为我本人是只设置了一个分区&#xff0c;所以打开如下&#xff0c;从上面可以得到硬盘序列号 2.查看ip地址和MAC地址 ctrl alt t 打开命令行 输入ifconfig, 如下图所示&#xff0c;MAC地址即为&…

多家知名媒体到访“光子1号金融算力中心“ 交流AI与算力未来观

5月23日&#xff0c;企商在线 “光子1号金融算力中心媒体参观日”活动成功举办&#xff0c;十多家主流媒体、IT行业媒体及自媒体代表走进光子1号金融算力中心&#xff0c;深入了解企商业务发展、战略规划及“光子1号金融算力中心”等企商打造的新型数字基础设施&#xff0c;共同…

论文研读之hpn-ssh: High speed bulk data transfer using the SSH protocol

优化解决方案的策略 其中提到盲目的增加窗口大小实际上可能导致性能下降,这种下降可能由这几个因素引起:中间路由器上的缓冲区填满;路径过快饱和;过度缓冲;引入新的问题。虽然较大的缓冲区在某些情况下可能提高性能,但也有引入新的、难以诊断的问题的风险。这种复杂性使…

QtCreator,动态曲线实例

样式图&#xff1a; .ui 在sloem1.ui文件中&#xff0c;拖入一个label控件&#xff0c; 头文件.h #include "QtGui/QPainter.h" #include "QtCore/QTimer.h"protected:bool eventFilter(QObject *obj,QEvent *event);void labelPaint();public slots: /…

6款网站登录页(附带源码)

6款网站登录页 效果图及部分源码123456 领取源码下期更新预报 效果图及部分源码 1 部分源码 <style>* {margin: 0;padding: 0;}html {height: 100%;}body {height: 100%;}.container {height: 100%;background-image: linear-gradient(to right, #fbc2eb, #a6c1ee);}.l…

NSS‘题目练习3

[SWPUCTF 2021 新生赛]easyupload3.0 打开题目发现要求上传.jpg文件 先上传抓包&#xff0c;尝试更改后缀 换一种形式 文件头绕过 都试过之后尝试上传.htaccess文件&#xff0c;发现上传成功 会将之后上传的文件后缀自动更名为.php 再上传.jpg文件 蚁剑连接找到flag [SWPUCTF …

【oracle的安装记录】

oracle安装记录 一、下载以后&#xff0c;解压到同一路径下面 二、双击可执行安装文件&#xff0c;等待文件加载 三、双击以后&#xff0c;弹出信息 四、提示该窗口&#xff0c;点击【是】即可 五、未填写配置安全更新信息 六、弹出小窗口&#xff0c;选择【是】 七、安装选项…

宿舍管理系统--毕业设计

毕业设计&#x1f4bc;MD5加密&#x1f512;SSM框架&#x1f3a8;Layui框架&#x1f384; 实现功能 管理员的登录与登出 管理员,班级,学生,宿舍&#xff0c;卫生&#xff0c;访客各模块增删改查 个别模块关联查询 各个模块数据导出Excel 一些截图

Log360:护航安全,远离暗网风险

暗网有时候就像是一个神秘的地下世界&#xff0c;是互联网的隐蔽角落&#xff0c;没有任何规则。这是一个被盗数据交易、网络犯罪分子策划下一步攻击的地方。但仅仅因为它黑暗&#xff0c;不意味着你要对潜在的威胁视而不见。 暗网 这就是ManageEngine Log360的用武之地&…

创新工具|AI革新内容营销:策略、工具与实施指南

探索如何利用人工智能&#xff08;AI&#xff09;提升内容营销策略&#xff0c;从SEO优化到个性化推荐。本指南详细介绍了11款顶尖AI工具&#xff0c;旨在帮助中国的中高级职场人士、创业家及创新精英高效地策划和生成引人入胜的内容&#xff0c;同时确保内容的专业性、权威性和…

佩戴安全头盔监测识别摄像机

佩戴安全头盔是重要的安全措施&#xff0c;尤其在工地、建筑工程和工业生产等领域&#xff0c;安全头盔的佩戴对于工人的生命安全至关重要。为了更好地管理和监控佩戴安全头盔的情况&#xff0c;监测识别摄像机成为了一项重要的工具。监测识别摄像机可以通过智能技术监测并记录…

每日一练2024.5.24(补2024.5.26)

题目&#xff1a; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]示例 2&#xff1a; 输入&#x…