访问者模式和依赖注入模式 19/30 | Python 主题月
写在前面
本文正在参加「Python主题月」,详情查看活动链接
这个月是 Python 活动月,我决定尝试用 Python 来刷这 30 天的每日一题和随机一题。然后如果周末有精力,我想捣鼓捣鼓这个python-patterns
设计模式对我来说更多的是学习而不是我的个人经验总结,所以我很可能理解偏,如果有大佬见到了请及时指出,我之所以选择在掘金来写一些个人的东西是因为这里的文章质量更高,我不希望后来者看到了这些文章被误导。
访问者模式
class Node:
pass
class A(Node):
pass
class B(Node):
pass
class C(A, B):
pass
class Visitor:
def visit(self, node, *args, **kwargs):
meth = None
for cls in node.__class__.__mro__:
meth_name = "visit_" + cls.__name__
meth = getattr(self, meth_name, None)
if meth:
break
if not meth:
meth = self.generic_visit
return meth(node, *args, **kwargs)
def generic_visit(self, node, *args, **kwargs):
print("generic_visit " + node.__class__.__name__)
def visit_B(self, node, *args, **kwargs):
print("visit_B " + node.__class__.__name__)
def main():
"""
>>> a, b, c = A(), B(), C()
>>> visitor = Visitor()
>>> visitor.visit(a)
generic_visit A
>>> visitor.visit(b)
visit_B B
>>> visitor.visit(c)
visit_B C
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
依赖注入模式
import datetime
from typing import Callable
class ConstructorInjection:
def __init__(self, time_provider: Callable) -> None:
self.time_provider = time_provider
def get_current_time_as_html_fragment(self) -> str:
current_time = self.time_provider()
current_time_as_html_fragment = '<span class="tinyBoldText">{}</span>'.format(
current_time
)
return current_time_as_html_fragment
class ParameterInjection:
def __init__(self) -> None:
pass
def get_current_time_as_html_fragment(self, time_provider: Callable) -> str:
current_time = time_provider()
current_time_as_html_fragment = '<span class="tinyBoldText">{}</span>'.format(
current_time
)
return current_time_as_html_fragment
class SetterInjection:
"""Setter Injection"""
def __init__(self):
pass
def set_time_provider(self, time_provider: Callable):
self.time_provider = time_provider
def get_current_time_as_html_fragment(self):
current_time = self.time_provider()
current_time_as_html_fragment = '<span class="tinyBoldText">{}</span>'.format(
current_time
)
return current_time_as_html_fragment
def production_code_time_provider() -> str:
"""
Production code version of the time provider (just a wrapper for formatting
datetime for this example).
"""
current_time = datetime.datetime.now()
current_time_formatted = f"{current_time.hour}:{current_time.minute}"
return current_time_formatted
def midnight_time_provider() -> str:
"""Hard-coded stub"""
return "24:01"
def main():
"""
>>> time_with_ci1 = ConstructorInjection(midnight_time_provider)
>>> time_with_ci1.get_current_time_as_html_fragment()
'<span class="tinyBoldText">24:01</span>'
>>> time_with_ci2 = ConstructorInjection(production_code_time_provider)
>>> time_with_ci2.get_current_time_as_html_fragment()
'<span class="tinyBoldText">...</span>'
>>> time_with_pi = ParameterInjection()
>>> time_with_pi.get_current_time_as_html_fragment(midnight_time_provider)
'<span class="tinyBoldText">24:01</span>'
>>> time_with_si = SetterInjection()
>>> time_with_si.get_current_time_as_html_fragment()
Traceback (most recent call last):
...
AttributeError: 'SetterInjection' object has no attribute 'time_provider'
>>> time_with_si.set_time_provider(midnight_time_provider)
>>> time_with_si.get_current_time_as_html_fragment()
'<span class="tinyBoldText">24:01</span>'
"""
if __name__ == "__main__":
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS)
小结
参考文献
- 无