Patterns mean “I have run out of language.” - Rich Hickey
之前学习设计模式的时候总是没有什么感觉,因为记性不好一直没记住多少。python不像java中比较强调设计模式(编程套路),动态语言也内置了像是装饰器、迭代器等模式,另外python中的『一切皆对象』、鸭子类型等也导致python中实现的设计模式和其他语言有些差别。根据YAGNI(you aren’t gonna need it)和KISS(Keep it simple sutpid)原则,如果能用简单易懂的方式实现,最好不要滥用设计模式以免增加复杂度和维护难度。本博客是《Mastering Python Design Patterns》的读书笔记,涵盖大部分设计模式,有兴趣可以参考下,代码示例版本为python3.5.2。(3.6都发布了,已经被时代遗弃:cry:……)
1: The Fctory Pattern(工厂模式)先来看三种创建模式中的第一种工厂模式。
解释:处理对象创建,客户端可以申请一个对象而不用知道对象被哪个class创建。可以方便地解耦对象的使用和创建。有两种实现,工厂方法和抽象工厂.
Factory Method(工厂方法):执行单独的函数,通过传参提供需要的对象的信息。通过一个demo看看例子:
import json import xml.etree.ElementTree as etree class JSONConnector: def __init__(self, filepath): self.data = dict() with open(filepath, mode='r', encoding='utf8') as f: self.data = json.load(f) @property def parsed_data(self): return self.data class XMLConnector: def __init__(self, filepath): self.tree = etree.parse(filepath) @property def parsed_data(self): return self.tree def connection_factory(filepath): """ 工厂方法,使用者不必理会使用哪个Connector,只要传入文件类型自动返回 合适的Connector """ if filepath.endswith('json'): connector = JSONConnector elif filepath.endswith('xml'): connector = XMLConnector else: raise ValueError('Cannot connect to {}'.format(filepath)) return connector(filepath) Abstract Factory(抽象工厂)工厂方法适合创建对象种类单一的情况,如果有多种不同类型对象需要创建,使用抽象工厂模式。以实现一个游戏的例子说明,在一个抽象工厂类里实现多个关联对象的创建:
class Frog: def __init__(self, name): self.name = name def __str__(self): return self.name def interact_with(self, obstacle): """ 不同类型玩家遇到的障碍不同 """ print('{} the Frog encounters {} and {}!'.format( self, obstacle, obstacle.action())) 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() class Wizard: def __init__(self, name): self.name = name def __str__(self): return self.name def interact_with(self, obstacle): print('{} the Wizard battles against {} and {}!'.format( self, obstacle, obstacle.action())) class Ork: def __str__(self): return 'an evil ork' def action(self): return 'kill 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() 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)使用抽象工厂可以实现比工厂方法更为复杂的对象创建
2: The Builder Pattern(构造模式)当对象需要多个部分组合起来一步步创建,并且创建和表示分离的时候。可以这么理解,你要买电脑,工厂模式直接返回一个你需要型号的电脑,但是构造模式允许你自定义电脑各种配置类型,组装完成后给你。这个过程你可以传入builder从而自定义创建的方式。
# factory pattern MINI14 = '1.4GHz Mac mini' class AppleFactory: class MacMini14: def __init__(self): self.memory = 4 # in gigabytes self.hdd = 500 # in gigabytes self.gpu = 'Intel HD Graphics 5000' def __str__(self): info = ('Model: {}'.format(MINI14), 'Memory: {}GB'.format(self.memory), 'Hard Disk: {}GB'.format(self.hdd), 'Graphics Card: {}'.format(self.gpu)) return '\n'.join(info) def build_computer(self, model): if model == MINI14: return self.MacMini14() else: print("I don't know how to build {}".format(model)) # 使用工厂 afac = AppleFactory() mac_mini = afac.build_computer(MINI14) print(mac_mini) # builder模式 class Computer: def __init__(self, serial_number): self.serial = serial_number self.memory = None # in gigabytes self.hdd = None # in gigabytes self.gpu = None def __str__(self): info = ('Memory: {}GB'.format(self.memory), 'Hard Disk: {}GB'.format(self.hdd), 'Graphics Card: {}'.format(self.gpu)) return '\n'.join(info) class ComputerBuilder: def __init__(self): self.computer = Computer('AG23385193') def configure_memory(self, amount): self.computer.memory = amount def configure_hdd(self, amount): self.computer.hdd = amount def configure_gpu(self, gpu_model): self.computer.gpu = gpu_model class HardwareEngineer: def __init__(self): self.builder = None def construct_computer(self, memory, hdd, gpu): self.builder = ComputerBuilder() [step for step in (self.builder.configure_memory(memory), self.builder.configure_hdd(hdd), self.builder.configure_gpu(gpu))] @property def computer(self): return self.builder.computer # 使用buidler,可以创建多个builder类实现不同的组装方式 engineer = HardwareEngineer() engineer.construct_computer(hdd=500, memory=8, gpu='GeForce GTX 650 Ti') computer = engineer.computer print(computer)相比工厂模式,使用构造模式允许我们使用不同的builder来『指示』对象创建的过程,从而实现对象创建的控制。
3:The Prototype Pattern(原型模式)这是创建模式中的最后一个,用来克隆一个对象,有点像生物学中的有丝分裂。我们可以使用python内置的copy模块实现。拷贝分为深拷贝和浅拷贝,深拷贝会递归复制并创建新对象,而浅拷贝会利用引用指向同一个对象.深拷贝的优点是对象之间互不影响,但是会耗费资源,创建比较耗时;如果不会修改对象可以使用浅拷贝,更加节省资源和创建时间。
“A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original. A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.” import copy from collections import OrderedDict class Book: def __init__(self, name, authors, price, **rest): '''Examples of rest: publisher, length, tags, publication date''' self.name = name self.authors = authors self.price = price # in US dollars 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) class Prototype: def __init__(self): self.objects = {} 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)) obj = copy.deepcopy(found) obj.__dict__.update(attr) # 实现拷贝时自定义更新 return obj def main(): b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), price=118, publisher='Prentice Hall', length=228, publication_date='1978-02-22', tags=('C', 'programming', 'algorithms', 'data structures')) prototype = Prototype() cid = 'k&r-first' prototype.register(cid, b1) b2 = prototype.clone(cid, name='The C Programming Language (ANSI)', price=48.99, length=274, publication_date='1988-04-01', edition=2) for i in (b1, b2): print(i) print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))待续。。。