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

前言

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

工厂模式的定义:

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

一个好理解的例子:

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

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

  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();
        }
    }
相关推荐
轻口味44 分钟前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami1 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
吃杠碰小鸡1 小时前
lodash常用函数
前端·javascript
emoji1111112 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼2 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_748250032 小时前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
vue3 如何使用 mounted
前端·javascript·vue.js
m0_748235952 小时前
web复习(三)
前端
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter
麦兜*2 小时前
轮播图带详情插件、uniApp插件
前端·javascript·uni-app·vue