工厂方法简介
工厂方法是为处理对象创建任务而编写的函数,使用工厂方法时,我们不需要知道创建对象是如何实现的。
Django网络框架使用工厂方法模式来创建网络表单的字段。Django中包含的表单模块,支持创建不同种类的字段(例如CharField、EmailField等)。而且它们的部分行为可以通过max_length或required等属性进行自定义。
from django import forms
class PersonForm(forms.Form):
name = forms.CharField(max_length=100)
birth_date = forms.DateField(required=False)
实现工厂方法: json和xml解析
- 需求
在这个案例中,我们有一些输入数据存储在XML和JSON文件中,我们想解析它们并检索信息。同时我们希望同意客户端与这些(以及未来所有)外部服务的连接。我们将使用工厂方法来解决这个问题。这个例子只关注XML和JSON,但增加对更多服务的支持也是类似的。
首先,我们来看看数据文件。
JSON文件movies.json是一个包含美国电影信息(片名、年份、导演姓名、类型等)数据集的例子(在GitHub上找到)。这实际上是一个很大的文件,但这里是一个摘录,为了更好的可读性而进行了简化,以显示其内容是如何组织的。
[
{"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}
]
XML文件person.xml是基于维基百科的例子(j.mp/wikijson),包含了个人的信息(名,姓,性别等),如下所示。
<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>
- 实现
import json
import xml.etree.ElementTree as etree
class JSONDataExtractor:
def __init__(self, filepath):
self.data = dict()
with open(filepath, mode='r', encoding='utf-8') as f:
self.data = json.load(f)
@property
def parsed_data(self):
return self.data
class XMLDataExtractor:
def __init__(self, filepath):
self.tree = etree.parse(filepath)
@property
def parsed_data(self):
return self.tree
def dataextraction_factory(filepath):
if filepath.endswith('json'):
extractor = JSONDataExtractor
elif filepath.endswith('xml'):
extractor = XMLDataExtractor
else:
raise ValueError('Cannot extract data from {}'.format(filepath))
return extractor(filepath)
def extract_data_from(filepath):
factory_obj = None
try:
factory_obj = dataextraction_factory(filepath)
except ValueError as e:
print(e)
return factory_obj
def main():
sqlite_factory = extract_data_from('data/person.sq3')
print()
json_factory = extract_data_from('data/movies.json')
json_data = json_factory.parsed_data
print(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_factory = extract_data_from('data/person.xml')
xml_data = xml_factory.parsed_data
liars = xml_data.findall(f".//person[lastName='Liar']")
print(f'found: {len(liars)} persons')
for liar in liars:
firstname = liar.find('firstName').text
print(f'first name: {firstname}')
lastname = liar.find('lastName').text
print(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()
虽然 JSONDataExtractor 和 XMLDataExtractor 有相同的接口,但 parsed_data() 返回的内容并不是以统一的方式处理的。必须使用不同的 Python 代码来处理每个数据提取器。尽管能够为所有的提取器使用相同的代码会很好,但这在大多数情况下是不现实的,除非我们为数据使用某种通用的映射,这通常是由外部数据提供者提供的。
网友评论