掌握工厂方法模式,打造灵活多变的软件生产线

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本文深入探讨了工厂方法模式在软件开发中的应用与优势,帮助读者掌握这一设计模式,进而打造灵活多变的软件生产线。文章首先介绍了工厂方法模式的基本概念和原理,通过生动的例子解释了该模式如何在不修改代码的情况下创建不同对象。接着,文章详细分析了工厂方法模式的特点和优势,包括降低对象之间的耦合度、提高系统的可扩展性和可维护性等。最后,文章结合实际案例,阐述了如何在项目中运用工厂方法模式来构建高效、灵活的软件架构。本文内容深入浅出,既适合初学者入门学习,也能为有经验的开发者提供有价值的参考。通过掌握工厂方法模式,您将能够更有效地应对软件开发中的挑战,实现更优质的软件产品。

💪🏻 制定明确可量化的目标,持默默的做事。


工厂方法模式是一种创建型设计模式,它提供了一种创建对象的接口,但将具体实例化对象的工作推迟到子类中完成。这样做的目的是创建对象时不用依赖于具体的类,而是依赖于抽象,这提高了系统的灵活性和可扩展性。

以下是工厂方法模式的几个关键组成部分:

  1. 产品(Product): 定义了工厂方法所创建的对象的接口。在我们的日志记录器示例中,Logger 类就是一个产品接口。
  2. 具体产品(Concrete Product): 实现了产品接口的具体类。继承自Logger类的FileLoggerConsoleLoggerDatabaseLogger类就是具体产品。
  3. 创建者(Creator): 声明了工厂方法,这个方法返回一个产品类型的对象。通常情况下,创建者类将是抽象类,并包含工厂方法的声明。在我们的示例中,LoggerFactory就是一个创建者。
  4. 具体创建者(Concrete Creator): 覆盖了工厂方法以返回一个具体产品实例。这是实际决定要实例化哪一个产品的类。例如,FileLoggerFactoryConsoleLoggerFactoryDatabaseLoggerFactory都是具体创建者,它们覆盖工厂方法以返回它们各自的产品实例。

工厂方法模式的工作方式:

  • 定义产品接口: 首先定义一个产品接口,它描述了产品的公共接口。
  • 创建具体产品: 然后为每种类型的产品实现具体类。
  • 创建抽象创建者: 接着创建一个创建者(通常是抽象类或接口)来声明工厂方法。工厂方法通常会有一个返回类型为产品接口的返回类型。
  • 实现具体创建者: 创建具体创建者类来实现抽象创建者中声明的工厂方法,返回具体产品的实例。
  • 在应用中使用创建者: 最后在应用程序中,我们使用创建者类来调用工厂方法,获取产品对象的实例。

工厂方法模式的优点:

  • 降低耦合度: 客户代码从具体类解耦,并依赖于抽象。这意味着客户代码不需要改变就能与任何新增的具体产品工作。
  • 增加了系统的可扩展性: 新的具体产品可以很容易地加入到系统中,因为现有的客户代码不会受到影响。
  • 提高代码的可维护性: 如果一种产品在多处创建,更改产品的实现或者更换一个产品都会更加容易和集中。

工厂方法模式的缺点:

  • 增加了代码的复杂性: 可能需要引入许多新类,每种类型的产品都需要一个新的具体创建者类。
  • 需要更多的设计考虑: 设计好抽象创建者和具体创建者之间的关系需要一定的设计经验和考虑。

总的来说,工厂方法模式在需要灵活和可扩展的系统中非常有用,尤其是当我们预计产品类可能会经常改变时。它有助于保持一个健壮而灵活的代码库。


一、案例

场景

       需要一个创建不同类型日志记录器的框架,日志记录器可能记录到文件、控制台或者数据库。

1.1 示例代码

       定义一个抽象日志记录器类和一个工厂方法:

/**
 * 日志处理抽象类
 */
public abstract class Logger {
    /**
     * 操作日志
     */
    public abstract void log(String message);
}
/**
 * 工厂方法抽象类
 */
public abstract class LoggerFactory {
    /**
     * 操作日志
     */
    public void log(String message) {
        createLogger().log(message);
    }
    /**
     * 工厂方法,创建日志对象的接口对象
     */
    public abstract Logger createLogger();
}

       为文件日志类型实现具体的日志记录器和对应的工厂:

/**
 * 文件日志实现类
 */
public class FileLogger extends Logger {
    public void log(String message) {
        // 逻辑来将消息写入文件
        System.out.println("File logger: " + message);
    }
}
/**
 * 文件日志工厂方法类
 */
public class FileLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        // 可以在这里添加创建FileLogger所需的逻辑和初始化
        return new FileLogger();
    }
}

       为控制台日志类型实现具体的日志记录器和对应的工厂:

/**
 * 控制台日志实现类
 */
public class ConsoleLogger extends Logger {
    public void log(String message) {
        // 逻辑来将消息打印到控制台
        System.out.println("Console logger: " + message);
    }
}
/**
 * 控制台工厂方法类
 */
public class ConsoleLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        // 创建ConsoleLogger的逻辑
        return new ConsoleLogger();
    }
}

       现在,在应用程序中,我们可以根据需要使用工厂来创建日志记录器对象,而不必直接实例化它们。这样,如果以后需要添加新的日志记录器类型(例如,数据库日志记录器),我们只需要添加一个新的工厂而不需要修改现有代码。

       客户端示例代码:

public class Application {
    public static void main(String[] args) {
        LoggerFactory factory = new FileLoggerFactory();
        // 或者 factory = new ConsoleLoggerFactory();
        factory.log("这是一条日志信息.");
    }
}

       当 Application 运行时,根据选择的工厂类型,它将使用对应的工厂创建一个日志记录器,并通过这个记录器记录消息。这个示例遵循了工厂方法的设计原则,因为它使对象的创建和使用分离,使得系统易于扩展和维护。

       

1.2 扩展-添加数据库日志


       如果我们想要将日志记录扩展到数据库,我们首先需要为数据库日志创建一个新的Logger子类,然后实现对应的工厂类。下面展示了如何实现这一扩展:

// 数据库日志记录器——实现Logger抽象类
public class DatabaseLogger extends Logger {
    public void log(String message) {
        // 示例逻辑来将消息保存到数据库
        System.out.println("Database logger: " + message);
        // 这里可以包含实际将日志保存到数据库的代码
    }
}
// 数据库日志工厂——继承LoggerFactory
public class DatabaseLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        // 创建DatabaseLogger的逻辑,可以在这里包含初始化代码
        return new DatabaseLogger();
    }
}

       在Application 或其他任何需要日志记录功能的部分,现在可以不作出太多改动地简单地引入新的DatabaseLogger:

public class Application {
    public static void main(String[] args) {
        LoggerFactory factory = new DatabaseLoggerFactory();
        // 或者 factory = new FileLoggerFactory();
        // 或者 factory = new ConsoleLoggerFactory();
        factory.log("这是一条日志信息.");
    }
}

       现在,无论是FileLoggerFactoryConsoleLoggerFactory还是DatabaseLoggerFactory,均不需要修改 Application 中的任何代码。进一步说,如果有必要添加其他类型的日志记录,如远程API日志记录、XML日志记录等,整个流程同样适用。这就展示了工厂方法模式在扩展性方面的强大之处。每次新增一种产品(本例中的Logger实现),只需添加一个新的具体工厂类且不需要改动现有的代码,符合开闭原则(对修改封闭,对扩展开放)。

       通过上述示例,你可以看到工厂方法设计模式是如何工作的,它能够提供足够的灵活性,允许系统在不直接依赖具体类的情况下创建对象。这种方式降低了类间的耦合,提高了代码的可维护性与可扩展性。

       

二、模式讲解

2.1 功能

功能工厂方法主要工能是让父类在不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现

这样在设计的时候,不用去考虑具体的实现,需要某个对象,把它通过工厂方法返回就好了, 在使用这些对象实现功能的时候还是通过接口来操作,这类似于 IoC/DI 的思想。

       

2.2 工厂方法模式结构

  • Logger:定义工厂方法所创建的对象的类,也就是实际需要使用的对象的类。
  • 子类A:具体的 Logger 接口的实现对象。
  • Factory:创建器,声明工厂方法,工厂方法通常会返回一个 Logger 类型的实例对象,而且多是抽象方法。也可以在 Factory里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Logger类型的实现对象。
  • 实现类B:具体的创建器对象,覆盖实现 Factory 定义的工厂方法,返回具体的 Logger实例。

       

2.3 示例代码程序结构图

       

2.4 简单工厂方法结构图

2.5 工厂方法模式与简单工厂模式

       工厂方法模式与简单工厂模式结构如上图2 和 图3。

       若要添加新的日志类型,简单工厂模式需要添加这个新功能类的日志子类,再在Factory中添加一个case语句来做判断;工厂方法模式需要添加这个新功能类的日志子类,再添加一个实现Factory的工厂类。但要我再去更改客户端,这 不等于不但没有减化难度,反而增加了很多类和方法,把复杂性增加了 吗?为什么要这样?”

       简单工厂模式最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。但是如果添加新的日志类型,就要去个性case判断条件,这违背了开-闭原则。(工厂类扩展了,也修改了)

       于是工厂方法出现了。

工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

       此时,添加新的日志类型,只需要添加新的日志子炻和新的日志工厂类,满足了开-闭原则。(对扩展开放,对修改关闭)

       

三、扩展-工厂方法与IoC/DI

       IOC-Inversion of Control,控制反转。

       DI-Dependency Injection,依赖流入。

 3.1 弄明白IoC/DI

 1. 参与者:一般有三方参与者,一个是某个对象;另一个是IoC/DI 的容器;还有一个是某个对象的外部资源。

 2. 谁依赖于谁: 当然是某个对象依赖于 IoC/DI 的容器。

 3. 为什么需要依赖:对象需要 IoC/DI 的容器来提供对象需要的外部资源。

 4. 谁注入于谁:很明 显是 IoC/DI 的容器注入某个对象。

 5. 到底注入什么:就是注入某个对象所需要的外部资源。

6. 谁控制谁:当然是 IoC/DI 的容器来控制对象了。

 7. 控制什么:主要是控制对象实例的创建。

8. 为何叫反转 : 反转是相对于正向而言的,那么什么算是正向的呢? 考虑一下常规情况下的应用程序,如果要在A 里面使用C,你会怎么做呢? 当然是直接去创建C 的对象,也就是说,在A 类中主动去获取所需要的外部资源C,这种情况被称为正向的。

       那么什么是反向呢?就是A 类不再主动去获取C,而是被动等待,等待IoC/DI 的容器获 取一个C的实例,然后反向地注入到A 类中。

 9. 依赖注入和控制反转是同一概念吗?

       依赖注入和控制反转是对同一件事情的不同描述。 从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度去描述,可以 把依赖注入描述得完整点 :应用程序依赖容器创建并注入它所需要的外部资源;而控制 反转是从容器的角度去描述,描述得完整点就是 :容器控制应用程序,由容器反向地向 应用程序注入其所需要的外部资源。

       小结:其实 IoC/DI 对编程带来的最大改变不是在代码 上,而是在思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击, 但是在 IoC/DI 思想中,应用程序就变成被动的了,被动地等待 IoC/DI 容器来创建并注入它所需要的资源了。

       

 3.2 工厂方法与IoC/DI的关系

       IoC/ DI:主从换位,被动等待IOC/DI容器来创建并流入它所需要的资源。

       工厂方法

/**
 * 工厂方法抽象类
 */
public abstract class LoggerFactory {
    /**
     * 操作日志
     */
    public void log(String message) {
        createLogger().log(message);
    }
    /**
     * 工厂方法,创建日志对象的接口对象
     */
    public abstract Logger createLogger();
}

       log方法中,需要用Logger类,可是又不知道要用哪一个,也不去主动去创建了,反正在子类里已经实现了,不用管怎么获取,直接使用日志功能,类似于从子类注入进来。  

       从思想层面上,会发现工厂方法示模式和 IoC/DI 的思想是相似的,都是“ 主动变被动” ,进行了“ 主从换位” ,从而获得了更灵活的程序结构。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7月前
|
敏捷开发 开发框架 数据可视化
|
算法 测试技术 数据安全/隐私保护
规则引擎算法的魅力:文档管理软件的灵活性与可扩展性
数字时代已经来了,文档管理软件已经成了企业和组织的宠儿。它们不仅能够帮你打理一大堆文档和信息,还能让你的工作效率飙升,减少犯错的机会,而且信息查找和分享也变得飞快。但是,随着各种各样的需求一直在不停地增长和变化,这些软件也要不停地充电升级,以满足用户们的新愿望。规则引擎算法在这方面可是大有作为,尤其是在让软件更灵活、更能扩展方面,它功不可没。接下来就让我们来看看规则引擎算法在文档管理软件中有哪些作用——
222 1
|
2月前
|
监控 供应链 数据可视化
团队高效流程管理必备:方法与实用软件推荐
在当前激烈的商业竞争环境中,高效的业务流程管理对企业成功至关重要。本文介绍了业务流程管理的方法,包括流程梳理、优化、监控与评估及员工培训与参与,并推荐了几款实用的业务流程管理系统(BPMS)。BPMS能自动化、可视化业务流程,提升效率。具体推荐包括板栗看板、Trello和Wrike,它们分别在任务管理、团队协作及项目管理方面有各自的优势。选择合适的BPMS有助于提高工作效率、增强流程透明度、提升团队协作能力,规范管理并降低风险,从而增强企业竞争力。
85 0
|
7月前
|
监控 Java Android开发
安卓应用开发:打造高效用户界面的五大策略
【4月更文挑战第29天】 在安卓应用开发的世界中,构建一个既美观又高效的用户界面(UI)对于吸引和保留用户至关重要。本文将深入探讨五种策略,这些策略可以帮助开发者优化安卓应用的UI性能。我们将从布局优化讲起,逐步过渡到绘制优化、内存管理、异步处理以及最终的用户交互细节调整。通过这些实践技巧,你将能够为用户提供流畅而直观的体验,确保你的应用在竞争激烈的市场中脱颖而出。
|
7月前
|
编解码 测试技术 Android开发
安卓应用开发:打造高效用户界面的五大关键策略
【5月更文挑战第29天】 在竞争激烈的移动应用市场中,一个直观高效的用户界面(UI)对于吸引和保持用户至关重要。针对安卓平台,本文将探讨五种提升应用UI效率与用户满意度的关键策略。这些策略不仅基于最新的设计趋势,还结合了性能优化的实践技巧,旨在为开发者提供实用指南,帮助他们创建流畅、响应迅速且美观的应用体验。
|
设计模式 算法 搜索推荐
工厂+策略模式:让生活更便捷的秘密武器
在日常生活中,我们经常面临选择的困扰,比如选择适合自己口味的咖啡,选择合适的手机品牌等等。而工厂+策略模式就是一种能够帮助我们做出更好选择的秘密武器。本文将以生活化的语言,介绍工厂+策略模式的意义,并举例说明其在日常工作中的应用场景。
168 0
|
小程序 开发工具
一个无需等待的世界不美吗?为什么游戏开发中需要人为的制造等待?
本文主要内容有微信小游戏开发中的 3 种不同的等待积木块的使用场景和使用方法。 这是微信小游戏开发的基础系列,使用的游戏开发工具为“微信小游戏制作工具”。如果你没有任何的游戏开发经验,欢迎阅读我的“人人都能做游戏”系列教程,它会手把手的教你做出自己的第一个微信小游戏。
96 0
进销存系统能给企业带来什么好处?
企业必须思考如何提高企业管理,如何更高效地降本增效?在这种背景下,进销存系统便诞生了。点晴进销存系统能给企业带来哪些好处?我们一起来看一下吧!
163 0
|
设计模式 Java 程序员
Java设计模式——工厂模式——快速生产 高效精准
本文目录 1. 关于设计模式 2. 工厂模式1.0——极简版本 3. 工厂模式2.0——拓宽产品种类 4. 工厂模式3.0——精准的产品线 5. 总结
111 0
|
监控 Android开发 iOS开发