您的位置:首页 > 编程语言 > Python开发

基于Python的设计模式

2016-10-31 09:36 363 查看

·创建型模式

创建型设计模式处理对象创建相关的问题,目标是当直接创建对象不太方便时,提供更好的方式

工厂模式

在工厂设计模式中,调用方可以无需知道这个对象来自哪里(即使用哪个类来生成这个对象)而请求一个对象。其思想是简化对象的创建。

工厂有两种形式:

·工厂方法:它是一个方法,对不同的输入参数返回不同的对象

eg: Django框架使用工厂方法来创建表单字段(CharFiled, TextField)

应用:

·创建对象的代码分布在多个不同的地方

·将对象的创建和使用解耦

·工厂方法可以在必要时创建新的对象,从而提高性能和内存使用率。

示例

import json
import xml.etree.ElementTree as etree

class JSONConnector:

def __init__(self, filepath):
self.data = dict()
with open(filepath, mode='r', encoding='utf-8') as fh:
self.data = json.loads(s)

@property
def parse_data(self):
return self.data

class XMLConnector:

def __init__(self, filepath):
self.tree = etree.parse(filepath)

@property
def parse_data(self):
return self.tree

"""This is a factory method"""
def connect_factory(filepath):
if filepath.endswith('json'):
connector = JSONConnector
elif filepath.endswith('xml'):
connector = XMLConnector
else:
raise ValueError("Cannot connect to {}".format(filepath))
return connector(filepath)

"""Packaging connect_factory()"""
def connect_to(filepath):
factory = None
try:
factory = connect_factory(filepath)
except ValueError as err:
print(err)
return factory

def main():
xml_factory = connect_to("Your xml filepath")
xml_data = xml_factory.parse_data

json_factory = connect_to("Your json filepath")
json_data = json_factory.parse_data

if __name__ == "__main__":
main()


上面的connect_factory(filepath)是一个工厂方法,基于输入文件的扩展名返回一个JSONConnector和XMLConnector的实例。

@property使得方法像是一个常规的属性。

那么现在有一个问题,代码中可以直接实例化那两个对象,如何禁止?

这个问题在JAVA中很好解决,直接把类的构造函数声明为private的就可以了,但Python中并没有类似的机制,但是Python中的函数可以内嵌类,可以从这个角度试一试。

def connect_factory(filepath):
class JSONConnector:

def __init__(self, filepath):
self.data = dict()
with open(filepath, mode='r', encoding='utf-8') as fh:
self.data = json.loads(s)

@property
def parse_data(self):
return self.data

class XMLConnector:

def __init__(self, filepath):
self.tree = etree.parse(filepath)

@property
def parse_data(self):
return self.tree
if filepath.endswith('json'):
connector = JSONConnector
elif filepath.endswith('xml'):
connector = XMLConnector
else:
raise ValueError("Cannot connect to {}".format(filepath))
return connector(filepath)


现在直接创建对象的话就会报错了

NameError: name 'JSONConnector' is not defined


·抽象工厂:一组用于创建一系列相关事物对象的工厂方法。

抽象工厂模式是抽象方法的一种泛华。概括的说,一个抽象工厂是一组工厂方法,其中的每个工厂方法负责产生不同种类的对象。

什么时候使用抽象工厂:通常一开始使用工厂方法,如果后来发现需要许多工厂方法,那么就将一系列对象的过程合并在一起更合理,从而引入抽象工厂。

示例

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

def __str__(self):
return self.name

def interact_with(self, obstacle):
print("{0} the Frog encounters {1} and {2}! ".format(self, obstacle, obstacle.action()))

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

def action(self):
return "eats it"

"""This is a abstract factory. """
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()

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

def __str__(self):
return self.name

def interact_with(self, obstacle):
print("{0} the wizard battles against {1} and {2}! ".format(self, obstacle, obstacle.action()))

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

def action(self):
return "kills it"

"""This is a abstract factory too"""
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()

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("Welcome {0}. How old are you? ".format(name))
age = int(age)
except ValueError as err:
print("Age {0} is invalid, please try again...".format(age))
return (False, age)
return (True, age)

def main():
name = input("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()


小结

工厂方法的实现是一个不属于任何类的单一函数,负责单一种类对象的创建

抽象工厂的实现是同属于单个类的许多个工厂方法用于创建一系列种类的相关对象

建造者模式

建造者模式将一个复杂对象的构造过程与其表现分离。如果一个对象必须经过多个步骤来创建,并且要求同一个构造过程可以产生不同的表现,就可以使用建造者模式

以制作Pizza为例,准备好一个Pizza需要多个步骤,且这些步骤都有顺序。此外,不同的Pizza烘焙时间也不一样。

from enum import Enum

PizzaProgress = Enum("PizzaProgress", "queued preparation baking ready")
PizzaDough = Enum("PizzaDough", "thin thick")
PizzaSource = Enum("PizzaSauce", "tomato creme_fraiche")
PizzaTopping = Enum("PizzaTopping", "mozzarella double_mozzarella bacon ham mushrooms red_onion oregano")
STEP_DELAY = 3


最终的产品是Pizza,所以由一个Pizza类描述。

class Pizza:
def __init__(self, name):
self.name = name
self.dough = None
self.sauce = None
self.topping = []

def __str__(self):
return self.name

def prepare_dough(self, dough):
self.dough = dough
print("preparing the {0} dough of your {1}...".format(self.dough.name, self))
time.sleep(STEP_DELAY)
print("done with the {0} dough.".format(self.dough.name))


在这里我们创建两种不同的Pizza,所以要有两个建造者。每个建造者创建一个Pizza实例,并包含Pizza制作流程的方法:prepare_dough(), add_sauce(), add_topping()和bake()

class MargaritaBuilder:
def __init__(self):
self.pizza = Pizza("margarita")
self.progress = PizzaProgress.queued
self.baking_time = 5

def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thin)

def add_sauce(self):
print("adding the tomato sauce to your margarita...")
self.pizza.sauce = PizzaSource.tomato
time.sleep(STEP_DELAY)
print("done with the tomato sauce")

def add_topping(self):
print("adding the topping (double mo) to...")
self.pizza.topping.append([i for i in (PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
time.sleep(STEP_DELAY)
print("done with the topping (double)")

def bake(self):
self.progress = PizzaProgress.baking
print("baking your mar for {} second".format(self.baking_time))
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print("ready")


上面只列举了第一种建造模式,第二种和它大同小异。

现在该创建指挥者了,指挥者在这里是Waiter,核心是construct_pizza方法,该方法接受一个建造者作为参数,并以正确的顺序执行所有准备步骤。

class Waiter:
def __init__(self):
self.builder = None

def construct_pizza(self, bulider):
self.builder = builder
[step() for step in (builder.prepare_dough, builder.add_sauce, builder.add_topping, builder.bake)]

@property
def pizza(self):
return self.builder.pizza


以后情况适合适合建造者模式

创建一个复杂对象(对象由多个部分组成,且对象的创建要经过多个不同的步骤,或许这些步骤还需要特定的顺序)

要求一个对象能有不同的表现,并希望将对象的构造和表现解耦

想要在某个时间点创建对象,但在稍后的时间点再访问

原型模式

原型设计模式(Prototype design pattern)用来创建对象的克隆,其最简单的形式就是一个clone()函数,接受一个对象作为输入参数,返回输入对象的一个副本。可以用copy.deepcopy()来完成。

原型设计模式就是复制出一个相同的对象。当我们已有一个对象,并希望创建该对象的一个完整副本时可以采用。

现在来谈谈深拷贝和浅拷贝的区别

深拷贝:构造一个新的复制对象后,递归的将在原始对象中找到的对象的副本插入新对象中。也就是原始对象的所有数据都被复制到新的对象中。

浅拷贝:将原始对象的引用插入新对象。即浅拷贝依赖于引用

示例

从描述一本书开始,在init()方法中,只有三个形参是固定的,剩下的一个是rest可变列表,调用者能以字典形式传入更多参数,方便克隆该对象时加入一些新的特性。

from collections import OrderedDict
class Book:
def __init__(self, name, authors, price, **rest):
"""比如rest可以有出版商,标签,isbn等"""
self.name = name
self.authors = authors
self.price = price
self.__dict__.update(rest)

def __str__(self):
mylist = []
ordered = OrderedDict(sorted(self.__dict__.items()))
for i in ordered.keys():
mylist.append("{}: {}".format(i, ordered[i]))
if i == "price":
mylist.append("¥")
mylist.append("\n")
return " ".join(mylist)


Prototype类实现了原型设计模式,其核心是clone()方法。其次,包含了register()和unregister(),用来在一个字典中追踪被克隆的对象,但非必需。

class Prototype:
def __init__(self):
self.objects = dict()

def register(self, identifier, obj):
self.objects[identifier] = obj

def unregister(self, identifier):
del self.objects[identifier]

def clone(self, identifier, **attr):
found = self.objects.get(identifier)
if not found:
raise ValueError("Incorrect object identifier: {}".format(identifier))
import copy
obj = copy.deepcopy(found)
obj.__dict__.update(attr)
return obj


原型模式是最后一个创建型模式,在Python中是一种内置特性,用copy.deepcopy()来完成。

结构型模式

结构型模式处理一个系统中不同实体之间的关系,关注的是提供一种简单的对象组合方式来创造新功能

适配器模式

适配器模式(Adapter pattern)实现两个不兼容接口之间的兼容。比如:希望把一个老组件用在新系统或者把新组件用在老系统,不对代码进行修改两者就能够通信的情况很少见,或者是干脆不能修改代码,在这些情况下,可以编写一个额外的代码层,包含让两个接口之间能够进行通信所需的全部修改,这个代码层就叫适配器。

示例

首先创建两个类,各自有一个方法。保存为external.py

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

def __str__(self):
return "the {} synthesizer".format(self.name)

def play(self):
return "is playing an song"

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

def __str__(self):
return "{} the human".format(self.name)

def speak(self):
return "says hello"


创建一个通用的Adapter类

from external import Synthesizer, Human
class Computer:
def __init__(self, name):
self.name = name

def __str__(self):
return "the {} computer".format(self.name)

def execute(self):
return "exexute a program"

class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)

def __str__(self):
return str(self.obj)

def main():
objects = [Computer("Asus")]
synth = Synthesizer("moog")
objects.append(Adapter(synth, dict(execute=synth.play)))
human = Human("Bob")
objects.append(Adapter(human, dict(execute=human.speak)))
for i in objects:
print("{} {}".format(str(i), i.execute()))

if __name__ == "__main__":
main()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息