C#工厂模式——简单工厂、工厂方法、反射+简单工厂、抽象工厂

C# 专栏收录该内容
1 篇文章 0 订阅

引言

        工厂模式是比较常用的设计模式,主要用于返回实例对象,将多种类型的创建集中放在一个地方创建。工厂模式一般分为简单工厂模式,工厂方法模式,抽象工厂模式。各有优缺点,今天这里还加入了简单工厂模式的另一种实现,添加反射实现简单工厂。

例子

         一个很简单的例子:有个富豪想买奔驰,宝马,福特汽车,开出去玩。

代码:

什么模式都不用

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万次,日常开发基本也不太可能调用这么多次,具体是否用,自己项目衡量。

  • 8
    点赞
  • 1
    评论
  • 18
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值