一文详解“工厂模式”以及python语言的实现

2019-04-13 17:19发布

一、什么是“工厂模式”——factory pattern

工厂模式,也称之为“简单工厂模式”或者是“静态工厂模式工厂模式(Factory Pattern)是 程序设计中 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式 中,我们在创建对象时不会对客户端暴露创建逻辑,所谓的“创建逻辑”是指我必须要知道创建函数的构造函数的逻辑组成,才能创建对象。 比如: class Student:     def __init__(self,name,age):         self.name=name         self.age=age student=Student('张三',23) 在上面的例子中,要想创建一个学生对象,我就必须要知道学生的 姓名 、年龄 这两项基本信息才能创建,这两个信息,就是所谓的创建逻辑,在实际中,可能知道的信息还要更多我才能创建一个实例,因为在同龄人中 姓名也相同的大有人在,我还需要知道他多高,性别、喜欢什么、籍贯等等才能完全创建一个学生实例。那有没有办法在不知道学生姓名和这些信息的情况下创建一个学生实例呢?这就是工厂模式要做的事情。 所谓”工厂模式“就是专门提供一个”工厂类“去创建对象,我只需要告诉这个工厂我需要什么,它就会返回给我一个相对应的对象。 说的更通俗一点,就是专门创建类型的实例的工厂(类) 

二、工厂模式的使用场景

使用场景:您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。  总结:如果要创建一系列复杂的对象,需要提供很多的创建信息;或者是我想要隐藏创建对象时候的“代码逻辑”,就需要使用“工厂模式” 优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了,不需要知道其他详细信息。 2、作为调用者而言,屏蔽产品的具体实现,调用者只关心产品的接口。 缺点:当有新的类型要加入到系统中时,必须修改工厂类,加入必要的处理逻辑,这违背了“开闭原则”。在简单工厂模式中,所有的产品都是由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。(工厂方法模式和工厂模式是有区别的哦) 使用实例: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

三、工厂模式的python代码实现

比如我需要创建很多的图形对象,这些图形包括三角形、圆、矩形、椭圆等等,当然我们可以使用每一个类型的构造函数去创建。但是我们不这样做,我们只需要专门创建一个“工厂类”,然后在需要创建某个实例的时候告诉它我需要什么类,就可以啦,如下代码所示: import math #定义4个图形类,并且每一个图形都有一个可以计算面积的方法 class Circle:     def Area(self,radius):         return math.pow(radius,2)*math.pi class Rectangle:     def Area(self,longth,width):         return 2*longth*width class Triangle:     def Area(self,baselong,height):         return baselong*height/2 class Ellipse:     def Area(self,long_a,short_b):         return long_a*short_b*math.pi #================================= #定义创建对象的一个工厂 class Factory:     def create_shape(self, name):         if name =='Circle':             return Circle()         elif name == 'Rectangle':             return Rectangle()         elif name == 'Triangle':             return Triangle()         elif name == 'Ellipse':             return Ellipse()         else:             return None if __name__=='__main__':     factory=Factory()     circle=factory.create_shape('Circle')     circle_area=circle.Area(2)     print(f'这是一个圆,它的面积是:{circle_area}')     rectangle=factory.create_shape('Rectangle')     rectangle_area=rectangle.Area(2,3)     print(f'这是一个长方形,它的面积是:{rectangle_area}')     triangle=factory.create_shape('Triangle')     triangle_area=triangle.Area(2,3)     print(f'这是一个三角形,它的面积是:{triangle_area}')     ellipse=factory.create_shape('Ellipse')     ellipse_area=ellipse.Area(3,2)     print(f'这是一个椭圆,它的面积是:{ellipse_area}') 上面代码的运行结果为: 这是一个圆,它的面积是:12.566370614359172
这是一个长方形,它的面积是:12
这是一个三角形,它的面积是:3.0
这是一个椭圆,它的面积是:18.84955592153876 从上面可以看出,我们在创建某一个图形类的时候,根本不关心这个类的构造函数到底是什么,甚至它都没有自定义的构造函数,这并不影响,我们只需要告诉工厂,你给我创建一个什么类即可,除了这点信息以外,不需要知道任何的额外信息,这就是所谓的“隐藏创建类的代码逻辑” 。 但是上面的方法依然有一个缺点: 简单工厂模式实现了生成产品类的代码跟客户端代码分离,在工厂类中你可以添加所需的生成产品的逻辑代码,但是问题来了,优秀的java代码是符合“开放-封闭”原则的,也就是说对扩展开发,对修改关闭,如果你要加一个产品类C,你就要修改工厂类里面的生成产品的代码,在这里你就要增加if-else判断。对于这个问题,我们的工厂方法模式就可以解决这个问题。

四、简单工厂模式所存在的问题

根据程序设计的“开放-封闭原则”。即所谓的对扩展开放,对修改关闭。就是如果我想要拓展一个类中的相应功能,是完全可以的,我只需要边编写专门的拓展模块,并不需要修改原来类中的任何代码。(如果学过C#,里面有一种拓展方法,就是专门针对这种情况的)。 简单工厂模式实现了生成产品类的代码跟客户端代码分离,(这里的生成产品类指的就是上面例子中的那些图形类,客户端指的是需要调用上面图形类种方法或属性的程序员)。在工厂类中你可以添加所需的生成产品的逻辑代码,但是问题来了,优秀的代码是符合“开放-封闭”原则的,也就是说对扩展开放,对修改关闭,如果你要加一个图形类,比如平行四边形类,要使得依然能够通过工厂类型创建对象,就要修改工厂类里面的产生对象的代码,在这里你就要增加if-else判断,需要修改原来工厂函数的代码,这不就是要修改源代码吗?这就一定程度上又违背了“开放-封闭原则”。 对于这个问题,我们的工厂方法模式就可以解决这个问题。(请参见下一篇) 补充:上面的“工厂类”对于像C#和java这样的语言来说,是必备的,因为它们的函数是依托于类的,但是因为python是特别灵活的语言,我不需要工厂类Factory,直接用一个函数代替也是完全没有问题的。如下代码,当然,根据设计模式的原则,最好还是创建一个工厂类Factory,然后让这个类里面的创建函数去创建类。 直接用一个函数代替工厂类,如下: def create_shape(name):         if name =='Circle':             return Circle()         elif name == 'Rectangle':             return Rectangle()         elif name == 'Triangle':             return Triangle()         elif name == 'Ellipse':             return Ellipse()         else:             return None if __name__=='__main__':     circle=create_shape('Circle')     circle_area=circle.Area(2)     print(f'这是一个圆,它的面积是:{circle_area}') 上面我直接用一个函数代替工厂类也是可以的。