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();
}
}
测试结果
- 点赞
- 收藏
- 关注作者
评论(0)