原型模式和三层模式 5/30 | Python 主题月
写在前面
本文正在参加「Python主题月」,详情查看活动链接
这个月是 Python 活动月,我决定尝试用 Python 来刷这 30 天的每日一题和随机一题。然后如果周末有精力,我想捣鼓捣鼓这个python-patterns
设计模式对我来说更多的是学习而不是我的个人经验总结,所以我很可能理解偏,如果有大佬见到了请及时指出,我之所以选择在掘金来写一些个人的东西是因为这里的文章质量更高,我不希望后来者看到了这些文章被误导。
原型模式
这是常见创建型模式的最后一种。原型模式通过克隆原型创建新的对象实例。这种模式的目的是减少应用程序所需的类的数量。它不依赖子类,而是通过在运行时复制一个原型实例来创建对象。这很有用,尤其是当类的实例只有几种不同的状态组合时或者当实例化很昂贵时,它使派生新种类的对象更容易。
接下来看代码
from typing import Any, Dict
class Prototype:
def __init__(self, value: str = "default", **attrs: Any) -> None:
self.value = value
self.__dict__.update(attrs)
def clone(self, **attrs: Any) -> None:
"""Clone a prototype and update inner attributes dictionary"""
# Python in Practice, Mark Summerfield
# copy.deepcopy can be used instead of next line.
obj = self.__class__(**self.__dict__)
obj.__dict__.update(attrs)
return obj
class PrototypeDispatcher:
def __init__(self):
self._objects = {}
def get_objects(self) -> Dict[str, Prototype]:
"""Get all objects"""
return self._objects
def register_object(self, name: str, obj: Prototype) -> None:
"""Register an object"""
self._objects[name] = obj
def unregister_object(self, name: str) -> None:
"""Unregister an object"""
del self._objects[name]
def main() -> None:
"""
>>> dispatcher = PrototypeDispatcher()
>>> prototype = Prototype()
>>> d = prototype.clone()
>>> a = prototype.clone(value='a-value', category='a')
>>> b = a.clone(value='b-value', is_checked=True)
>>> dispatcher.register_object('objecta', a)
>>> dispatcher.register_object('objectb', b)
>>> dispatcher.register_object('default', d)
>>> [{n: p.value} for n, p in dispatcher.get_objects().items()]
[{'objecta': 'a-value'}, {'objectb': 'b-value'}, {'default': 'default'}]
>>> print(b.category, b.is_checked)
a True
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
当应用程序中的原型数量可能不同时,保留一个分发器(调度器,注册表,管理器等等叫法不同但有非常相似的功能)可能会很有用。这允许客户端程序在克隆一个新的实例之前查询分发器这里的原型。程序里的分发器包含三个原型的副本:default
,objecta
和 objectb
。
原型模式在日常生活中非常常见,洗衣机上各种通用模式同时允许用户自己改某些参数这里就是原型模式。一些大型 MMORPG 游戏里的角色捏脸功能也是提供了一些标准的素体模型然后允许玩家自己去修改人物模型细节,这里一样是原型模式。之前小面馆的例子中标准的一碗面里会撒上葱花或者香菜,食客可以选择要葱花还是要香菜还是两个都要还是什么都不放,这一样是原型模式。
三层模式
从这里开始,后面的是结构型模式。所谓“三层”就是将演示、应用处理和数据管理三个功能分开,分表现层,逻辑层和数据层。这个模式我觉得需要和 MVC 这个东西区分一下,这是两个完全不相关的东西。有些面试官可能会提问这里,问法比较隐蔽,就比如在问你 MVC 的时候面试官觉得你说的很抽象,需要你举一个例子,然后在你思考的时候他突然蹦出来一句“MVC 是三层模式吗?”这时候就要小心别掉坑里了(语言的艺术,嗯,懂得都懂),很容易顺口就一句“MVC 当然是三层了”,然后面试官问的问题就逐渐远离 MVC 或者三层模式了。这里要是怕掉坑,可以反问一句面试官他想问的是 “3-tier” 还是想问 MVC 和 MVP,MVVM 这些东西。
回归正题,下面看三层模式的代码
from typing import Dict, KeysView, Optional, Union
class Data:
"""Data Store Class"""
products = {
"milk": {"price": 1.50, "quantity": 10},
"eggs": {"price": 0.20, "quantity": 100},
"cheese": {"price": 2.00, "quantity": 10},
}
def __get__(self, obj, klas):
print("(Fetching from Data Store)")
return {"products": self.products}
class BusinessLogic:
"""Business logic holding data store instances"""
data = Data()
def product_list(self) -> KeysView[str]:
return self.data["products"].keys()
def product_information(
self, product: str
) -> Optional[Dict[str, Union[int, float]]]:
return self.data["products"].get(product, None)
class Ui:
"""UI interaction class"""
def __init__(self) -> None:
self.business_logic = BusinessLogic()
def get_product_list(self) -> None:
print("PRODUCT LIST:")
for product in self.business_logic.product_list():
print(product)
print("")
def get_product_information(self, product: str) -> None:
product_info = self.business_logic.product_information(product)
if product_info:
print("PRODUCT INFORMATION:")
print(
f"Name: {product.title()}, "
+ f"Price: {product_info.get('price', 0):.2f}, "
+ f"Quantity: {product_info.get('quantity', 0):}"
)
else:
print(f"That product '{product}' does not exist in the records")
def main():
"""
>>> ui = Ui()
>>> ui.get_product_list()
PRODUCT LIST:
(Fetching from Data Store)
milk
eggs
cheese
<BLANKLINE>
>>> ui.get_product_information("cheese")
(Fetching from Data Store)
PRODUCT INFORMATION:
Name: Cheese, Price: 2.00, Quantity: 10
>>> ui.get_product_information("eggs")
(Fetching from Data Store)
PRODUCT INFORMATION:
Name: Eggs, Price: 0.20, Quantity: 100
>>> ui.get_product_information("milk")
(Fetching from Data Store)
PRODUCT INFORMATION:
Name: Milk, Price: 1.50, Quantity: 10
>>> ui.get_product_information("arepas")
(Fetching from Data Store)
That product 'arepas' does not exist in the records
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
例子里的三层就是数据管理,业务逻辑和用户界面,非常简单。实际上举例子的话 3-tier 和 MVC 可能举出来的例子是一个例子,因为它们都是要让各块代码之间降低耦合度。这种时候就要从举的例子开始扩展了,可以从小到大来扩展,也可以从大到小来扩展。至于网上各种观点和看法我不做评价,相信各位也不愿意当一个概念警察,真没意思,这东西就是看自己的理解,能和给你提问题的人达成共鸣当然是最好的结果了,两个人想的有出入那就有出入吧,毕竟大家的项目经历都不一样。我拿写监督学习的代码来举一个可能不算恰当的例子,一开始会读入数据集,然后数据预处理,这两步一般是深度学习框架提供好的接口,可以算在业务逻辑里。然后有些非常新鲜的数据集一般是刚采集好就被拿来用了,这些新鲜的数据集里面表,字段,数值的描述以及预处理步骤是数据集提供方提供的,这种时候读入数据和数据预处理被划分在数据管理这里。应用处理按现在的工作流程一定是使用深度学习框架来建模,这一步是应用处理逻辑。表现或者演示就是将训练好的模型投入使用,这一步里需要在实际的机器上进行部署,有些会部署在门禁的摄像头那里,有些会部署在车上,有些部署在数据中心,有些部署在手机里,手表里;这些部署好的模型投入使用(预测工作)时就不需要训练了(当然也有一边预测一边继续训练的模型,话不要说死)。我故意举了一个和 web 开发稍微有些远的例子,防止自己又动不动陷入 3-tier 和 MVC 的掰扯中。
小结
原型模式就是克隆对象的创造型模式,三层模式是数据管理,应用逻辑,演示表现分开的结构型模式。
参考文献
- 无