初识常用12种设计模式---(其二)抽象工厂(abstract factory)
1.抽象工厂设计模式
-
1.1 什么是抽象工厂?
- 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 抽象工厂模式属于创建模式之一,创建模式主要是对类的实例化进行了抽象
-
1.2 解决问题的思路是什么?
- 1.2.1: 一个是只知道所需要的一系列对象的接口,而不知具体实现,或者是不知道具体使用哪一个实现
- 1.2.2: 另一个是这一系列对象是相关或者相互依赖的,也就是说纪要创建接口的对象,还要约束它们之间的关系
-
1.2.3: 工厂方法或简单工厂就是解决只知道接口不知道实现的问题,为什么不用它们呢?
解:因为它们关注的是单个产品对象的创建,而不是一系列产品
2.抽象工厂问题
- 2.1: 问题:现在客户要求工程师组装一台计算机,其中相应的CPU和主板等配件由客户自己*选择,那么客户如何选取对应匹配的CPU和主板呢?
- 2.2: 提示:CPU有多种,主板也有多种。CPU中的针脚数要对应主板中的插槽数、CPU的频率(主频)要与主板中的总线频率对应等等条件。(在此我们只以针脚和插槽为例)
- 2.3: 示例:CPU提供IntelCPU、HPCPU、AMDCPU;主板提供Intel_ATXMB、HS_ATXMB、JJ_ATXMB。
- 2.4: 注意:IntelCPU是不能与JJ_ATXMB和Intel_ATXMB匹配的
以下数据均是示例,不匹配真实数据
配件类型 | 针脚\插槽数 |
---|---|
IntelCPU | 1000 |
AMDCPU | 2000 |
HPCPU(惠普CPU) | 3000 |
HS_ATXMB(华硕主板) | 1000 |
JJ_ATXMB(技嘉主板) | 2000 |
Intel_ATXMB | 3000 |
3.不同模式的解决方案
- 对于工程师装机,只是知道CPU和主板的接口,而不知道具体实现,那么用工厂方法或者简单工厂即可实现。但是因为两个工厂模式都是对单个产品进行创建,无法做到产品间的相互关联,所以会出现IntelCPU(1000)和Intel_ATXMB(3000)不匹配的情况。(具体代码暂且不提供)
4.抽象工厂
-
4.1: 结构:
-
4.2: 说明:
- 4.2.1: AbstractFactory:抽象工厂,定义创建一系列产品对象的接口
- 4.2.2: ConcreteFactory:具体工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建
- 4.2.3: AbstractProduct:定义一类有关联产品对象的接口
- 4.2.4: ConcreteProduct:具体的产品实现对象,通常在具体工厂里面,会选择具体的产品实现对象,来创建符合抽象工厂定义的方法返回的产品类型的对象
- 4.2.5: Client:客户端,主要使用抽象工厂来获取一系列所需要的产品对象,然后面向这些产品对象的接口编程,以实现需要的功能
- 4.3: 伪代码:
/// <summary>
/// 声明一系列产品的操作接口
/// </summary>
public interface abstractfactory
{
/// 创建CPU的方法对象
CPUAPI cpu();
/// 创建主板的方法对象
MainBorderAPI mainBorder();
}
#region==系列产品接口==
/// <summary>
/// 声明一个产品接口:CPU
/// </summary>
public interface CPUAPI
{
//CPU的针脚数
int CPUPins { get; set; }
//输出CPU的针脚数
void PrintCPUPins();
}
/// <summary>
/// 声明一个产品接口:主板
/// </summary>
public interface MainBorderAPI
{
//主板的针脚插槽数
int MBHoles { get; set; }
//输出主板的插槽数
void PrintMBHoles();
}
#endregion
#region==CPU产品类==
/// <summary>
/// 实现CPU接口的具体产品类:Intel
/// </summary>
public class IntelCPU : CPUAPI
{
//因特尔的CPU针脚数1000
private int cpuPins = 1000;
//给接口的属性赋值
public int CPUPins
{
get { return cpuPins; }
set { cpuPins = value; }
}
public IntelCPU()
{
}
public void PrintCPUPins()
{
Console.WriteLine("IntelCPU Pins:" + this.CPUPins);
}
}......(还有两个在此不列出)
#endregion
#region==主板产品类==
/// <summary>
/// 实现主板接口的具体产品类:华硕ATX
/// </summary>
public class HS_ATXMB : MainBorderAPI
{
//华硕的插槽数1000
private int mbHoles = 1000;
//给接口的属性赋值
public int MBHoles
{
get { return mbHoles; }
set { mbHoles = value; }
}
public void PrintMBHoles()
{
Console.WriteLine("MBHoles:" + this.MBHoles);
}
}......(还有两个在此不列出)
#endregion
#region==实现抽象工厂==
/// <summary>
/// 抽象工厂实现1:IntelCPU+华硕MB
/// </summary>
public class ImplementFactory1:abstractfactory
{
public CPUAPI cpu()
{
return new IntelCPU();
}
public MainBorderAPI mainBorder()
{
return new HS_ATXMB();
}
}......(还有两个在此不列出)
#endregion
main()
{
//这个new ImplementFactory1();是用这个类的对象的 构造函数 来实例化抽象工厂接口对象
abstractfactory af = new ImplementFactory1();
//此处如果写成af.cpu()就出问题。因为该af接口对象是由构造函数实例化,
//那么它只会去调用实现该接口的类IntelCPU的构造函数,然后运行完构造函数则退出。
//所以必须是af.cpu().PrintCPUPins();
af.cpu().PrintCPUPins();
af.mainBorder().PrintMBHoles();
}
-
4.4 结果:
- 4.5: 存放:所有代码(C#)在github(https://github.com/liusa1997/DesignModel )供参考,由于初识设计模式,还需要仔细钻研
5.抽象工厂的优缺点
-
优点:
- 5.1: 分离接口与实现 :客户端使用抽象工厂来创建需要的对象,而客户端根本不知道具体的实现是谁,客户端只是面向产品的接口编程。也就是,客户端从具体的产品实现中解耦
- 5.2: 使得切换产品簇变得容易 : 因为一个具体的工厂实现代表的是一个产品簇,如上面的ImplementFactory1就是一个工厂,然后ImplementFactory2就是另一个。客户端选用不同的工厂实现,就相当于在切换不同的产品簇。
-
缺点:
- 5.1: 不太容易扩展新的产品 :比如要给产品簇增加一个新的产品,那么abstractfactory也要变,concertefactory也要变,abstractproduct也要变,concreteproduct仍然要变,所以不容易扩展。
- 5.2: 容易造成类层次复杂
6.参考文献
- 研磨设计