基于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()
相关文章推荐
- 基于python的25种设计模式
- 设计模式之一Template Method介绍以及以及基于python的代码展示
- 深入浅出基于Java的解释器设计模式
- 深入浅出基于Java的解释器设计模式
- 使用模型驱动开发和基于模式的工程来设计 SOA之第 4 部分
- 深入浅出基于Java的代理设计模式
- 【出版直播】博客园征途系列,《设计模式——基于C#的工程化实现及扩展》书签制作完成
- 基于模型、模式的技术架构设计
- 使用Biztalk Server实现基于消息的状态机设计模式
- 深入浅出基于Java的代理设计模式
- 【出版直播】博客园征途系列,《设计模式——基于C#的工程化实现与扩展》电子书、示例代码发布,互动网预订开始
- 使用Python语言设计基于HTML的C语言语法加亮显示程序
- 基于职能的权限设计——数据库“龙”式权限设计模式
- 基于游戏设计思想的新体验性服务模式
- 设计模式-基于C#的工程化实现及扩展 (目录)
- 深入浅出基于Java的代理设计模式
- 深入浅出基于Java的代理设计模式
- Python设计模式系列之一: 用模式改善软件设计
- 【现场直播】博客园征途系列,《设计模式——基于C#的工程化实现及扩展》封面火热出炉
- 关于《设计模式——基于C#的工程化实现》勘误的内容。感谢您对这本书的关心和支持