导航
2简单工厂
3工厂方法
3抽象工厂
4后记
1 为什么要使用工厂模式?
解耦
工厂模式的核心目的是将对象的创建与使用分离,先说对象的创建,我们习惯于在构造函数中进行一系列对象的初始化甚至逻辑处理,虽然这在开发中很常见,但如果仔细推敲,会发现其实并不符合常理。如果把汽车的发动机当作一个对象,设计图纸当作构造函数的参数,我们传入参数,然后发动机自己把自己生产出来了。是的,发动机自己生产了自己,是不是感觉有些怪异?但这确实是软件中的大部分情形,其实现实生活中的场景更符合逻辑,把图纸送到发动机生产工厂,由工厂完成发动机的生产制造。在软件开发中,大多数简单对象,并不需要使用工厂,但当一个对象的构造过程相对复杂、易变时,就需要考虑使用工厂将对象的创建过程解耦了。
再说对象的使用,在简单的小型项目中,直接使用new来获取对象简便快捷,并没有问题。但当项目具有一定规模,需要统一考虑扩展性(使用一个对象替换另一个对象),需要统一管理多个对象的创建过程、生命周期、依赖关系时,工厂就大有用武之地了。
抽象
先解释一下抽象的概念,在软件设计中,抽象是为解耦和扩展而生的。举个例子,我们打开电脑机箱找到主板上的南北桥芯片,可以看到它们是完全焊接在主板上的,这种不可替换的设计是高耦合不可扩展的。我们再找到内存条,发现它们可以拆卸并更换为其它品牌,这种可替换的设计即为可扩展。再稍深入思考一下,主板上的内存条为什么能安装不同的品牌?原因是有相关技术标准,比如长宽尺寸,针脚数量,通信标准等,不同的内存条厂商,只要遵循标准生产出来的内存条就能安装到同一块主板上。在软件开发中,让主板支持不同厂商内存条的能力称之为主板具备可扩展性,定义内存条接口标准称之为抽象,根据标准生产主板和内存条称之为面向抽象编程(面向接口编程)。
在软件设计中,只要是工厂模式,不论简单工厂、工厂方法、还是抽象工厂,生产构造出来的都是抽象对象,而非具体对象。这条规则非常重要,我们在小型项目中,效率优先,可以直接new出来具体的对象使用,但即然使用了工厂模式,说明项目具有了一定的复杂程度和规模,此时就应该统一考虑可扩展性了。
可测试性
做过单元测试的小伙伴应该深有体会,越是封闭的对象越难以测试。这很容易理解,要对一个对象进行测试,就需要从外部对它注入各种条件,然后观察它的反应,如果条件都注入不进去,何来的反应呢?构造函数对测试而言,天生就是一个无法测试的封闭黑盒,而工厂模式把这个黑盒从对象中解耦了出来,从而允许测试条件的注入。文章来源:https://www.wubayue.com
2 简单工厂
在一个工厂中,通过向静态方法传递不同的参数,生产并返回不同的产品,即简单工厂,又形象的称之为静态工厂方法(Static Factory Method),示例代码如下:
// 抽象产品
public interface ICellphone
{ }
// 具体产品
public class Xiaomi1Cellphone : ICellphone
{ }
public class Xiaomi2Cellphone : ICellphone
{ }
// 小米1和小米2是早期的小米手机型号
public enum CellphoneBrand
{
Xiaomi1,
Xiaomi2
}
// 简单工厂
public class CellphoneFactory
{
// 通过不同的参数构造并返回不同的对象
public static ICellphone Build(CellphoneBrand brand)
{
switch (brand)
{
case CellphoneBrand.Xiaomi1:
return new Xiaomi1Cellphone();
case CellphoneBrand.Xiaomi2:
return new Xiaomi2Cellphone();
// 缺点:当产品扩展时存在修改,不符合开闭原则
// ...
default:
return null;
}
}
}
1994年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides四位合著了《设计模式-可复用面向对象软软件的基础》,书中总结了23种常用的设计模式,后来成为了大名鼎鼎的GoF23(四人帮:Gang of Four)。
简单工厂虽然不在GoF23当中,但实际开发中也较为常见,它的优点是非常简单,缺点是当产品扩展时,需要修改工厂中的代码,不符合“开闭原则”。文章来源:https://www.wubayue.com
3 工厂方法
在介绍工厂方法模式前,先简单介绍一下“开闭原则”,开闭原则在上世纪80年代就被总结了出来,其核心思想是对修改关闭,对扩展开放。对修改关闭指的是应避免修改已存在的代码,因为这部分代码是经过测试的稳定代码,任何修改都可能引入新的BUG;对扩展开放指的是应允许增加新功能,因为软件存在的价值就是不断满足用户新的需求。开闭原则看似简单,实则包含了众多软件设计思想与实现细节,整个“SOLID原则”几乎都是围绕它展开的,后续专门写一篇文章介绍开闭原则,此处就不作详述了。
工厂方法模式的核心思想是每增加一种产品,就对应的增加一个工厂,为了贯彻这个思路,方便的为新增产品匹配工厂,它对工厂也进行了抽象,示例代码如下:
// 抽象产品
public interface ICellphone
{ }
// 具体产品
public class XiaomiCellphone : ICellphone
{ }
public class RedmiCellphone : ICellphone
{ }
// 抽象工厂
public interface ICellphoneFactory
{
ICellphone Build();
}
// 具体工厂
public class XiaomiCellphoneFactory : ICellphoneFactory
{
public ICellphone Build()
{
return new XiaomiCellphone();
}
}
public class RedmiCellphoneFactory : ICellphoneFactory
{
public ICellphone Build()
{
return new RedmiCellphone();
}
}
工厂方法符合开闭原则,但它也存在缺陷:一个工厂只能生产一种产品,工厂数量会随着产品数量的增加而不断增加。文章来源:https://www.wubayue.com
4 抽象工厂
在具有一定规模的实际项目中,对象通常不会孤立的出现,而是以系列的方式出现,比如小米和红米,两个品牌下不会只有一款产品,而会有一系列产品:手机、电视、电脑、智能音箱、扫地机器人等等。抽象工厂模式专为生产一系列商品而设计,它解决了工厂方法中工厂数量随着产品数量不断增加的弊端,也更贴合实际的项目场景,示例代码如下:文章来源:https://www.wubayue.com
// 抽象产品
public interface ICellphone
{ }
public interface ITelevision
{ }
// 具体产品
public class XiaomiCellphone : ICellphone
{ }
public class RedmiCellphone : ICellphone
{ }
public class XiaomiTelevision : ITelevision
{ }
public class RedmiTelevision : ITelevision
{ }
// 抽象工厂
public interface IFactory
{
ICellphone BuildCellphone();
ITelevision BuildTelevision();
}
// 具体工厂,生产一个系列的产品
public class XiaomiFactory : IFactory
{
public ICellphone BuildCellphone()
{
return new XiaomiCellphone();
}
public ITelevision BuildTelevision()
{
return new XiaomiTelevision();
}
}
public class RedmiFactory
{
public ICellphone BuildCellphone()
{
return new RedmiCellphone();
}
public ITelevision BuildTelevision()
{
return new RedmiTelevision();
}
}
5 后记
很早之前就想将GoF23中的设计模式以这种图、文、代码的方式整理成文,一直拖延直到最近注册了心仪的域名wubayue.com,终于开始将软件开发这些年的一些经验整理成文。后续会陆续补充完整GoF23,敬请关注。文章来源:https://www.wubayue.com
<全文完>