chatGPT教你设计模式[2] ——创建型模式(工厂模式)

1. 引言

在软件开发中,我们经常需要创建对象来封装数据和实现业务逻辑。然而,如果直接在代码中使用 new 关键字来创建对象,会使得代码的耦合度增加,系统的可扩展性和可维护性降低。这时,工厂模式就派上用场了。

工厂模式是软件设计模式中最常用的创建型模式之一,它提供了一种创建对象的最佳方式。工厂模式可以将对象的创建和使用分离开来,使得代码更加灵活,并且提高了系统的可扩展性和可维护性。

在工厂模式中,我们通常有三种实现方式:简单工厂模式、抽象工厂模式和工厂方法模式。在本篇博客中,我们将深入介绍这三种工厂模式的基本概念、结构、优缺点和应用场景,帮助你更好地理解和应用这些模式。

2 简单工厂模式

简单工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。

在简单工厂模式中,有一个工厂类,该类负责创建其他类的实例。工厂类包含一个用于创建对象的方法,该方法通过传递参数来决定要创建哪种对象。

例如,假设我们有一个用于创建不同类型的形状的工厂类,我们可以使用如下代码实现它:

class ShapeFactory:@staticmethoddef get_shape(shape_type):if shape_type == 'circle':return Circle()elif shape_type == 'square':return Square()elif shape_type == 'rectangle':return Rectangle()class Circle:def draw(self):print("Drawing a circle")class Square:def draw(self):print("Drawing a square")class Rectangle:def draw(self):print("Drawing a rectangle")# usage
factory = ShapeFactory()
shape = factory.get_shape("circle")
shape.draw() # Output: Drawing a circle

在这个例子中,我们定义了一个 ShapeFactory 类,它包含一个静态方法 get_shape,该方法接收一个字符串参数,用于决定要创建哪种形状。我们还定义了三个形状类,分别是 Circle、Square 和 Rectangle,每个类都有一个 draw 方法,用于绘制形状。

通过调用工厂类的 get_shape 方法,我们可以轻松地创建不同类型的形状,而无需要直接使用具体的形状类,这样我们就可以避免在代码中出现大量的条件语句。

2.1. 模式优缺点

简单工厂模式的优点在于,它可以使代码的可读性和可维护性提高,因为它隐藏了具体的实现细节,只暴露了一个简单的接口供外界调用。

但是,简单工厂模式也有一些缺点。首先,如果需要增加新的类型,则必须修改工厂类的代码,这违反了“开放-封闭原则”。其次,如果需要增加新的类型,则必须对工厂类进行测试,以确保修改后的代码仍然能够正常工作。

2.2 模式结构

简单工厂模式的结构如下:

  • 工厂类角色:它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
  • 抽象产品角色:它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法。
  • 具体产品角色:它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。

在设计模式中,简单工厂模式是一种比较简单的创建型模式,它适用于只有少数产品类的场景,但如果产品类数量较多,就会导致工厂类的代码变得臃肿,不利于维护。因此,在设计模式中,简单工厂模式通常被视为一种“过渡”模式,它可以帮助开发人员理解工厂模式的基本思想,并为进一步学习更高级的工厂模式(如工厂方法模式和抽象工厂模式)打下基础。

3 抽象工厂模式

抽象工厂模式是一种创建型设计模式,它提供了一种创建相关或依赖对象的最佳方式。

在抽象工厂模式中,有一个抽象工厂类,它包含多个用于创建不同类型对象的抽象方法。具体工厂类继承抽象工厂类,并实现抽象方法,从而创建具体的对象。

与简单工厂模式相比,抽象工厂模式更加灵活,因为它允许开发人员创建不同的工厂类来生成不同的对象。这样,在新增类型时,只需要创建新的具体工厂类和相应的对象类,就不需要修改已有的代码。

例如,假设我们有一个抽象工厂类用于创建不同类型的车辆,我们可以使用如下代码实现它:

class VehicleFactory:def get_vehicle(self, vehicle_type):passclass CarFactory(VehicleFactory):def get_vehicle(self, vehicle_type):if vehicle_type == 'small':return SmallCar()elif vehicle_type == 'medium':return MediumCar()elif vehicle_type == 'large':return LargeCar()class BikeFactory(VehicleFactory):def get_vehicle(self, vehicle_type):if vehicle_type == 'small':return SmallBike()elif vehicle_type == 'medium':return MediumBike()elif vehicle_type == 'large':return LargeBike()class SmallCar:def __str__(self):return "Small Car"class MediumCar:def __str__(self):return "Medium Car"class LargeCar:def __str__(self):return "Large Car"
class SmallBike:def __str__(self):return "Small Bike"class MediumBike:def __str__(self):return "Medium Bike"class LargeBike:def __str__(self):return "Large Bike"# usage
car_factory = CarFactory()
bike_factory = BikeFactory()small_car = car_factory.get_vehicle("small")
print(small_car) # Output: Small Car
medium_car = car_factory.get_vehicle("medium")
print(medium_car) # Output: Medium Car
large_car = car_factory.get_vehicle("large")
print(large_car) # Output: Large Carsmall_bike = bike_factory.get_vehicle("small")
print(small_bike) # Output: Small Bike
medium_bike = bike_factory.get_vehicle("medium")
print(medium_bike) # Output: Medium Bike
large_bike = bike_factory.get_vehicle("large")
print(large_bike) # Output: Large Bike

在这个例子中,我们定义了一个抽象工厂类 VehicleFactory,它包含一个抽象方法 get_vehicle,用于创建车辆对象。我们还定义了两个具体工厂类,分别是 CarFactory 和 BikeFactory,它们分别继承了 VehicleFactory 类并实现了 get_vehicle 方法。

通过调用具体工厂类的 get_vehicle 方法,我们可以轻松地创建不同类型的车辆对象,而无需直接使用具体的车辆类。

3.1 模式优缺点

优点:

  • 封装性好
    抽象工厂模式隔离了具体类的生成,使得用户不需要知道什么被创建。用户只需要知道所对应的工厂即可得到所需要的产品,而无需关心创建细节。

  • 产品族内的约束是更少的
    抽象工厂模式中的抽象工厂角色只需要提供多个生成产品的方法,这样,在系统中只需要有抽象工厂的具体实现类对应的具体产品即可。

缺点:

  • 产品族扩展麻烦
    抽象工厂模式需要增加新的产品族时,需要修改抽象工厂和所有的具体工厂类,这违背了“开闭原则”。

  • 产品等级结构扩展麻烦
    抽象工厂模式需要增加新的产品等级结构时,需要修改抽象工厂和所有的具体工厂类,这也违背了“开闭原则”。

3.2 模式结构

抽象工厂模式是一种创建型设计模式,它提供一种方法来创建一组相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式有四个要素:

抽象工厂类:抽象工厂类是工厂模式的核心,它声明了创建所有产品对象的方法。

具体工厂类:具体工厂类是抽象工厂类的子类,它实现了在抽象工厂中声明的创建产品的方法。

抽象产品类:抽象产品类是工厂模式所创建的对象的超类型,声明了产品对象的主要特征和功能。

具体产品类:具体产品类是抽象产品类的子类,它实现了抽象产品类中声明的方法,构成了具体的产品对象。

总的来说,抽象工厂模式是一种很有用的设计模式,它能够提供一组相关的产品对象,而无需指定它们的具体类。但是,在实际使用中,需要注意增加新的产品等级结构可能会比较困难,因此需要谨慎使用。

3. 工厂方法模式

工厂方法模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。

在工厂方法模式中,有一个抽象工厂类,它包含一个抽象方法用于创建对象。具体工厂类继承抽象工厂类,并实现抽象方法,从而创建具体的对象。

与抽象工厂模式相比,工厂方法模式更加灵活,因为它允许开发人员创建不同的工厂类来生成不同的对象,而不需要修改已有的代码。

例如,假设我们有一个抽象工厂类用于创建不同类型的车辆,我们可以使用如下代码实现它:

from abc import ABC, abstractmethodclass VehicleFactory(ABC):@abstractmethoddef get_vehicle(self):passclass CarFactory(VehicleFactory):def get_vehicle(self):return Car()class BikeFactory(VehicleFactory):def get_vehicle(self):return Bike()class Car:def __str__(self):return "Car"class Bike:def __str__(self):return "Bike"# usage
car_factory = CarFactory()
car = car_factory.get_vehicle()
print(car) # Output: Carbike_factory = BikeFactory()
bike = bike_factory.get_vehicle()
print(bike) # Output: Bike

在这个例子中,我们通过调用具体工厂类的 get_vehicle 方法来创建车辆对象。

3.1 模式优缺点

优点:

  • 在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实例化哪一个类。

  • 工厂方法模式通过使用面向对象的编程技巧,实现了在不修改具体工厂角色的情况下引进新的产品。

  • 工厂方法模式在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其代码。

缺点:

  • 在添加新产品时,需要编写新的具体产品类和对应的具体工厂类,这增加了系统的复杂度。

  • 如果产品类型较多,则会有较多的具体工厂和具体产品类,增加了系统的维护难度。

3.2 模式结构

工厂方法模式的结构如下所示:

抽象工厂(Abstract Factory):它是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。

具体工厂(Concrete Factory):它实现了抽象工厂中定义的接口,并且负责生产一种具体的产品。一个应用程序通常只需要一个具体工厂类。

抽象产品(Abstract Product):它是工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。

具体产品(Concrete Product):它是继承了抽象产品类的具体类,每一个具体产品都提供了唯一的功能。

客户端(Client):它是使用工厂方法模式的角色,它需要创建一个产品对象,但是它并不知道具体的产品类,只需要知道所对应的工厂即可。

工厂方法模式是比较简单的,它用来创建一类产品,而且在添加新产品时,无须修改抽象工厂和抽象产品提供的接口,也无须修改客户端。但它同时也有一些缺点,需要编写新的具体产品类和对应的具体工厂类,这增加了系统的复杂度。此外,如果产品类型较多,则会有较多的具体工厂和具体产品类,增加了系统的维护难度。

总的来说,工厂方法模式是一种非常实用的设计模式,适用于在不同条件下创建不同对象的场景。它比简单工厂模式和抽象工厂模式更加灵活,能够满足更复杂的需求。

4 抽象工厂模式和工厂方法模式比较

4.1 两者的区别

抽象工厂模式和工厂方法模式是两种常用的工厂模式,它们都用于解决在软件开发中对象的创建问题。但是两者还是有一些区别的。

首先,抽象工厂模式的目的是为了创建一组相关的或相互依赖的产品,而工厂方法模式的目的是为了创建一类产品。

其次,抽象工厂模式的结构较复杂,它需要定义抽象工厂类、具体工厂类、抽象产品类和具体产品类,而工厂方法模式的结构较简单,它只需要定义抽象工厂类、具体工厂类和具体产品类。

最后,抽象工厂模式的扩展性较差,当需要增加新的产品族时,需要修改抽象工厂类的源代码,而工厂方法模式的扩展性较好,只需要增加新的具体工厂类和具体产品类即可。

总的来说,抽象工厂模式适用于创建一组相关的或相互依赖的产品,而工厂方法模式适用于创建一类产品。在选择使用哪种工厂模式时,应该根据具体的需要和情况来决定。

4.2 两者的联系

工厂方法模式是抽象工厂模式的简化版,它将抽象工厂模式中的抽象产品类去掉,只保留具体产品类和抽象工厂类。这就使得工厂方法模式的结构比抽象工厂模式更加简单。

同时,在工厂方法模式中,具体工厂类的职责也比较单一,只需要负责生产具体产品,不需要负责生产多个产品等职责。这也使得工厂方法模式比抽象工厂模式更加简单。

在某些情况下,缺少抽象产品类可能会使工厂方法模式的结构变得更加复杂。

举个例子,假设你的系统中有一类产品,比如电子产品,它可以有手机、电脑等不同的产品。如果你使用工厂方法模式来实现这个系统,那么你需要为每种产品都定义一个具体产品类,比如 MobilePhone、Computer 等。

但是,如果你使用抽象工厂模式来实现这个系统,你可以将所有的产品都定义在一个抽象产品类的子类中,比如 Product 类的子类 MobilePhone、Computer 等。这样,就可以避免定义多个具体产品类,使得系统的结构变得更加简单。

总的来说,工厂方法模式的优点在于它的结构比较简单,实现较为容易。但是,在某些情况下,缺少抽象产品类可能会使得系统的结构变得更加复杂,不利于代码的维护和扩展。所以,在使用工厂方法模式时,如果需要添加新的具体产品类,就需要修改工厂类的代码,这违背了开闭原则。如果有多个产品族,就需要定义多个工厂类,这会增加系统的复杂度。

如果使用抽象工厂模式,就可以在不修改工厂类的情况下,通过增加新的具体产品类和抽象产品类来扩展系统,同时也可以很方便地支持多个产品族。

因此,抽象工厂模式在支持多个产品族和扩展性较强的场景中更为适用,而工厂方法模式则适用于只有一个产品族且扩展性不是很强的场景。

4.3 如何选择两者的使用?

工厂方法模式和抽象工厂模式都有各自的优点和适用场景。

抽象工厂模式适用于需要创建一组相关或相互依赖的产品的场景,比如需要同时创建一个汽车、一个发动机和一个轮胎。在这种情况下,抽象工厂模式可以满足需求,因为它能够提供一个规范,用于定义所有可能被创建的产品对象的接口,并负责提供这些产品对象的具体实例。

然而,在一些情况下,可能并不需要创建一组相关或相互依赖的产品,只需要创建一个单独的产品对象就可以了。在这种情况下,使用抽象工厂模式可能会显得多余,因为它提供的功能超出了实际需求。这就是工厂方法模式的优势所在。

工厂方法模式只需要一个工厂接口和若干个具体工厂类,就能实现对单个产品的创建。比如,只需要创建一个汽车,就可以使用工厂方法模式。它比抽象工厂模式更简单,代码量也更少,更容易理解。但是,如果我们的系统需要支持多种产品,并且所有的产品都在一个产品族内,那么抽象工厂模式是更好的选择。如果只需要支持一种产品,那么工厂方法模式是更好的选择。

5. 小结

在本篇博客中,我们介绍了三种常用的工厂模式:简单工厂模式、抽象工厂模式和工厂方法模式。

简单工厂模式是最简单的工厂模式,它通过一个工厂类来负责创建其他对象。简单工厂模式的优点在于实现简单,但是缺点是不够灵活,不能应对新的需求变化。

抽象工厂模式是一种比较高级的工厂模式,它能够创建多个产品族的产品。抽象工厂模式的优点在于能够应对新的需求变化,但是缺点是实现较复杂。

工厂方法模式是一种常用的工厂模式,它使用一个抽象工厂来负责创建一组相关的产品。工厂方法模式的优点在于能够应对新的需求变化,并且实现较为灵活。

通过本篇博客的内容,我们已经深入了解了简单工厂模式、抽象工厂模式和工厂方法模式的基本概念、结构、优缺点和应用场景。希望本篇博客能够为你的学习带来帮助,并吸引你持续关注该博客。

本文链接:https://my.lmcjl.com/post/4309.html

展开阅读全文

4 评论

留下您的评论.