关于工厂模式的定义和实现

前言

部分摘自:《设计模式之禅》 这本书

工厂模式的定义:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使-一个 类的实例化延迟到其子类。

一个好理解的例子:

东汉《风俗通》记录了一则神话故事, 开天辟地,未有人民,女娲搏黄土做人",讲述的内容就是大家非常熟悉的女娲造人的故事。开天辟地之初,大地上并没有生物,只有苍茫大地,纯粹而洁净的自然环境,寂静而又寂寞, 于是女娟决定创造一个新物种(即人类)来增加世界的繁荣,怎么制造呢? 别忘了 女娲是神仙,没有办不到的事情,造人的过程是这样的,首先,女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,最后放置到大地上生长,工艺过程是没有错的,但是意外随时都会发生。 第一次烤泥人,感觉应该熟了,往大地上一放,哇没烤熟!于是一个白人诞生了! 第二次烤泥人,上一次没烤熟,这次多烤一会儿, 放到世间一看,嘿,熟过头了,于是黑人诞生了! 第三次烤泥人,边烧制一边察看,直到表皮微责,嘿,刚好,于是黄色人种出现了!

这个造人过程是比较有意思的,是不是可以通过代码来实现这个过程呢? 在面向对象的思维中,万物皆对象,是对象我们就可以通过软件设计来实现。

  1. 首先对造人过程进行分析,该过程步及三个对象:女娲、八卦炉、三种不同肤色的人。
  2. 女娲可以使用场景类Client来表示。
  3. 八卦炉类似于一个工厂,负责制造产品(即人类)。
  4. 三种不同肤色的人,他们都是同一个抽象类(人类)下的不同实现,都是人类,只是肤色、语言不同。对于八卦炉来说都是它生产出的产品。

先定义工厂接口:

csharp 复制代码
    public interface IFactory
    {
        public T CreateHuman<T>() where T : Human;
    }

定义工厂的实现类:

csharp 复制代码
	public class HumanFactory: IFactory
    {
        public T CreateHuman<T>() where T : Human
        {
            // 动态实例化泛型类
            return Activator.CreateInstance<T>();
        }
    }

定义人的抽象类:

csharp 复制代码
  	public abstract class Human
    {
        public abstract void GetColor();
    }

定义不同肤色的人类:

csharp 复制代码
 	public class YellowHuman : Human
    {
        public override void GetColor()
        {
            Console.WriteLine("我是黄种人!");
        }
    }
    
	public class WhiteHuman : Human
    {
        public override void GetColor()
        {
            Console.WriteLine("我是白种人!");
        }
    }

	public class BlackHuman : Human
    {
        public override void GetColor()
        {
            Console.WriteLine("我是黑人!");
        }
    }

客户端:

csharp 复制代码
	class Program
    {
        static void Main(string[] args)
        {
			// 实例化工厂类
            HumanFactory factory = new HumanFactory();
 
            Human yellowHuman = factory.CreateHuman<YellowHuman>();
            yellowHuman.GetColor();

            Human whiteHuman = factory.CreateHuman<WhiteHuman>();
            whiteHuman.GetColor();
            
            Human blackHuman = factory.CreateHuman<BlackHuman>();
            whiteHuman.GetColor();
        }
    }

执行结果:

工厂模式的优点呢?我们为什么要使用工厂模式?

首先,良好的封装性代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象。只要知道这个产品的类名即可,不用知道创建对象的艰辛过程,降低模块间的耦合。
其次,工厂模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类。就可以完成 拥抱变化。例如在我们的例子中,需要增加一个棕色人种,则只需要增加一个BrownHuman类 ,工厂类不需要任何修改就可完成系统扩展。
再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。
在数据库开发中,大家应该能够深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MySQL切换到Oracle,需要改动的地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。
最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象 类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!

工厂模式的适用场景

首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类。 例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择: POP3、 IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail, 然后定义对邮件的操作方法,用不同的方法实现三个具体的产品类(也就是连接方式)。再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计,可以做到完美的扩展。

简单工厂模式

我们这样考虑一个问题: 一个模块仅需要一个工厂类,没有必要把它生产出来,使用静态的方法就可以了,根据这一要求, 我们把上例中的HumanFactory修改一下,把生产Human的方法改成静态的,并且去掉IFactory接口就行了。 工厂类:

csharp 复制代码
	 public class HumanFactory
    {
        public static T CreateHuman<T>() where T : Human
        {
            return Activator.CreateInstance<T>();
        }
    }

客户端:

csharp 复制代码
 class Program
    {
        static void Main(string[] args)
        {

            //HumanFactory factory = new HumanFactory();

            Human yellowHuman = HumanFactory.CreateHuman<YellowHuman>();
            yellowHuman.GetColor();

            Human whiteHuman = HumanFactory.CreateHuman<WhiteHuman>();
            whiteHuman.GetColor();

            Human blackHuman = HumanFactory.CreateHuman<BlackHuman>();
            blackHuman.GetColor();
        }
    }
相关推荐
ziyue757517 分钟前
vue修改element-ui的默认的class
前端·vue.js·ui
树叶会结冰38 分钟前
HTML语义化:当网页会说话
前端·html
冰万森43 分钟前
解决 React 项目初始化(npx create-react-app)速度慢的 7 个实用方案
前端·react.js·前端框架
牧羊人_myr1 小时前
Ajax 技术详解
前端
浩男孩1 小时前
🍀封装个 Button 组件,使用 vitest 来测试一下
前端
蓝银草同学1 小时前
阿里 Iconfont 项目丢失?手把手教你将已引用的 SVG 图标下载到本地
前端·icon
李广坤1 小时前
状态模式(State Pattern)
设计模式
布列瑟农的星空1 小时前
重学React —— React事件机制 vs 浏览器事件机制
前端
一小池勺2 小时前
CommonJS
前端·面试