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年大年初四凌晨。
目录
相关文章
|
9天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
23天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
35 5
Java反射机制:解锁代码的无限可能
|
20天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
53 3
|
25天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
69 10
|
20天前
|
分布式计算 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 若是设置参数该如何设置
|
19天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
27天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
30 6
|
27天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
28天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
54 3
|
28天前
|
存储 Java 开发者
Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效
【10月更文挑战第19天】在软件开发中,随着项目复杂度的增加,数据结构的组织和管理变得至关重要。Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,帮助开发者告别混乱,提升代码质量。
27 1