引言
工厂模式是比较常用的设计模式,主要用于返回实例对象,将多种类型的创建集中放在一个地方创建。工厂模式一般分为简单工厂模式,工厂方法模式,抽象工厂模式。各有优缺点,今天这里还加入了简单工厂模式的另一种实现,添加反射实现简单工厂。
例子
一个很简单的例子:有个富豪想买奔驰,宝马,福特汽车,开出去玩。
代码:
什么模式都不用
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Client : MonoBehaviour {
public abstract class Car
{
public abstract void Dirve();
}
public class Benz : Car
{
public override void Dirve()
{
Debug.Log("Dirve a Benz");
}
}
public class BMW : Car
{
public override void Dirve()
{
Debug.Log("Dirve a BMW");
}
}
public class Ford : Car
{
public override void Dirve()
{
Debug.Log("Dirve a Ford");
}
}
void Start () {
Car car1 = new Benz();
car1.Dirve();
Car car2 = new BMW();
car2.Dirve();
Car car3 = new Ford();
car3.Dirve();
}
}
上述代码已实现功能,但是上述代码有一些这样的情况:
1.如果有另一个富豪他也想买这三种车来开,那么在他的类里面也要写这三个类然后new3辆车——代码重复
2.一个富豪,他里面有三种汽车如何制造出来的代码,作为一个客户,他却要懂生产——职责混淆
但是,聪明的你肯定马上就想到,直接把这三个汽车类单独抽出来,这样,客户直接new出来就可以了,职责也不混淆,别的富豪要车也可以直接new出来,代码也不重复。
那我们接下来看,假设我的汽车抽象类,稍微复杂那么一丢丢,如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Car {
public float Length;
public float Width;
public float High;
public float MaxSpeed;
public abstract void Dirve();
public abstract void Test(); //出厂测试
}
1.new一辆奔驰车出来,这些字段都需要赋值,还要运行一个出厂测试方法,如果这些赋值,方法在客户client类里面写,那么其他所有客户都要写这些代码,代码重复。
但是你肯定马上说:“不对,我可以写在汽车类的构造函数里面”,当然可以,不过,如果要干的事情特别多,几十行上百行的代码,全放构造函数里面,有点违背设计原则,在构造函数做太复杂的操作,当出错时发现错误有时会很困难。特别是定义为全局变量时,程序还没有运行、就出错了。构造函数应该尽量简单,一眼望穿,最好能保证永不出现构造失败的情况。
2.如果Benz(奔驰车)多了一个子类,奔驰二代Benz2,现在买奔驰车返回这个类的对象,那么所有客户端的new Benz(),都要换成new Benz2(),而如果做成工厂模式,直接在返回奔驰车的代码哪里更改一下返回Benz2类的对象就可以了,反正返回的都是Car(抽象父类)——方便维护。
3.如果所有的汽车类,在返回对象之前,又要添加新的逻辑了,那么需要一个个类的找,修改,如果全集中在工厂类中,直接在工厂类中修改即可,避免遗漏,还是——方便维护。
所以工厂模式主要是有代码重用,分割职责解耦,便于维护的特点。
那么接下来来看看工厂模式:
还是上面的例子:有个富豪想买奔驰,宝马,福特汽车,开出去玩。
一、简单工厂
汽车类
public abstract class Car {
public abstract void Dirve();
}
奔驰
public class Benz : Car{
public override void Dirve()
{
Debug.Log("Dirve a Benz");
}
}
宝马
public class BMW : Car {
public override void Dirve()
{
Debug.Log("Dirve a BMW");
}
}
福特
public class Ford : Car {
public override void Dirve()
{
Debug.Log("Dirve a Ford");
}
}
简单工厂类
public class SimpleFactory {
public static Car CreatCar(string car)
{
Car newCar = null;
if (car == "Benz")
{
newCar = new Benz();
}
else if (car == "BMW")
{
newCar = new BMW();
}
else if (car == "Ford")
{
newCar = new Ford();
}
return newCar;
}
}
客户类
public class ClientSimple : MonoBehaviour {
void Start () {
//简单工厂
Car car1 = SimpleFactory.CreatCar("Benz");
car1.Dirve();
Car car2 = SimpleFactory.CreatCar("BMW");
car2.Dirve();
Car car3 = SimpleFactory.CreatCar("Ford"); ;
car3.Dirve();
}
}
上述代码,客户不需要知道怎么生产汽车(解耦),多个客户要车都可以调用工厂得到汽车,如果汽车初始要做很多事情,都可以在工厂类里面newcar之后调各自类的方法,赋值,如果什么模式都不用,直接new的方式,就要写在类的构造函数里面或者直接在客户类写,不太好(重复)。
上述代码也会有一些情况,如果我需要新增一种车,比如莱斯莱斯,那么我就要在工厂类里面多加一个else if条件判断。工厂类就要进行修改——扩展性不强,这个工厂类包含类所有类的创建,很重要,频繁改动如果出错影响面广。
如果种类不多,简单工厂用起来还是不错的。
二、工厂方法
汽车类同上,
抽象工厂类:
public abstract class Factory {
public abstract Car CreatCar();
}
奔驰工厂
public class BenzFactory : Factory {
public override Car CreatCar()
{
return new Benz();
}
}
宝马工厂
public class BMWFactory : Factory {
public override Car CreatCar()
{
return new BMW();
}
}
福特工厂
public class FordFactory : Factory {
public override Car CreatCar()
{
return new Ford();
}
}
客户类
public class ClientSimple : MonoBehaviour {
void Start () {
//工厂方法
Car car1 = new BenzFactory().CreatCar();
car1.Dirve();
Car car2 = new BMWFactory().CreatCar();
car2.Dirve();
Car car3 = new FordFactory().CreatCar();
car3.Dirve();
}
}
上述代码,添加新类型的莱斯莱斯车,需要新增莱斯莱斯车类和莱斯莱斯工厂类,工厂父类Factory不需要修改,这样对于已有的工厂不会产生任何潜在的改动影响——便于扩展。
对于这种方式,和直接new Benz(), new BMW(), new Ford()有点像,每一种车有一个工厂类,不过还是上面讲的,如果返回一个实例要做很多事情,不好直接写在构造函数里面,可以写在具体的工厂类里面,而且如果工厂父类要添加新的逻辑,所有工厂同时享有。
工厂方法就是每加一种类型,要添加汽车类,还要添加具体工厂类,增加开销。
三、抽象工厂
抽象工厂其实就是工厂方法的扩展,以前工厂方法父类,只生产汽车,现在工厂作死新开了一条火箭生产流水线,要可以制作火箭了,那么工厂父类就变成这样:
public abstract class Factory {
public abstract Car CreatCar();
public abstract Rocket CreatRocket();
}
那么奔驰工厂,宝马工厂,福特工厂类都要添加生产火箭的方法,奔驰牌火箭,宝马牌火箭,福特牌火箭都属于继承自抽象火箭类Rocket,和之前的Car是一模一样的逻辑。代码就不放了,看这个工厂父类就知道了,这就是抽象工厂和工厂方法的区别。
这个抽象工厂,如果增加一种新产品种类,工厂父类和所有子类工厂,都需要改动。
是否使用看自己项目需求了。
四、反射+简单工厂
public class SimpleFactory {
public static Car CreatCar(string car)
{
Car newCar = null;
if (car == "Benz")
{
newCar = new Benz();
}
else if (car == "BMW")
{
newCar = new BMW();
}
else if (car == "Ford")
{
newCar = new Ford();
}
return newCar;
}
}
上面的是之前的简单工厂,主要就是不便于扩展,新增一种车,就要在工厂类里面多加一个条件判断。
现在添加反射实现简单工厂,代码如下:
public class ReflectionSimpleFactory {
public static Car CreatCar(string carClass)
{
//获取当前程序集
Assembly ass = Assembly.GetCallingAssembly();
//获取程序集中的类
Type t = ass.GetType(carClass);
//创建类的实例对象
Car o = (Car)Activator.CreateInstance(t);
return o;
}
}
客户类还是和之前简单工厂一样调用
public class ClientSimple : MonoBehaviour {
void Start () {
//反射+简单工厂
Car car1 = ReflectionSimpleFactory.CreatCar("Benz");
car1.Dirve();
Car car2 = ReflectionSimpleFactory.CreatCar("BMW");
car2.Dirve();
Car car3 = ReflectionSimpleFactory.CreatCar("Ford");
car3.Dirve();
}
}
如果新增一种劳斯莱斯车型,只需要添加新类RR这一个类就可以,工厂类不用做其他改动,解决了简单工厂的扩展性问题。
public class RR : Car {
public override void Dirve()
{
Debug.Log("Dirve a RR");
}
}
public class ClientSimple : MonoBehaviour {
void Start () {
//反射+简单工厂
Car car1 = ReflectionSimpleFactory.CreatCar("Benz");
car1.Dirve();
Car car2 = ReflectionSimpleFactory.CreatCar("BMW");
car2.Dirve();
Car car3 = ReflectionSimpleFactory.CreatCar("Ford");
car3.Dirve();
Car car4 = ReflectionSimpleFactory.CreatCar("RR");
car4.Dirve();
}
}
反射也存在效率问题,我这里创建基本什么都没干,循环创建3辆车,100万次试了一下,测试代码如下:
using System.Diagnostics;
using UnityEngine;
public class Client_Factory : MonoBehaviour {
void Start () {
Stopwatch st = new Stopwatch();
st.Reset();
st.Start();
for (int i = 0; i < 1000000; i++)
{
简单工厂
//Car car1 = SimpleFactory.CreatCar("Benz");
car1.Dirve();
//Car car2 = SimpleFactory.CreatCar("BMW");
car2.Dirve();
//Car car3 = SimpleFactory.CreatCar("Ford"); ;
car3.Dirve();
工厂方法
//Car car1 = new BenzFactory().CreatCar();
car1.Dirve();
//Car car2 = new BMWFactory().CreatCar();
car2.Dirve();
//Car car3 = new FordFactory().CreatCar();
car3.Dirve();
//反射+简单工厂
Car car1 = ReflectionSimpleFactory.CreatCar("Benz");
//car1.Dirve();
Car car2 = ReflectionSimpleFactory.CreatCar("BMW");
//car2.Dirve();
Car car3 = ReflectionSimpleFactory.CreatCar("Ford");
//car3.Dirve();
}
st.Stop();
UnityEngine.Debug.Log(st.ElapsedMilliseconds.ToString()+ " ms");
}
}
用时分别是简单工厂:237ms,工厂方法:326ms,反射+简单工厂:10305ms,前两种基本差别不大,反射之后差了几十倍。这个类创建很简单,如果类复杂,反射可能更耗时,不过这是100万次,日常开发基本也不太可能调用这么多次,具体是否用,自己项目衡量。