C#设计模式 之 抽象工厂模式
发布日期:2021-06-29 19:10:15
浏览次数:3
分类:技术文章
本文共 4445 字,大约阅读时间需要 14 分钟。
C#设计模式 之 抽象工厂模式
别名:Abstract Factory,Kit
一,意图
提供一个接口,让接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
二,动机
在程序开发中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
问题来了:
如何应对这种变化?如何绕过常规的对象创建方法(new)?工厂模式的缘起:
常规创建对象的方法:
CZYTest test = new CZYTest();
new 的问题:
实现依赖,不能应对“具体实例化类型”的变化。解决思路:
封装变化点 ---- 哪里变化,封装哪里。 也就是说,如果没有变化,就不需要额外的封装。解决方案:
变化点在“对象创建”,所有就封装“对象创建”适用场景:
抽象工厂模式主要用于应对“新系列”的需求变动,难以应对“新对象”的需求变动。抽象工厂模式经常和工厂方法模式共同组合来应对“对象创建”的需求变化
三,结构
- 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
- 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体都必须实现相应的抽象产品。
- 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
- 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。
四,优缺点
优点:
- 单一职责原则: 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
- 开闭原则: 向应用程序中引入新产品变体时, 你无需修改客户端代码。
- 分离了具体的类: 抽象工厂模式帮助我们控制一个应用创建对象的类。因为一个工厂封装创建产品对戏的职责和过程,他将客户程序与类的实现分离开来。客户程序通过它们的抽象接口操纵实例。
- 利于产品一致性: 当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一系列中的对象, 这一点很重要。而抽象工厂模式很容易实现这一点。
缺点:
- 代码复杂化: 由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。
- 难以扩展:难以扩展生产新种类的产品。这是因为,抽象工厂接口确定了可以被创建的产品集合。支持新种类的产品就需要拓展你该工厂接口,这将涉及抽象工厂类及其所有子类的改变。(代码量较大)
五,应用场景
应用场景:
在以下情况可以使用抽象工厂模式:
- 如果代码需要与多个不同系列的相关产品交互, 但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑, 你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。
- 抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。 只要代码通过该接口创建对象, 那么你就不会生成与应用程序已生成的产品类型不一致的产品。
- 如果你有一个基于一组抽象方法的类, 且其主要功能因此变得不明确, 那么在这种情况下可以考虑使用抽象工厂模式。
- 在好的设计程序中, 每个类只负责一件事。 如果一个类与多种类型产品交互, 就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。
举例理解:
下面以王者荣耀星元皮肤为例:
一款皮肤基本由:武器,头饰,身体组成。
青春系列:青春武器,青春头饰,青春身体; 圣诞系列:圣诞武器,圣诞头饰,圣诞身体; 以后还有可能添加其他系列…组成皮肤的对象是基本不变的,皮肤系列是变化点,所以适用于抽象工厂模式。
六,代码实现
实现方式:
- 为所有产品声明抽象产品接口。 然后让所有具体产品类实现这些接口(抽象类)。
- 声明抽象工厂接口(抽象类), 并且在接口中为所有抽象产品提供一组构建方法。
- 为每种产品变体实现一个具体工厂类。
- 在应用程序中开发初始化代码。 该代码根据应用程序配置或当前环境, 对特定具体工厂类进行初始化。 然后将该工厂对象传递给所有需要创建产品的类。
- 找出代码中所有对产品构造函数的直接调用, 将其替换为对工厂对象中相应构建方法的调用。
示例代码:
还是以上面那个星元皮肤为例:
- 创建抽象工厂 – 皮肤工厂,武器,头饰,身体工厂。
// 武器工厂public abstract class WuQiFactory { }// 头饰工厂public abstract class TouShiFactory { }// 身体工厂public abstract class ShenTiFactory { }// 皮肤工厂public abstract class PiFuFactory{ // 创建武器,头饰,身体 public abstract WuQiFactory CreateWuQi(); public abstract TouShiFactory CreateTouShi(); public abstract ShenTiFactory CreateShenTi();}
- 创建具体工厂 – 圣诞系列
// 圣诞武器具体实现public class ShengDanWuQi : WuQiFactory { }// 圣诞头饰public class ShengDanTouShi : TouShiFactory { }// 圣诞身体public class ShengDanShenTi : ShenTiFactory { }// 皮肤工厂public class ShengDanPiFuFactory : PiFuFactory{ // 创建武器,头饰,身体 public override WuQiFactory CreateWuQi() { Console.WriteLine("... 创建圣诞武器 ..."); return new ShengDanWuQi(); } public override TouShiFactory CreateTouShi() { Console.WriteLine("... 创建圣诞头饰 ..."); return new ShengDanTouShi(); } public override ShenTiFactory CreateShenTi() { Console.WriteLine("... 创建圣诞身体 ..."); return new ShengDanShenTi(); } }
青春系列 – 以后拓展其他系列,直接按照这模式新建就可以了。
// 青春武器具体实现public class QinChunWuQi : WuQiFactory { }// 青春头饰public class QinChunTouShi : TouShiFactory { }// 青春身体public class QinChunShenTi : ShenTiFactory { }// 皮肤工厂public class QinChunFuFactory : PiFuFactory{ // 创建武器,头饰,身体 public override WuQiFactory CreateWuQi() { Console.WriteLine(" --- 创建青春武器 --- "); return new QinChunWuQi(); } public override TouShiFactory CreateTouShi() { Console.WriteLine(" --- 创建青春头饰 --- "); return new QinChunTouShi(); } public override ShenTiFactory CreateShenTi() { Console.WriteLine(" --- 创建青春身体 --- "); return new QinChunShenTi(); }}
- 模拟客户程序使用抽象工厂
// 模拟客户程序class GameManager{ PiFuFactory pifuFactory; public GameManager(PiFuFactory pifuFactory) { this.pifuFactory = pifuFactory; } WuQiFactory wuQi; TouShiFactory touShi; ShenTiFactory shenTi; // 创建皮肤 public void CreatePiFu() { Console.WriteLine("GameManager 模拟客户程序 开始创建星元系列皮肤..."); wuQi = pifuFactory.CreateWuQi(); touShi = pifuFactory.CreateTouShi(); shenTi = pifuFactory.CreateShenTi(); }}
- C# Main 方法模拟调用:
class Program{ static void Main(string[] args) { GameManager gm = new GameManager(new ShengDanPiFuFactory()); gm.CreatePiFu(); GameManager gm1 = new GameManager(new QinChunFuFactory()); gm.CreatePiFu(); Console.ReadKey(); }}
测试结果
转载地址:https://czhenya.blog.csdn.net/article/details/114768606 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月24日 10时17分43秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Java 为什么需要内部类
2019-04-30
Java的可变参数列表
2019-04-30
Java 容器类型的打印
2019-04-30
Asp.net获取User-agent
2019-04-30
判断浏览器是否为微信客户端浏览器
2019-04-30
Sql查询数据分页显示
2019-04-30
Js 定义正则表达式
2019-04-30
ie8用ajax访问不能每次都刷新的问题
2019-04-30
Java 实现把异常信息写入到文件中
2019-04-30
Java 实现自定义异常 以及如何使用该异常
2019-04-30
Java 获取当前日期util
2019-04-30
Java RuntimeException类几点说明
2019-04-30
Js解析Json字符串
2019-04-30
Sql server 内置函数实现md5加密
2019-04-30
Java 格式字符串的使用
2019-04-30
JavaScript 实现倒计时
2019-04-30
Java Map的遍历
2019-04-30
Android 计时器的实现
2019-04-30
Android AsyncTask 异步任务取消
2019-04-30
Asp.net 解决表单提交之后 页面刷新会再次提交表单
2019-04-30