Python 设计模式 - 工厂模式
精通Python设计模式第二版 第 1 章 工厂模式 学习笔记
工厂模式
工厂模式的思想意在简化对象的创建过程。调用者(客户端)不清楚对象的来源以及生成对象的方式,调用者只需要调用某个方法或者实例某个类 既可以获得想要对象。这些对象根据不同的生产需求而产生
两种形式
- 工厂方法:一个方法(或者说函数)根据不同的输入参数返回不同的对象
- 抽象工厂:一组用于创建一系列相关对象的工厂方法
工厂方法的实现
让我们想象一下这样一个需求,我们有一些数据文件需要分析,分别是XML和JSON。这两种文件需要不容的库解析数据,但是调用者不想关心,然后区分处理这两种数据。这种场景需求就可以使用工厂方法来解决,工厂方法会帮我们做好不同数据格式的数据解析。
让我们先看一下 需要解析的文件数据
movies.json
[{"title": "After Dark in Central Park","year": 1900,"director": null,"cast": null,"genre": null},{"title": "Boarding School Girls' Pajama Parade","year": 1900,"director": null,"cast": null,"genre": null},{"title": "Buffalo Bill's Wild West Parad","year": 1900,"director": null,"cast": null,"genre": null},{"title": "Caught","year": 1900,"director": null,"cast": null,"genre": null},{"title": "Clowns Spinning Hats","year": 1900,"director": null,"cast": null,"genre": null},{"title": "Capture of Boer Battery by British","year": 1900,"director": "James H. White","cast": null,"genre": "Short documentary"},{"title": "The Enchanted Drawing","year": 1900,"director": "J. Stuart Blackton","cast": null,"genre": null},{"title": "Family Troubles","year": 1900,"director": null,"cast": null,"genre": null},{"title": "Feeding Sea Lions","year": 1900,"director": null,"cast": "Paul Boyton","genre": null}
]
person.xml
<persons><person><firstName>John</firstName><lastName>Smith</lastName><age>25</age><address><streetAddress>21 2nd Street</streetAddress><city>New York</city><state>NY</state><postalCode>10021</postalCode></address><phoneNumbers><phoneNumber type="home">212 555-1234</phoneNumber><phoneNumber type="fax">646 555-4567</phoneNumber></phoneNumbers><gender><type>male</type></gender></person><person><firstName>Jimy</firstName><lastName>Liar</lastName><age>19</age><address><streetAddress>18 2nd Street</streetAddress><city>New York</city><state>NY</state><postalCode>10021</postalCode></address><phoneNumbers><phoneNumber type="home">212 555-1234</phoneNumber></phoneNumbers><gender><type>male</type></gender></person><person><firstName>Patty</firstName><lastName>Liar</lastName><age>20</age><address><streetAddress>18 2nd Street</streetAddress><city>New York</city><state>NY</state><postalCode>10021</postalCode></address><phoneNumbers><phoneNumber type="home">212 555-1234</phoneNumber><phoneNumber type="mobile">001 452-8819</phoneNumber></phoneNumbers><gender><type>female</type></gender></person>
</persons>
接下来,让我们看看如何实现这样一个工厂方法
1. 先找到能解析这两种数据的库
import json
import xml.etree.ElementTree as etree
2. 解析数据的方法
类JSONDataExtractor用于解析JSON文件,它有parsed_data()方法,返回一个包含所有数据的字典。
类XMLDataExtractor则用于解析XML文件
class JSONDataExtractor:"""JSONDataExtractor : 用于解析JSON文件Attributes:"""def __init__(self, filepath):self.data = dict() # 包含所有数据的字典with open(filepath, 'r', encoding='utf8') as f:self.data = json.load(f)@propertydef parsed_data(self):return self.dataclass XMLDataExtractor():"""XMLDataExtractor : 用于解析XML文件Attributes:"""def __init__(self, filepath):self.tree = etree.parse(filepath)@propertydef parsed_data(self):return self.tree
3. 工厂方法
根据输入文件的拓展名,返回一个JSONDataExtractor或XMLDataExtractor的实例
def dataextraction_factory(filepath):'''工厂方法 根据输入文件的拓展名,返回一个JSONDataExtractor或XMLDataExtractor的实例:param filepath::return:'''if filepath.endswith('json'):extractor = JSONDataExtractorelif filepath.endswith('xml'):extractor = XMLDataExtractorelse:raise ValueError('Cannot extract data from {}'.format(filepath))return extractor(filepath)
4. 最后添加一些异常处理机制
def extract_data_from(filepath):'''dataextraction_factory() 方法的装饰器。它增加了异常处理机制:param filepath::return:'''factory_obj = Nonetry:factory_obj = dataextraction_factory(filepath)except ValueError as e:print(e)return factory_obj
5. 工厂函数的使用
def main():# 展示如果使用 工厂方法处理JSON文件json_factory = extract_data_from('data/movies.json')json_data = json_factory.parsed_dataprint(f'Found: {len(json_data)} movies')for movie in json_data:print(f"Title: {movie['title']}")year = movie['year']if year:print(f"Year: {year}")director = movie['director']if director:print(f"Director: {director}")genre = movie['genre']if genre:print(f"Genre: {genre}")print()# 展示如何使用工厂方法处理XML文件xml_factory = extract_data_from('data/person.xml')xml_data = xml_factory.parsed_dataliars = xml_data.findall(f".//person[lastName='Liar']")print(f'found: {len(liars)} persons')for liar in liars:firstname = liar.find('firstName').textprint(f'first name: {firstname}')lastname = liar.find('lastName').textprint(f'last name: {lastname}')[print(f"phone number ({p.attrib['type']}):", p.text)for p in liar.find('phoneNumbers')]print()print()if __name__ == '__main__':main()
获得的打印如下
Found: 9 movies
Title: After Dark in Central Park
Year: 1900Title: Boarding School Girls' Pajama Parade
Year: 1900Title: Buffalo Bill's Wild West Parad
Year: 1900Title: Caught
Year: 1900Title: Clowns Spinning Hats
Year: 1900Title: Capture of Boer Battery by British
Year: 1900
Director: James H. White
Genre: Short documentaryTitle: The Enchanted Drawing
Year: 1900
Director: J. Stuart BlacktonTitle: Family Troubles
Year: 1900Title: Feeding Sea Lions
Year: 1900found: 2 persons
first name: Jimy
last name: Liar
phone number (home): 212 555-1234first name: Patty
last name: Liar
phone number (home): 212 555-1234
phone number (mobile): 001 452-8819
工厂方法完整代码
factory_method.py
import json
import xml.etree.ElementTree as etreeclass JSONDataExtractor:"""JSONDataExtractor : 用于解析JSON文件Attributes:"""def __init__(self, filepath):self.data = dict() # 包含所有数据的字典with open(filepath, 'r', encoding='utf8') as f:self.data = json.load(f)@propertydef parsed_data(self):return self.dataclass XMLDataExtractor():"""XMLDataExtractor : 用于解析XML文件Attributes:"""def __init__(self, filepath):self.tree = etree.parse(filepath)@propertydef parsed_data(self):return self.treedef dataextraction_factory(filepath):'''工厂方法 根据输入文件的拓展名,返回一个JSONDataExtractor或XMLDataExtractor的实例:param filepath::return:'''if filepath.endswith('json'):extractor = JSONDataExtractorelif filepath.endswith('xml'):extractor = XMLDataExtractorelse:raise ValueError('Cannot extract data from {}'.format(filepath))return extractor(filepath)def extract_data_from(filepath):'''dataextraction_factory() 方法的装饰器。它增加了异常处理机制:param filepath::return:'''factory_obj = Nonetry:factory_obj = dataextraction_factory(filepath)except ValueError as e:print(e)return factory_objdef main():# sqlite_factory = extract_data_from('data/person.sq3')# print()# 展示如果使用 工厂方法处理JSON文件json_factory = extract_data_from('data/movies.json')json_data = json_factory.parsed_dataprint(f'Found: {len(json_data)} movies')for movie in json_data:print(f"Title: {movie['title']}")year = movie['year']if year:print(f"Year: {year}")director = movie['director']if director:print(f"Director: {director}")genre = movie['genre']if genre:print(f"Genre: {genre}")print()# 展示如何使用工厂方法处理XML文件xml_factory = extract_data_from('data/person.xml')xml_data = xml_factory.parsed_dataliars = xml_data.findall(f".//person[lastName='Liar']")print(f'found: {len(liars)} persons')for liar in liars:firstname = liar.find('firstName').textprint(f'first name: {firstname}')lastname = liar.find('lastName').textprint(f'last name: {lastname}')[print(f"phone number ({p.attrib['type']}):", p.text)for p in liar.find('phoneNumbers')]print()print()if __name__ == '__main__':main()
抽象工厂的实现
假设我们正在创建一个游戏,或者想将一个迷你游戏作为应用程序的一部分来取悦用户。我们希望至少包括两款游戏:一款儿童游戏,一款成人游戏。我们将根据用户的输入,在运行时决定创建和启动哪款游戏
1. 儿童游戏的创建
男主角是一只喜欢吃虫子的青蛙,每一个主角都需要一个好的名字
class Frog():"""Frog :Attributes:"""def __init__(self, name):self.name = namedef __str__(self):return self.namedef interact_with(self, obstacle):'''用于描述 青蛙和障碍物之间的交互:param obstacle::return:'''act = obstacle.action()msg = f'{self} the Frog encounters {obstacle} and {act} !'print(msg)class Bug():"""Bug :Attributes:"""def __str__(self):return 'a bug'def action(self):'''青蛙只支持一种行为:吃掉虫子'''return 'eats it'
2. 儿童游戏的抽象工厂
它的主要任务是创建游戏中的主角与障碍物。保持创建方法的独立性以及名称的通用性
class FrogWorld():"""FrogWorld : 抽象工厂。它的主要任务是创建游戏中的主角与障碍物Attributes:"""def __init__(self, name):print(self)self.player_name = namedef __str__(self):return '\n\n\t------ Frog World ------'def make_character(self):return Frog(self.player_name)def make_obstacle(self):return Bug()
3. 成人游戏的创建
跟儿童游戏的区别是,这是一个巫师打兽人的游戏
class Wizard():"""Wizard :Attributes:"""def __init__(self, name):self.name = namedef __str__(self):return self.namedef interact_with(self, obstacle):act = obstacle.action()msg = f'{self} the Wizard battles against {obstacle} and {act}!'print(msg)class Ork():"""Ork :Attributes:"""def __str__(self):return 'an evil ork'def action(self):return 'kills it'
4. 成人游戏的抽象工厂
情况与儿童游戏类似
class WizardWorld():"""WizardWorld :Attributes:"""def __init__(self, name):print(self)self.player_name = namedef __str__(self):return '\n\n\t------ Wizard World ------'def make_character(self):return Wizard(self.player_name)def make_obstacle(self):return Ork()
5. 游戏的主入口
接收一个工厂作为输入,并使用它来创建游戏世界
class GameEnvironment():"""GameEnvironment : 游戏的主入口。它接收一个工厂作为输入,并使用它来创建游戏世界Attributes:"""def __init__(self, facotry):self.hero = facotry.make_character()self.obstacle = facotry.make_obstacle()def play(self):'''初始化主角与障碍物之间的交互:return:'''self.hero.interact_with(self.obstacle)
6. 游戏创建逻辑
def validate_age(name):'''提示用户给出一个有效年龄。如果年龄无效,则返回一个首元素为False的元祖:param name::return:'''try:age = input(f'Welcome {name}. How old are you? \n')age = int(age)except ValueError as err:print(f'Age {age} is invalid, please try again...')return (False, age)return (True, age)
7. 游戏运行逻辑
def main():'''询问用户姓名和年龄,并根据用户的年龄决定应该玩哪款游戏:return:'''name = input("Hello, What's your name? \n")valid_input = Falsewhile not valid_input:valid_input, age = validate_age(name)game = FrogWorld if age < 18 else WizardWorldenvironment = GameEnvironment(game(name))environment.play()if __name__ == '__main__':main()
获取打印如下
Hello, What's your name?
coco
Welcome coco. How old are you?
19------ Wizard World ------
coco the Wizard battles against an evil ork and kills it!
抽象工厂完整代码
class Frog():"""Frog :Attributes:"""def __init__(self, name):self.name = namedef __str__(self):return self.namedef interact_with(self, obstacle):'''用于描述 青蛙和障碍物之间的交互:param obstacle::return:'''act = obstacle.action()msg = f'{self} the Frog encounters {obstacle} and {act} !'print(msg)class Bug():"""Bug :Attributes:"""def __str__(self):return 'a bug'def action(self):'''青蛙只支持一种行为:吃掉虫子'''return 'eats it'class FrogWorld():"""FrogWorld : 抽象工厂。它的主要任务是创建游戏中的主角与障碍物Attributes:"""def __init__(self, name):print(self)self.player_name = namedef __str__(self):return '\n\n\t------ Frog World ------'def make_character(self):return Frog(self.player_name)def make_obstacle(self):return Bug()class Wizard():"""Wizard :Attributes:"""def __init__(self, name):self.name = namedef __str__(self):return self.namedef interact_with(self, obstacle):act = obstacle.action()msg = f'{self} the Wizard battles against {obstacle} and {act}!'print(msg)class Ork():"""Ork :Attributes:"""def __str__(self):return 'an evil ork'def action(self):return 'kills it'class WizardWorld():"""WizardWorld :Attributes:"""def __init__(self, name):print(self)self.player_name = namedef __str__(self):return '\n\n\t------ Wizard World ------'def make_character(self):return Wizard(self.player_name)def make_obstacle(self):return Ork()class GameEnvironment():"""GameEnvironment : 游戏的主入口。它接收一个工厂作为输入,并使用它来创建游戏世界Attributes:"""def __init__(self, facotry):self.hero = facotry.make_character()self.obstacle = facotry.make_obstacle()def play(self):'''初始化主角与障碍物之间的交互:return:'''self.hero.interact_with(self.obstacle)def validate_age(name):'''提示用户给出一个有效年龄。如果年龄无效,则返回一个首元素为False的元祖:param name::return:'''try:age = input(f'Welcome {name}. How old are you? \n')age = int(age)except ValueError as err:print(f'Age {age} is invalid, please try again...')return (False, age)return (True, age)def main():'''询问用户姓名和年龄,并根据用户的年龄决定应该玩哪款游戏:return:'''name = input("Hello, What's your name? \n")valid_input = Falsewhile not valid_input:valid_input, age = validate_age(name)game = FrogWorld if age < 18 else WizardWorldenvironment = GameEnvironment(game(name))environment.play()if __name__ == '__main__':main()
Python 设计模式 - 工厂模式相关推荐
- Python设计模式-策略模式
Python设计模式-策略模式 代码基于3.5.2,代码如下; #coding:utf-8 #策略模式class sendInterface():def send(self,value):raise ...
- Python设计模式-建造者模式
Python设计模式-建造者模式 代码基于3.5.2,代码如下; #coding:utf-8 #建造者模式 class Burger():name = ""price = 0.0d ...
- Python设计模式-状态模式
Python设计模式-状态模式 代码基于3.5.2,代码如下; #coding:utf-8 #状态模式class state():def writeProgram(self,work):raise N ...
- Python设计模式-备忘录模式
Python设计模式-备忘录模式 代码基于3.5.2,代码如下; #coding:utf-8 #备忘录模式 import randomclass gameCharacter():vitality = ...
- Python设计模式-解释器模式
Python设计模式-解释器模式 代码基于3.5.2,代码如下; #coding:utf-8 #解释器模式class PlayContext():play_text = Noneclass Expre ...
- Python设计模式-命令模式
Python设计模式-命令模式 代码基于3.5.2,代码如下; #coding:utf-8 #命令模式class barbecuer():def bakeButton(self):print(&quo ...
- Python设计模式-外观模式
Python设计模式-外观模式 代码基于3.5.2,代码如下; #coding:utf-8 # 外观模式class AlarmSensor:def run(self):print("Alar ...
- Python设计模式-桥接模式
Python设计模式-桥接模式 基于Python3.5.2,代码如下 #coding:utf-8class Shape():name = ""param = "" ...
- Python设计模式-代理模式
Python设计模式-代理模式 基于Python3.5.2,代码如下 #coding:utf-8info_struct = dict() info_struct["addr"] = ...
最新文章
- 小程序不支持wx.request同步请求解决方法
- 华为鸿蒙系统手机销量,两个品牌助力华为新生,但最终会是谁拯救谁
- ARM函数调用时参数传递规则
- 整理的常用JAVA开源库简介
- 不擅演讲的马化腾在 08 年讲了什么?
- python字典由什么组成_在Python中,将由关键字对组成的列表添加到字典中最简单的方法是什么?...
- 【HDOJ2087】剪花布条(KMP)
- 签证计算机专业敏感吗,签证中最常见被check的14个敏感专业
- 电力系统卫星时钟同步工作的重要性
- 从零开始一起学习SLAM | 学习SLAM到底需要学什么?
- B样条曲线(B-spline Curves)
- linux下查看设备的接口,linux 查看sdio接口有哪些设备?
- 四叶草剧场服务器维修价格,四叶草剧场服务器介绍 服务器选择攻略
- postman全方位讲解(有空看下)
- 电商、数字化下的中国书店
- 基于时间序列分析方法的零售业快消品销量预测研究
- Error: errCode: -501000 | errMsg: Environment create at tencentcloud cannot access from wx-miniapp
- 什么是 “零” 拷贝?
- emacs官方中文手册
- Hive的安装与配置