外观模式和享元模式 8/30 | Python 主题月
写在前面
本文正在参加「Python主题月」,详情查看活动链接
这个月是 Python 活动月,我决定尝试用 Python 来刷这 30 天的每日一题和随机一题。然后如果周末有精力,我想捣鼓捣鼓这个python-patterns
设计模式对我来说更多的是学习而不是我的个人经验总结,所以我很可能理解偏,如果有大佬见到了请及时指出,我之所以选择在掘金来写一些个人的东西是因为这里的文章质量更高,我不希望后来者看到了这些文章被误导。
外观模式
Facade模式是一种为更复杂的系统提供更简单的统一接口的方法。它通过提供一个单一的入口点,为访问底层系统的功能提供了一种更简单的方式。这种抽象在现实生活中很多情况下都可以看到。例如,我们只需按下一个按钮就可以打开一台电脑,但事实上,当这种情况发生时,会有许多程序和操作(例如从磁盘加载程序到内存)。在这种情况下,按钮作为一个统一的接口,连接到所有开启计算机的基本程序。
来看例子:
# Complex computer parts
class CPU:
"""
Simple CPU representation.
"""
def freeze(self):
print("Freezing processor.")
def jump(self, position):
print("Jumping to:", position)
def execute(self):
print("Executing.")
class Memory:
"""
Simple memory representation.
"""
def load(self, position, data):
print(f"Loading from {position} data: '{data}'.")
class SolidStateDrive:
"""
Simple solid state drive representation.
"""
def read(self, lba, size):
return f"Some data from sector {lba} with size {size}"
class ComputerFacade:
"""
Represents a facade for various computer parts.
"""
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.ssd = SolidStateDrive()
def start(self):
self.cpu.freeze()
self.memory.load("0x00", self.ssd.read("100", "1024"))
self.cpu.jump("0x00")
self.cpu.execute()
def main():
"""
>>> computer_facade = ComputerFacade()
>>> computer_facade.start()
Freezing processor.
Loading from 0x00 data: 'Some data from sector 100 with size 1024'.
Jumping to: 0x00
Executing.
"""
if __name__ == "__main__":
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS)
复制代码
在Python标准库中,当我们使用 isdir
函数时可以看到这种模式。尽管用户只是用这个函数来知道一个路径是否指的是一个目录,但系统进行了一些操作,并调用其他模块(例如 os.stat
)来给出结果。
享元模式
享元模式的目的是尽量减少程序在运行时需要的对象的数量。Flyweight
是一个被多个上下文共享的对象,它与一个不被共享的对象没有区别。Flyweight
的状态不应受到其上下文的影响,这被称为其固有的状态。将对象的状态与对象的上下文解耦,允许 Flyweight
被共享。
import weakref
class Card:
"""The Flyweight"""
# Could be a simple dict.
# With WeakValueDictionary garbage collection can reclaim the object
# when there are no other references to it.
_pool = weakref.WeakValueDictionary()
def __new__(cls, value, suit):
# If the object exists in the pool - just return it
obj = cls._pool.get(value + suit)
# otherwise - create new one (and add it to the pool)
if obj is None:
obj = object.__new__(Card)
cls._pool[value + suit] = obj
# This row does the part we usually see in `__init__`
obj.value, obj.suit = value, suit
return obj
# If you uncomment `__init__` and comment-out `__new__` -
# Card becomes normal (non-flyweight).
# def __init__(self, value, suit):
# self.value, self.suit = value, suit
def __repr__(self):
return f"<Card: {self.value}{self.suit}>"
def main():
"""
>>> c1 = Card('9', 'h')
>>> c2 = Card('9', 'h')
>>> c1, c2
(<Card: 9h>, <Card: 9h>)
>>> c1 == c2
True
>>> c1 is c2
True
>>> c1.new_attr = 'temp'
>>> c3 = Card('9', 'h')
>>> hasattr(c3, 'new_attr')
True
>>> Card._pool.clear()
>>> c4 = Card('9', 'h')
>>> hasattr(c4, 'new_attr')
False
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
例子设置了一个“对象池”用来存储初始化的对象。当一个“卡片”被创建时,它首先检查它是否已经存在,而不是创建一个新的。这样做的目的是减少程序初始化的对象的数量。至少从这里可以看出设计模式并不是按照概念泾渭分明的,不知不觉就用到了对象池模式。
小结
外观模式为复杂的系统提供一个更简单的统一接口。享元模式通过与其他类似对象共享数据,最大限度地减少内存使用。