Factory Method工厂方法模式(Java代码实现)——创建型模式

简介: Factory Method工厂方法模式(Java代码实现)——创建型模式

在本人所编写的关于23种设计模式的文章中,前言基本上都是一样的,读者可以从章节2开始阅读,本篇是关于创建型模式中工厂方法模式(Factory Method Pattern)的详解。

1.前言

根据Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用面向对象软件的基础) 一书,四位作者把设计模式分为三大类,分别如下:

1.创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  • 单例模式(Singleton Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 工厂方法模式(Factory Method Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)

2.结构型模式
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge Pattern)
  • 组合模式(Composite Pattern)
  • 装饰器模式(Decorator Pattern)
  • 外观模式(Facade Pattern)
  • 享元模式(Flyweight Pattern)
  • 代理模式(Proxy Pattern)

3.行为型模式
这些设计模式特别关注对象之间的通信。

  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 解释器模式(Interpreter Pattern)
  • 迭代器模式(Iterator Pattern)
  • 中介者模式(Mediator Pattern)
  • 备忘录模式(Memento Pattern)
  • 观察者模式(Observer Pattern)
  • 状态模式(State Pattern)
  • 策略模式(Strategy Pattern)
  • 模板模式(Template Pattern)
  • 访问者模式(Visitor Pattern)

本篇是关于创建型模式中工厂方法模式(Factory Method Pattern)的详解。

2.工厂方法模式(Factory Method Pattern)

2.1.意图

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

2.2UML类图

在工厂模式中,我们在创建对象时不会对使用端暴露创建逻辑,并且是通过使用一个共同的接口(下图中TeaFactory)来指向新创建的对象,如下(以现在热门的奶茶为例):
在这里插入图片描述

2.3.Java代码具体实现

2.3.1包结构如下:

在这里插入图片描述

2.3.2tea包

public interface Tea {
    
    void make();
}
public class BlackTea implements Tea {
     


    @Override
    public void make() {
     
        System.out.println("make the black tea!");
    }
}
public class GreenTea implements Tea {
      
    @Override
    public void make() {
      
        System.out.println("make the green tea!");
    }
}
public class MilkTea implements Tea {
       
    @Override
    public void make() {
       
        System.out.println("make the milk tea!");
    }
}

2.3.3factory包

/** * @Author: YuShiwen * @Date: 2022/2/3 10:43 PM * @Version: 1.0 */
public class TeaFactory {
        
    public Tea getTea(String tea) {
        
        if ("blackTea".equalsIgnoreCase(tea)) {
        
            return new BlackTea();
        } else if ("greenTea".equalsIgnoreCase(tea)) {
        
            return new GreenTea();
        } else if ("milkTea".equalsIgnoreCase(tea)) {
        
            return new MilkTea();
        } else {
        
            throw new RuntimeException("其他茶品暂不支持!");
        }
    }
}

2.3.4main包

public class CustomerDemo {
          public static void main(String[] args) {
          new TeaFactory().getTea("milktea").make(); new TeaFactory().getTea("blacktea").make(); } } 

2.3.5运行结果

在这里插入图片描述

2.4.优缺点

优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

建议:
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

2.5在源码中的应用

2.5.1Integer.valueOf()方法

Integer它提供了静态方法valueOf()来创建Integer。那么这种方式和直接写new Integer(3)有何区别呢?我们观察valueOf()方法:
它的好处在于,valueOf()内部可能会使用new创建一个新的Integer实例,但也可能直接返回一个缓存的Integer实例。Integer默认的是[-128, 127]的缓存范围,我们查看源码的时候也发现了,其中的low的下限是最开始的时候就固定死了的 final int low = -128;而其中的high虽然是设置了final int的,但是最初的时候,是还没有赋值的;所以我们可以通过设置java 的时候的参数来配置运行时的参数:
java -D java.lang.Integer.IntegerCache.high=1024 TestAutoBoxCache
在这里插入图片描述

对于调用方来说,没必要知道Integer创建的细节。
工厂方法可以隐藏创建产品的细节,且不一定每次都会真正创建产品,完全可以返回缓存的产品,从而提升速度并减少内存消耗。

2.5.2List.of()方法

List.of()是Java9提供的方法,这个静态工厂方法接收可变参数,然后返回List接口。相较于Arrays.asList()方法,原数组修改对Arrays.asList生成的List有影响,对List.of 生成的List无影响。调用方获取的产品总是List接口,而且并不关心它的实际类型。即使调用方知道List产品的实际类型是java.util.ImmutableCollections$ListN,也不要去强制转型为子类,因为静态工厂方法List.of()保证返回List,但也完全可以修改为返回java.util.ArrayList。这就是里氏替换原则:返回实现接口的任意子类都可以满足该方法的要求,且不影响调用方。
在这里插入图片描述
在这里插入图片描述

2.5.3MessageDigest.getInstance()方法

在这里插入图片描述

对于MessageDigest.getInstance()方法也是如此,比如我们可以通过MessageDigest.getInstance("MD5");来获取MD5算法。

并且在Effective Java一书中,第二章第一条就是用静态工厂方法代替构造器

c514bba66c334ed49e2b9eefe8ac17be.png已完结,于2022年大年初四凌晨。
目录
相关文章
|
10天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
19 5
Java反射机制:解锁代码的无限可能
|
6天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
30 3
|
7天前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
5天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
6月前
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
312 1
|
Java BI API
在Java代码中打日志需要注意什么?
日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手,是撕逼和甩锅的利器!
699 0
|
缓存 Java 网络架构
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
167 0
|
Java BI Apache
在Java代码中打日志需要注意什么?
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 为什么要打日志? 日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手! 做一件事情之前,先思考为什么。
在Java代码中打日志需要注意什么?
|
缓存 架构师 搜索推荐
别在 Java 代码里乱打日志了,这才是正确的日志打印姿势!
使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
|
Java Android开发 C语言
02_JNI中Java代码调用C代码,Android中使用log库打印日志,javah命令的使用,Android.mk文件的编写,交叉编译
 1  编写以下案例(下面的三个按钮都调用了底层的C语言): 项目案例的代码结构如下: 2 编写DataProvider的代码: package com.example.ndkpassdata;   public class DataProvider {         /**      * 计算x和y的加法  apktools      *
1355 0