python设计模式-1工厂设计模式-2工厂方法

2021-04-02  本文已影响0人  python测试开发

工厂方法简介

工厂方法是为处理对象创建任务而编写的函数,使用工厂方法时,我们不需要知道创建对象是如何实现的。

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 代码来处理每个数据提取器。尽管能够为所有的提取器使用相同的代码会很好,但这在大多数情况下是不现实的,除非我们为数据使用某种通用的映射,这通常是由外部数据提供者提供的。

上一篇下一篇

猜你喜欢

热点阅读