基于Python的工厂模式学习总结

学习书籍精通Python设计模式

介绍

目的:简化对象的创建过程,在创建对象时无需关心内部逻辑与实现。

作用:与直接使用类实例化来创建对象相比,使用一个中心函数来创建对象更容易追踪。实现对象创建与使用的解耦,降低维护应用的复杂度。

工厂模式有两种形式:

  1. 工厂方法
  2. 抽象工厂

工厂方法

  • 基于单一的函数来处理对象创建任务
  • 传入一个参数,返回一个对象

Django框架的模型类使用的就是工厂方法:

foms就是一个工厂方法,传入CharFieldDateField就可以创建name对象和birth_data对象

from django import forms

class PersonForm(forms.Form):
	name = forms.CharField(max_length=100)
	birth_data = forms.DateField(required=False)

何时使用:如果创建对象的代码分布在许多不同的地方,导致难以追踪,这时候就可以使用工厂方法来重构。

工厂方法的实现

以读取json文件和xml文件来举例,json和xml文件格式如下:

[
 {"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}
 ]
 
   
    John 
    Smith 
    25 
    
21 2nd Street New York NY 10021
212 555-1234 646 555-4567 male
Jimy Liar 19
18 2nd Street New York NY 10021
212 555-1234 male
Patty Liar 20
18 2nd Street New York NY 10021
212 555-1234 001 452-8819 female

使用json和xml.etree来解析文件

python代码如下,其中 JSONDataExtractorXMLDataExtractor 为解析文件的类, @property 装饰器是将类中的方法当做类的属性, dataextraction_factory 为工厂方法, extract_data_from 是对工厂方法的异常处理的装饰。在主函数中调用装饰过后的工厂方法就可以获取对应的对象,使用对象的属性和方法。

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()

图解

基于Python的工厂模式学习总结_第1张图片

抽象工厂

  • 抽象工厂就是对许多工厂方法进行抽象、集合。
  • 比如一个汽车制造厂有许多生产线(门,车轮,发动机等等),每一个生产线就是一个工厂方法,你给他传递材料就返回给你所需的对象,整个生产线组合起来就是抽象工厂。

何时使用抽象工厂

当发现程序需要许多工厂方法,且将这些方法组合起来创建一系列对象是有意义的,那么就是用抽象工厂。

好处

抽象工厂可以使我们通过更改处于激活状态的工厂方法,动态的(在运行时)修改应用程序的行为。

抽象工厂模式的实现

一个小游戏程序,根据用户输入的年龄来判断用户该玩哪款游戏。

  1. 为Frog World游戏定义FrogBug
  2. 添加 FrogWorld 类,在其中使用FrogBug
  3. 为 Wizard game 游戏定义 WizardOrk
  4. 添加Wizard game 类,在其中使用WizardOrk
  5. 定义 GameEnvironment
  6. 添加 validate_age()函数
  7. 最后添加main()函数

代码如下:

# Frog game

class Frog:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def interact_with(self, obstacle):
        act = obstacle.action()
        msg = f'{self} the Frog encounters {obstacle} and {act}!'
        print(msg)

class Bug:
    def __str__(self):
        return 'a bug'

    def action(self):
        return 'eats it'

class FrogWorld:
    def __init__(self, name):
        print(self)
        self.player_name = name

    def __str__(self):
        return '\n\n\t------ Frog World -------'

    def make_character(self):
        return Frog(self.player_name)

    def make_obstacle(self):
        return Bug()


# Wizard game

class Wizard:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def interact_with(self, obstacle):
        act = obstacle.action()
        msg = f'{self} the Wizard battles against {obstacle} and {act}!'
        print(msg)

class Ork:
    def __str__(self):
        return 'an evil ork'

    def action(self):
        return 'kills it'

class WizardWorld:
    def __init__(self, name):
        print(self)
        self.player_name = name

    def __str__(self):
        return '\n\n\t------ Wizard World -------'

    def make_character(self):
        return Wizard(self.player_name)

    def make_obstacle(self):
        return Ork()

# Game environment
class GameEnvironment:
    def __init__(self, factory):
        self.hero = factory.make_character()
        self.obstacle = factory.make_obstacle()

    def play(self):
        self.hero.interact_with(self.obstacle)

def validate_age(name):
    try:
        age = input(f'Welcome {name}. How old are you? ')
        age = int(age)
    except ValueError as err:
        print(f"Age {age} is invalid, please try again...")
        return (False, age)
    return (True, age)

def main():
    name = input("Hello. What's your name? ")
    valid_input = False
    while not valid_input:
        valid_input, age = validate_age(name)
    game = FrogWorld if age < 18 else WizardWorld
    environment = GameEnvironment(game(name))
    environment.play()


if __name__ == '__main__':
    main()

图解
基于Python的工厂模式学习总结_第2张图片

你可能感兴趣的:(设计模式)