【愚公系列】2023年10月 二十三种设计模式(一)-工厂方法模式(Factory Method Pattern)
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
🚀前言
设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。
毫无疑问,设计模式对个人、团队和整个系统都带来了显著的益处。它们将代码开发提升到工程化水平,为软件工程构建提供了坚实的基础,就如同大厦的一块块精巧的砖石一样。在项目中明智地应用设计模式可以完美地解决各种复杂问题。每种设计模式都有相应的原理和最佳实践,它们描述了我们日常开发中不断遇到的问题,以及这些问题的核心解决方法。正是因为这种实用性和通用性,设计模式才能在软件开发中广泛地得以应用。设计模式是构建稳健、可扩展和可维护软件的关键工具,为开发者们提供了解决问题的智慧和指导。
🚀一、工厂方法模式(Factory Method Pattern)
工厂方法模式是一种创建型设计模式,其主要目的是将对象的创建过程延迟到子类中,让子类自己决定要实例化哪个具体工厂类。这个模式提供了一种灵活且可扩展的方式来创建对象,同时也保护了客户端代码免受对象创建细节的影响。
在工厂方法模式中,我们定义了一个创建对象的接口,通常称为工厂接口或创建者接口。每个具体的工厂类都实现了这个接口,并负责创建特定类型的对象。客户端代码通过调用工厂方法来获取对象,而不需要知道对象的具体创建逻辑。
这种模式的关键点在于,它将对象的创建过程委托给了子类,因此允许在不修改客户端代码的情况下引入新的具体产品类和工厂类。这使得系统更加可扩展和容易维护,同时也遵循了开闭原则,因为可以添加新的子类而无需修改现有的代码。
工厂方法模式提供了一种创建对象的最佳实践,通过将创建逻辑封装在子类中,实现了松耦合和高度可扩展的对象创建过程。
🚀二、使用步骤
🔎1.角色
🦋1.1 抽象工厂(Creator)
工厂方法模式(Factory Method Pattern)中的抽象工厂(Creator)是该模式的核心概念之一,其主要作用如下:
定义创建对象的接口:抽象工厂是一个抽象类或接口,它定义了一个或多个工厂方法,这些工厂方法用于创建产品对象。每个工厂方法通常对应一个具体的产品类型。
封装对象的创建逻辑:抽象工厂将对象的创建逻辑封装在工厂方法中,客户端代码无需关心具体产品的创建过程,只需调用工厂方法获取所需的产品对象。
延迟对象的创建:抽象工厂模式将对象的创建过程延迟到具体的工厂子类中。每个具体工厂子类负责创建特定类型的产品,从而实现了对象创建的多态性。
支持多态性和替代性:抽象工厂模式允许客户端代码通过工厂接口或抽象类与产品进行交互,而不需要知道具体产品的类名或实现细节。这支持多态性和对象替代性,使得客户端代码更加灵活。
扩展性:通过添加新的具体工厂子类和产品类,可以轻松地扩展系统,引入新的产品类型,而无需修改现有的客户端代码。
符合开闭原则:抽象工厂模式符合开闭原则,因为可以添加新的工厂子类和产品类,而不需要修改现有的代码,从而提高了系统的可维护性和可扩展性。
抽象工厂模式的核心作用是定义了一个创建对象的接口,将对象的创建过程延迟到具体的工厂子类中,实现了多态性、对象替代性和系统的可扩展性。这种模式常用于需要生产一组相关或兼容的产品对象,以满足不同客户端或配置的需求。它是一种强大的设计模式,特别适用于需要处理复杂对象家族的情况。
🦋1.2 具体工厂(Concrete Creator)
工厂方法模式(Factory Method Pattern)中的具体工厂(Concrete Creator)是一种具体的类,实现了抽象工厂(Creator)定义的工厂方法,用于创建具体的产品对象。具体工厂负责实例化特定类型的产品,它是工厂方法模式的关键组成部分,具有以下概念和作用:
实现工厂方法:具体工厂类必须实现抽象工厂中定义的一个或多个工厂方法。每个工厂方法通常对应一个具体的产品类型,它负责创建该类型的产品对象。
封装对象的创建逻辑:具体工厂类内部封装了对象的创建逻辑,客户端代码无需了解具体产品的构造过程,只需要通过工厂方法来获取所需的产品。
创建特定类型的产品:每个具体工厂类专门负责创建一组相关或兼容的产品对象,这些产品共享一致的接口或抽象类,但具体实现有所不同。
支持多态性:客户端代码通过与抽象工厂接口或抽象类交互,可以获得不同具体工厂创建的产品对象,从而支持多态性和对象替代性。
系统的可扩展性:通过添加新的具体工厂类,可以轻松地扩展系统,引入新的产品类型,而不需要修改现有的客户端代码。这符合开闭原则。
实例化产品对象:具体工厂类的主要责任是实例化产品对象,根据客户端的需求和具体工厂的实现,创建合适的产品对象并返回给客户端。
具体工厂是工厂方法模式的实际工作者,负责实现工厂方法以创建特定类型的产品。它们在系统中扮演着关键的角色,通过将对象的创建过程封装在具体工厂中,实现了系统的灵活性和可扩展性。客户端代码通过与抽象工厂接口或抽象类交互,与具体工厂解耦,从而能够轻松应对不同类型的产品需求。
🦋1.3 抽象产品(Product)
工厂方法模式(Factory Method Pattern)中的抽象产品(Product)是该模式的核心概念之一,其主要作用如下:
定义产品的接口:抽象产品是一个接口或抽象类,它定义了一组方法或属性,描述了产品对象应该具备的特征和行为。这个接口或抽象类定义了客户端与产品之间的通用接口。
抽象产品的特征:抽象产品不提供具体的实现细节,而是规定了产品应该有哪些特征,这些特征对于不同的具体产品来说是一致的,例如共享相同的方法签名。
用于对象替代性:抽象产品的存在使得客户端代码可以通过与抽象产品接口或抽象类交互,而不需要知道具体产品的类名或实现细节。这支持了对象替代性,客户端可以无缝切换不同具体产品的实例。
支持多态性:通过抽象产品,客户端可以以一致的方式与不同具体产品进行交互,利用多态性来处理产品对象,从而提高了代码的可维护性和可扩展性。
作为工厂方法的返回类型:抽象产品通常用作工厂方法的返回类型,具体工厂子类通过工厂方法实例化产品对象并返回给客户端,客户端代码直接使用抽象产品来引用新创建的对象。
抽象产品在工厂方法模式中扮演了定义产品通用特征和接口的角色,它是产品家族中所有具体产品的公共基础。通过抽象产品,可以实现客户端代码与具体产品的解耦,支持多态性,同时也为具体工厂提供了一致的返回类型。这种模式使得系统更加灵活、可扩展,并符合开闭原则,因为可以添加新的具体产品类而不需要修改现有的代码。
🦋1.4 具体产品(Concrete Product)
工厂方法模式(Factory Method Pattern)中的具体产品(Concrete Product)是实际的产品类,它们是由具体工厂类(Concrete Creator)创建的对象,具体产品的概念和作用如下:
实现产品的具体功能:具体产品是抽象产品(Abstract Product)的具体实现,它们提供了产品的实际功能和行为。每个具体产品类都实现了抽象产品接口或继承了抽象产品抽象类,以提供产品的具体特征。
封装产品的具体细节:具体产品类负责封装产品的具体细节和实现,客户端代码无需关心产品的构造细节,只需与抽象产品接口或抽象类进行交互。
具体产品的差异性:不同的具体产品类可以实现不同的功能,因此它们代表了产品家族中的不同成员。这些差异通常是根据业务需求或客户端需求来定义的。
支持多态性:客户端代码通过与抽象产品接口或抽象类交互,可以以一致的方式处理不同具体产品的实例,从而实现多态性,提高了系统的可维护性和可扩展性。
作为工厂方法的输出:具体产品类通常是工厂方法的输出,即工厂方法负责实例化具体产品对象,并将其返回给客户端代码。客户端通过具体产品的引用来使用新创建的对象。
具体产品是工厂方法模式中的产品实现,它们具体定义了产品的行为和特征。不同的具体产品类代表了产品家族中的不同成员,通过抽象产品接口或抽象类与客户端代码进行交互,支持多态性和对象替代性。工厂方法模式通过具体产品的封装和抽象,实现了对象创建的多态性和可扩展性,使得系统更加灵活和易于维护。
🔎2.示例
命名空间FactoryMethod中包含Logger日志基类,4个具体日志类,分别为TextLogger文本日志类、BinaryLogger二进制日志类、DatabaseLogger数据库日志类和CloudLogger云日志类。另外包含一个LoggerFactory工厂基类,它又包含4个对应的工厂实现类。本案例尝试以一个日志记录器来讲述工厂方法模式的运作机制。
public abstract class Logger {
public abstract void Log(string message);
}
抽象日志基类Logger。
public class TextLogger : Logger {
public override void Log(string message) {
Console.WriteLine($"INFO {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")}" +
$" [{this.LoggerType()}] {message}");
}
}
具体日志类,以文本形式记录日志。
public class BinaryLogger : Logger {
public override void Log(string message) {
Console.WriteLine($"INFO {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")}" +
$" [{this.LoggerType()}] {message}");
}
}
具体日志类,以二进制形式记录日志。
public class DatabaseLogger : Logger {
public override void Log(string message) {
Console.WriteLine($"INFO {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")}" +
$" [{this.LoggerType()}] {message}");
}
}
具体日志类,以数据库形式记录日志。
public class CloudLogger : Logger {
public override void Log(string message) {
Console.WriteLine($"INFO {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff")}" +
$" [{this.LoggerType()}] {message}");
}
}
具体日志类,以云服务器形式记录日志。
public abstract class LoggerFactory {
public abstract Logger CreateLogger();
}
工厂基类,用于生产日志记录器。
public class TextFactory : LoggerFactory {
public override Logger CreateLogger() {
return new TextLogger();
}
}
public class BinaryFactory : LoggerFactory {
public override Logger CreateLogger() {
return new BinaryLogger();
}
}
public class DatabaseFactory : LoggerFactory {
public override Logger CreateLogger() {
return new DatabaseLogger();
}
}
public class CloudFactory : LoggerFactory {
public override Logger CreateLogger() {
return new CloudLogger();
}
}
4个具体工厂类,用于生成相应的日志记录器。
public static class Extentions {
public static string LoggerType(this Logger logger) {
return logger
.ToString()
.Replace(nameof(FactoryMethod) + ".", "")
.Replace(nameof(Logger), "");
}
}
定义了一个扩展,方便处理日志字符串。
public class Program {
private static LoggerFactory _factory = null;
private static Logger _logger = null;
public static void Main(string[] args) {
_factory = new TextFactory();
CreateLogger();
_factory = new BinaryFactory();
CreateLogger();
_factory = new DatabaseFactory();
CreateLogger();
_factory = new CloudFactory();
CreateLogger();
Console.ReadKey();
}
const string LOG_MESSAGE = "I logged a lot of {0} info!";
private static void CreateLogger() {
_logger = _factory.CreateLogger();
_logger.Log(string.Format(LOG_MESSAGE, _logger.LoggerType()));
}
}
以上是调用方的代码,首先需要维持日志和工厂基类,然后创建不同的日志记录器并记录日志内容。以下是这个案例的输出结果:
INFO 2018-07-20 06:17:02.871 [Text] I logged a lot of Text info!
INFO 2018-07-20 06:17:02.928 [Binary] I logged a lot of Binary info!
INFO 2018-07-20 06:17:02.932 [Database] I logged a lot of Database info!
INFO 2018-07-20 06:17:02.933 [Cloud] I logged a lot of Cloud info!
🚀总结
🔎1.优点
工厂方法模式(Factory Method Pattern)具有多个优点,使其成为一种有用的设计模式,包括:
解耦客户端和具体产品:工厂方法模式通过引入抽象工厂和抽象产品,将客户端与具体产品的创建过程解耦。客户端代码只需要依赖于抽象工厂和抽象产品,而不需要直接与具体产品类交互,从而提高了代码的灵活性和可维护性。
支持多态性:工厂方法模式利用抽象产品作为接口,使客户端能够以一致的方式处理不同具体产品的实例。这支持了多态性,使得代码更易扩展和维护。
符合开闭原则:工厂方法模式符合开闭原则,允许系统在不修改现有代码的情况下引入新的产品类型。通过添加新的具体工厂和具体产品类,可以扩展系统的功能,而不会破坏现有代码的稳定性。
易于维护和扩展:工厂方法模式使得系统的结构更加清晰,每个具体产品都有对应的具体工厂,新产品的添加不会对已有代码产生负面影响。这种结构使系统更容易维护和扩展。
支持产品家族:工厂方法模式支持创建一组相关或兼容的产品对象,这些产品构成了一个产品家族。每个具体工厂负责创建产品家族中的一部分,从而支持创建复杂对象家族的需求。
便于测试:由于具体产品的创建逻辑被封装在具体工厂中,因此可以轻松地进行单元测试。测试时可以使用模拟具体工厂来创建产品,而无需实际创建产品的实例。
工厂方法模式提供了一种灵活、可扩展、易于维护的方式来处理对象的创建,使得系统更具弹性和可维护性。这使得它在各种应用中都有广泛的应用,特别是当需要处理多种产品类型或产品家族时,工厂方法模式尤为有
🔎2.缺点
工厂方法模式(Factory Method Pattern)虽然有很多优点,但也存在一些缺点,需要考虑和权衡:
类的数量增加:引入工厂方法模式会增加类的数量,每个具体产品都需要对应一个具体工厂类。如果产品数量庞大,可能会导致类的数量急剧增加,增加系统的复杂性。
增加代码复杂度:工厂方法模式引入了额外的抽象层次,包括抽象工厂和抽象产品。这会增加代码的复杂度,使得理解和维护系统变得更加困难。
不适用于简单场景:在某些简单情况下,引入工厂方法模式可能会显得过于繁琐。如果只有少量的产品和工厂,而且它们之间的关系简单明了,使用工厂方法模式可能会显得不必要。
增加运行时开销:在运行时,工厂方法模式需要客户端选择合适的具体工厂来创建产品对象。这可能会引入一定的运行时开销,尤其是在复杂的工厂层次结构中。
难以理解工厂层次:当工厂方法模式中存在多层工厂层次时,可能会难以理解不同工厂之间的关系和作用。复杂的工厂结构可能需要额外的文档和注释来帮助理解。
可能引入过度设计:如果在项目中过度使用工厂方法模式,可能会导致过度设计(overdesign),增加了不必要的复杂性和开销。在简单情况下,使用工厂方法可能会显得过度复杂。
工厂方法模式适用于需要创建多个产品对象,并且这些产品对象属于一个产品家族的情况。然而,它不是一种适用于所有场景的设计模式,需要根据具体情况来权衡其优点和缺点,确保使用它能够提高系统的可维护性和扩展性,而不会引入不必要的复杂性。
🔎3.使用场景
工厂方法模式(Factory Method Pattern)适用于以下一些场景和情况:
需要创建一组相关或兼容的产品:当系统需要创建一组相关的产品对象,而且这些产品对象之间存在某种关联或兼容性时,工厂方法模式是一个很好的选择。例如,创建不同类型的按钮、对话框或操作系统组件时,可以使用工厂方法模式。
延迟对象的创建:工厂方法模式允许将对象的创建延迟到子类中。这对于需要根据运行时条件来选择创建哪种具体产品的情况非常有用。例如,根据用户选择的主题来创建不同样式的界面元素。
符合开闭原则:如果需要在不修改现有代码的情况下引入新的产品类型,工厂方法模式是一种符合开闭原则的设计模式。只需创建新的具体工厂和具体产品类,而无需修改现有代码。
多态性和对象替代性:工厂方法模式支持多态性,客户端代码可以通过与抽象产品接口交互,处理不同具体产品的实例,从而实现对象替代性。这使得系统更加灵活和可扩展。
测试和单元测试:工厂方法模式使得产品的创建过程可以被模拟或替代,从而更容易进行单元测试。测试时可以使用模拟工厂来创建产品,而不必创建真实的产品实例。
产品家族:当需要创建一组相关产品,而且这些产品可以归纳为一个产品家族时,工厂方法模式是一种合适的选择。每个具体工厂负责创建产品家族中的一部分。
分离高层和低层模块:工厂方法模式将高层模块(客户端代码)与低层模块(具体产品的实现)分离,使得系统更易于维护和扩展。
工厂方法模式适用于需要创建多个产品对象,而且这些产品对象之间有一定的关联或兼容性,并且需要支持对象的多态性和替代性。它是一种有助于解耦和扩展的设计模式,特别适用于复杂的系统或需要频繁引入新
🚀感谢:给读者的一封信
亲爱的读者,
我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。
如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。
我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。
如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。
再次感谢您的阅读和支持!
最诚挚的问候, “愚公搬代码”
- 点赞
- 收藏
- 关注作者
评论(0)