简单工厂模式,工厂方法模式,抽象工厂模式
本文使用C#和Vuejs对简单工厂模式,工厂方法模式,抽象工厂模式进行了描述
分类: 创建型模式
参见
简单工厂模式
定义
- 该模式又被称为静态工厂方法模式;
- 简单工厂模式是由一个工厂对象决定创建出哪一种产品的实例;
- 简单工厂模式就是可以根据传入的参数的不同,返回不同的对象;
- 设计模式中的工厂模式借鉴了现实生活中工厂的概念。
示例
// 抽象汽车类, 也可以是接口
public abstract class Car
{
public string Type { get; set;}
}
// 特斯拉
public class Tesla: Car
{
public Tesla ()
{
this.Type = "电动力车";
Console.Writeline("生产特斯拉电动汽车");
}
}
// 奔驰
public class Benz: Car
{
public Benz ()
{
this.Type = "汽油动力车";
Console.WriteLine("生产奔驰汽车");
}
}
// 工厂
public class CarFactory
{
// 静态工厂方法
public static Car Build(string carName)
{
Car car = null;
if ("Tesla" == carName)
{
car = new Tesla();
} else if ("Benz" == carName)
{
car = new Benz();
} else
{
Console.WriteLine("没找到要生产的汽车Name");
}
return car;
}
}
// 消费者
public class Customer
{
public static void Main(String[] args)
{
CarFactory.Build("Benz");
CarFactory.Build("Tesla");
CarFactory.Build("Audi");
}
}
JavaScript 示例
var CarFactory = (function () {
var Car = function (carName, carType) {
this.CarName = carName;
this.CarType = carType;
};
return function (carName, carType) {
return new Car(carName, carType);
}
})();
// 消费
var audi = new CarFactory("Audi", "汽油车");
var benz = new CarFactory("Benz", "汽油车");
var tesla = new CarFactory("Tesla", "电动车");
Vuejs 示例
<html>
<head></head>
<body>
<div id="app">
</div>
<div id="test">
</div>
<script src="https://unpkg.com/vue"></script>
<script>
// 工厂
var buildTemplate = function (name) {
var template = ''
if ('app' === name) {
template = '<p style="color:red;">Hello ' + name + '</p>'
} else if ('test' === name) {
template = '<p style="color:blue;">Hello ' + name + '</p>'
}
return template
}
const appName = 'test'
// 消费者
var app = new Vue({
el: '#'+ appName,
template: buildTemplate(appName)
})
</script>
</body>
</html>
优点
- 将类实例化的操作与“使用”操作分离开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端(消费者)代码中显式指定,实现了解耦;也就是说,使用者可以直接消费产品而无需知道其生产的细节
- 把初始化实例时的工作放到工厂里进行,使代码更容易维护;
- 更符合面向对象的原则 -> 面向接口编程;
缺点
- 如果需要新的产品,只能加if-else或者Switch加case,也就是硬编码,违反了开闭原则,对维护和扩展不够友好,所以一般使用在条件不多变化不大的情况下。
- 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
- 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构;
使用场景
- 工厂类负责创建的对象比较少,不会造成工厂方法中的业务逻辑太过复杂
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心
工厂方法模式
定义
工厂方法模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现
示例
// 抽象汽车类, 也可以是接口
public abstract class Car
{
public string Type { get; set;}
}
// 特斯拉
public class Tesla: Car
{
public Tesla ()
{
this.Type = "电动力车";
Console.Writeline("生产特斯拉电动汽车");
}
}
// 奔驰
public class Benz: Car
{
public Benz ()
{
this.Type = "汽油动力车";
Console.WriteLine("生产奔驰汽车");
}
}
// 工厂接口
public interface ICarFactory
{
public Car Build();
}
public class BenzFactory: ICarFactory
{
public Car Build()
{
return new Benz();
}
}
public class TeslaFactory: ICarFactory
{
public Car Build()
{
return new Tesla();
}
}
// 消费者
public class Customer
{
public static void Main(String[] args)
{
ICarFactory factory = null;
Car teslaCar = null;
Car benzCar = null;
// 生产 Tesla
factory = new TeslaFactory();
teslaCar = facotry.Build();
// 生产 Benz
factory = new BenzFactory();
benzCar = factory.Build();
}
}
Vuejs 示例
<html>
<head></head>
<body>
<div id="app">
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var appTemplateFactory = {
build: function () {
return '<p style="color:red;">Hello app </p>'
}
}
var testTemplateFactory = {
build: function () {
return '<p style="color:blue;">Hello test </p>'
}
}
var buildApp = function () {
var factory = testTemplateFactory; // appTemplateFactory
var template = factory.build()
return new Vue({
el: '#app',
template: template
})
}
buildApp()
</script>
</body>
</html>
优点
- 符合开闭原则,容易扩展,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可;
- 符合单一职责原则,每个具体工厂类只负责创建对应的产品;
缺点
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度;
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
- 一个具体工厂只能创建一种具体产品;
讨论
既然是一个工厂对应一个目标对象,那我还要工厂干嘛,我直接 new 我的目标对象不就可以了
思考以下场景:
假如我有三个对象
Car car1 = new Tesla();
Car car2 = new Tesla();
Car car3 = new Tesla();
那么此时我不想要 Tesla 了,我想换成Benz,此时是不是需要修改三处(可能分散在系统各个类库里),这个时候工厂方法模式的好处在哪里体现呢?
CarFactory factory = new TeslaFactory();
Car car1 = factory.build();
Car car2 = factory.build();
Car car3 = factory.build();
这个时候,要换成Benz, 就修改
CarFactory factory = new BenzFactory();
一处就可以了。
别忘了,你在加 Teacher 的时候,是符合开闭原则的,而简单工厂模式是不符合的。
使用场景
- 客户端不知道它所需要的对象的类。客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;
- 抽象工厂类通过其子类来指定创建哪个对象。对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象
抽象工厂模式
定义
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类;
抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品;
抽象工厂模式主要能实现很多不同的类的修改。
示例
// 产品抽象接口
public interface IProduct {}
// 车 抽象接口
public interface ICar: IProduct {}
// 自行车 抽象接口
public interface IBike: IProduct {}
// 具体产品 - 特斯拉电动车
public class TeslaCar: ICar
{
public TeslaCar ()
{
Console.WriteLine("生产 Tesla 电动车");
}
}
// 具体产品 - 特斯拉自行车
public class TeslaBike: IBike
{
public TeslaBike ()
{
Console.WriteLine("生产 Tesla 自行车");
}
}
// 具体产品 - 奔驰汽车
public class BenzCar: ICar
{
public BenzCar ()
{
Console.WriteLine("生产 奔驰 汽油车");
}
}
// 具体产品 - 奔驰自行车
public class BenzBike: IBike
{
public BenzBike ()
{
Console.WriteLine("生产 奔驰 自行车");
}
}
// 抽象工厂
public interface IProductFactory
{
ICar BuildCar ();
IBike BuildBike ();
}
// 具体工厂类 - 特斯拉工厂
public class TeslaProductFactory: IProductFactory
{
public ICar BuildCar ()
{
return new TeslaCar();
}
public IBike BuildBike ()
{
return new TeslaBike();
}
}
// 具体工厂类 - 奔驰工厂
public class BenzProductFactory: IProductFactory
{
public ICar BuildCar ()
{
return new BenzCar();
}
public IBike BuildBike ()
{
return new BenzBike();
}
}
// 消费者
public class Customer
{
public static void Main()
{
IProductFactory factory = null;
factory = new TeslaProductFactory();
var car = factory.BuildCar();
var bike = factory.BuildBike();
/*
当要修改产品,比如我们不要特斯啦的产品了,我要奔驰的,那么就只需要改动一个地方
factory = new TeslaProductFactory(); => factory = new BenzProductFactory();
其他地方不需要修改
*/
}
}
结果
生产 Tesla 电动车
生产 Tesla 自行车
生产 奔驰 汽油车
生产 奔驰 自行车
Vuejs 示例
<html>
<head></head>
<body>
<div id="app">
</div>
<script src="https://unpkg.com/vue"></script>
<script>
var appFactory = {
buildTemplate: function () {
return '<div><p style="color:red;">Hello app </p><p> {{ message }} </p></div>'
},
buildData: function () {
return { message: 'app message'}
}
}
var testFactory = {
buildTemplate: function () {
return '<div><p style="color:red;">Hello test </p><p> {{ message }} </p></div>'
},
buildData: function () {
return { message: 'test message'}
}
}
var factory = testFactory; // testFactory
var buildApp = function () {
var template = factory.buildTemplate()
var data = factory.buildData()
console.log(data)
return new Vue({
el: '#app',
template: template,
data: data
})
}
buildApp()
</script>
</body>
</html>
讨论
每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式针对的是多个产品等级结构。有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品族,也就会在工厂等级结构中发现多少个具体工厂。总结一下我认为可以应用到抽象工厂模式的实际例子:
- 两种产品:特斯拉和奔驰,两种产品等级:车和自行车
- 两种产品:PC和MAC,两种产品等级:RAM,CPU
- 两种产品:水果和蔬菜,两种产品等级:南方特产,北方特产
- 两种产品:男人和女人,三种产品等级:黄种人,黑人,白人
就类似这种结构的对象关系都可以用抽象工厂模式来构造。。。。。
优点
- 分离接口和实现
- 切换产品族变的异常方便
缺点
- 如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类;比如增加一个滑板车(特斯拉滑板车,奔驰滑板车)
使用场景
为创建一组对象提供了一种解决方案
总结
- 简单工厂模式
简单工厂类负责了所有产品的创建逻辑,当需要引进一个新的产品时,就不得不修改工厂类的产品创建逻辑,当产品类型较多时有可能造成工厂类的产品创建逻辑过于复杂,不利于系统的维护和扩展 - 工厂方法模式
对简单工厂模式的设计优化,简单工厂模式中如果新增一类产品,就需要修改工厂静态方法的产品创建逻辑,而使用工厂方法模式,只需要新扩展出一个新的工厂子类和产品类,使系统具有了良好的扩展性和维护性 - 抽象工厂模式
为创建一组(一族)对象提供了一种解决方案,与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一组(一族)产品。
本文完。
参考资料: