Python面经【9】- Python设计模式专题-下卷
三、 工厂模式
工厂模式是最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
概念:创建一个对象的接口,让其子类自己决定实例化哪一个工厂类。工厂模式让类的实例化推迟到子类中进行。
主要解决:主要解决接口选择的问题
何时使用:我们明确计划不同条件下创建不同实例时。
在工厂设计模式中,客户端可以请求一个对象,而无需知道这个对象来自哪里;也就是,使用哪个类来生成这个对象。工厂背后的思想是简化对象的创建。与客户端自己基于实例化直接创建对象相比,基于一个中心化函数来实现,更易于追踪创建了那些对象。
通过将创建对象的代码和使用对象的代码解耦,工厂能够降低应用维护的复杂度。
工厂方法创建对象时,我们并没有与某个特定类耦合/绑定一起,而只是通过调用某个函数来提供关于我们想要什么的部分信息。这意味着修改这个函数比较容易,不需要同时修改使用这个函数的代码。
工厂通常有三种形式:
(1) 简单工厂:简单工厂并不是一个真正的设计模式,更像是一种编程习惯,它通过一个单独的类来创建实例,这个类通常包含一个静态方法,根据对不同的输入参数返回不同的对象;
(2) 工厂方法:工厂方式模式通过定义一个用于创建对象的接口,让子类决定实例化哪个类。这种模式使一个类的实例化延迟到其子类。
(3) 抽象工厂:它是一组用于创建一系列相关事物对象的工厂方法。
1. 简单工厂:【静态方法】
统一使用一个类作为对外接口,根据参数的不同,去选择实例化不同的类。
1. """
2. 两个产品(两种类型的书)
3. """
4. class TechnicalBooks(object):
5. """技术书籍"""
7. def publish(self):
8. return "Python-Book"
9.
10. class LiteraryBooks(object):
11. """文学书籍"""
12.
13. def publish(self):
14. return "Black Hole Book"
15.
16. # 现在我们有两种类型的书,分别是TechnicalBooks和LiteraryBooks的书
17. # 按照我们平常的方法来实例化的话,此时创建对象时是会对客户端暴露真正创建的类
18. it_books = TechnicalBooks()
19. ly_books = LiteraryBooks()
20.
21. # 这时我们就可以构造一个"简单工厂"把所有实例化的过程封装在里面,把真正实例的类隐藏起来
22. class SimpleFactory(object):
23. """简单工厂"""
24. @staticmethod
25. def publish_book(name):
26. if name == 'technical':
27. return TechnicalBooks()
28. elif name == 'literary':
29. return LiteraryBooks()
30. # 通过类调用:class_name.fun_name()
31. it_books2 = SimpleFactory.publish_book('technical')
32. ly_books2 = SimpleFactory.publish_book('literary')
简单工厂的好处在于,把不同的类实例化统一到一个“工厂”,即不对外暴露真正的创建类,也提供了一个对外的统一接口。但是简单模式有一个缺点,那就是违背了soild的“开闭原则”,加入我们还需要增加一种书籍,那就必须要对简单工厂SimpleFactory进行源码的修改。
简单工厂的使用场景:
- 已经确定有多少具体的类,不会再增加的情况下使用。
2. 工厂方法
上面的简单工厂我们知道了,如果新增一些类型的时候会违背软件设计中的开闭原则,但是我们希望再扩展新类时,不要修改原来的代码,这个时候我们可以在简单工厂的基础上把SimpleFactory抽象成不同的工厂,每个工厂对应生成自己的产品,这就是工厂方法。
1. """
2. 两个产品(两种类型的书)
3. """
4. from abc import ABC, abstractmethod
5. # 真正进行实例化的类
6. class TechnicalBooks(object):
7. """技术书籍"""
8. def publish(self):
9. return "Python-Book"
10.
11. class LiteraryBooks(object):
12. """文学书籍"""
13. def publish(self):
14. return "Black Hole Book"
15.
16. # 抽象工厂:先定义抽象类,然后每种类型的书籍都有自己对于的工厂
17. class AbstractFactory(metaclass=abc.ABCMeta):
18. """抽象工厂"""
19. @abstractmethod
20. def publish_book(self):
21. pass
22.
23. class TechnicalFactory(AbstractFactory):
24. """技术书籍工厂"""
25.
26. def publish_book(self):
27. return TechnicalBooks()
28.
29. class LiteraryFactory(AbstractFactory):
30. """文学书籍工厂"""
31.
32. def publish_book(self):
33. return LiteraryBooks()
34.
35. it_books2 = TechnicalFactory().publish_book()
36. ly_books2 = LiteraryFactory().publish_book()
# 如果通过类调用是不带括号的
# 该代码带括号等价于先创建实例然后通过实例调用方法
这样每个工厂就只负责生产自己的产品,避免了在新增产品时需要修改工厂的代码,遵循了“开闭原则”,如果需要新增产品时,只需要增加相应的工厂即可。
比如要增加一种小说类型的书籍,只需增加一个NovelBooks类和NovelFactory类。
工厂方法的使用场景:
- 当系统中拥有的子类很多,并且以后可能还需要不断拓展增加不同的子类时。
- 当设计系统时,还不明确具体有那些类时。
在工厂方法中,使用者不需要知道具体的产品类名,只需要知道其对应的工厂即可。
3. 抽象工厂
工厂方法解决了“开闭原则”的问题,但是我们出版书籍之前肯定还有其他的步骤,比如印刷。如果要每一个步骤我们就要写一个对应的工厂类,那我们就会需要创建很多多多的类了。为了解决这个问题,我们就需要抽象工厂类,让一个工厂可以生产同一类的多个产品或多个动作(步骤),这就是抽象工厂。
1. """
2. 两个产品(两种类型的书)
3. """
4. import abc
5.
6. # 印刷书籍
7. class PrintingTechnicalBooks(object):
8. """印刷技术书籍"""
9. def printing(self):
10. return "Print-Python-Book"
11.
12. class PrintingLiteraryBooks(object):
13. """印刷文学书籍"""
14. def printing(self):
15. return "Print Black Hole Book"
16.
17. # 出版书籍
18. class TechnicalBooks(object):
19. """出版技术书籍"""
20. def publish(self):
21. return "Python-Book"
22.
23. class LiteraryBooks(object):
24. """出版文学书籍"""
25. def publish(self):
26. return "Black Hole Book"
27.
28. # 抽象工厂:先定义抽象类,然后每种类型的书籍都有自己对于的工厂
29. class AbstractFactory(metaclass=abc.ABCMeta):
30. """抽象工厂"""
31.
32. @abc.abstractmethod
33. def print_book(self):
34. pass
35.
36. @abc.abstractmethod
37. def publish_book(self):
38. pass
39.
40. class TechnicalFactory(AbstractFactory):
41. """技术书籍工厂"""
42.
43. def print_book(self):
44. return PrintingTechnicalBooks()
45.
46. def publish_book(self):
47. return TechnicalBooks()
48.
49. class LiteraryFactory(AbstractFactory):
50. """文学书籍工厂"""
51. def print_book(self):
52. return PrintingLiteraryBooks()
53.
54. def publish_book(self):
55. return LiteraryBooks()
56.
58. # 实例化工厂对象
59. it = TechnicalFactory()
60. ly = LiteraryFactory()
61.
62. # 印刷书籍
63. it_print = it.print_book()
64. ly_print = ly.print_book()
65. # 出版书籍
66. it_publish = it.publish_book()
67. ly_publish = ly.publish_book()
- 抽象工厂模式与工厂方法模式的区别:
- 抽象工厂中的一个工厂对象可以负责多个不同产品对象的创建。
- 抽象工厂的使用场景:
- 当多个产品(步骤)集合在一起,组成产品族时
- 对应一个产品族时,如果只想显示接口而不是实现时。
四、 建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,建造者模式将所有细节都交由子类实现。需求:画人物,要求画一个人的左手、右手、左脚、右脚和身体,画一个瘦子和一个胖子。
1. 不使用设计模式
1. if __name__=='__name__':
2. print '画左手'
3. print '画右手'
4. print '画左脚'
5. print '画右脚'
6. print '画胖身体'
7.
8. print '画左手'
9. print '画右手'
10. print '画左脚'
11. print '画右脚'
12. print '画瘦身体'
这样写的缺点每画一个人,都要画他的5个部位,这些部位有一些是可以重用的,所以调用起来会比较繁琐,而且客户调用的时候可能会忘记画其中一个部分,所以容易出错。
建造一个抽象的类Builder,声明画他的五个部位的方法,每画一种人,就新建一个继承Builder的类,这样新建的类就必须实现Builder的所有方法,这里主要运用了抽象方法的特性,父类定义了几个抽象的方法,子类必须要实现这些方法,否则就报错,这里解决了会漏画一个部位的问题。建造一个指挥者类Director,输入一个Builder的方法,定义一个draw的方法,把画这五个部位的方法调用都放在里面,这样调用起来就不会繁琐了。
- Python本身不提供抽象类和接口机制,要想实现抽象类,可以借助abc模块。Abc模块是Abstract Base Class的缩写。
- 被@abstractmethod装饰为抽象方法后,该方法不能被实例化;除非子类实现了基类的抽象方法,所以能实例化。
2. 使用设计模式
1. #encodig=utf-8
2. from abc import ABCMeta, abstractmethod
3. class Builder():
4. __metaclass__ = ABCMeta
5.
6. @abstractmethod
7. def draw_left_arm(self):
8. pass
9.
10. @abstractmethod
11. def draw_right_arm(self):
12. pass
13.
14. @abstractmethod
15. def draw_left_foot(self):
16. pass
17.
18. @abstractmethod
19. def draw_right_foot(self):
20. pass
21.
22. @abstractmethod
23. def draw_body(self):
24. pass
25.
26. class Thin(Builder): # 继承抽象类,必须实现其中定义的方法
27. def draw_left_arm(self):
28. print('画瘦子左手')
29.
30. def draw_right_arm(self):
31. print('画瘦子右手')
32.
33. def draw_left_foot(self):
34. print('画瘦子左脚')
35.
36. def draw_right_foot(self):
37. print('画瘦子右脚')
38.
39. def draw_body(self):
40. print('画瘦子身体')
41.
42. class Fat(Builder): # 继承抽象类,必须实现其中定义的方法
43. def draw_left_arm(self):
44. print('画胖子左手')
45.
46. def draw_right_arm(self):
47. print('画胖子右手')
48.
49. def draw_left_foot(self):
50. print('画胖子左脚')
51.
52. def draw_right_foot(self):
53. print('画胖子右脚')
54.
55. def draw_body(self):
56. print('画胖子的身体')
57.
58. class Director():
59. def __init__(self, person):
60. self.person = person
61.
62. def draw(self):
63. self.person.draw_left_arm()
64. self.person.draw_right_arm()
65. self.person.draw_left_foot()
66. self.person.draw_left_foot()
67. self.person.draw_body()
68.
69. if __name__ == '__main__':
70. thin = Thin()
71. fat = Fat()
72. director_thin = Director(thin)
73. director_thin.draw()
74. director_fat = Director(fat)
75. director_fat.draw()
建造者模式用于将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。