在 python 中使用协程和函数作为方法保持 SOLID 和 DRY
Stay SOLID and DRY with coroutines and functions as methods in python
我有那个代码示例:
from time import sleep
import asyncio
class bird:
def __init__(self, sleeptime=1):
self.var = sleeptime
def wait_meep(self):
sleep(self.var)
print("Meep")
def do_sth(self):
print("Dop Dop Do do ...")
class bird_async:
def __init__(self, sleeptime=1):
self.var = sleeptime
async def wait_meep(self):
await asyncio.sleep(self.var)
print("Meep")
def do_sth(self):
print("Dop Dop Do do ...")
如您所见,两个客户端大部分相同,并且应包含相同的名称(以便每个人都知道会发生什么)。现在我要DRY写bird_async(bird)
。因为 bird
中的每个扩展名也应在 bird_async
中使用。这是可能的,但我的同事说,它不是 SOLID,因为我已经覆盖了 wait_meep
。现在我正在寻找不同的灵魂并找到抽象的 classes。
我不知道的是,如果创建一个抽象 class birdBase(ABC)
也是 SOLID。我也会在那里覆盖,因为首先它是一个函数,然后是一个协程,或者我在这里错了吗?
什么是 SOLID 和 DRY 解决方案,可以在不重命名方法的情况下将这两个 class 放在一起?
DRY 解决方案是某种子classing,就像您已经做过的那样。
我认为在您的条件下很难实现“SOLID”解决方案。事实上,您有两个函数 wait_meep
,它们实际上具有不同的签名和语义。即,第一个块用于睡眠间隔,可以是任意长。第二个 OTOH 是异步的,即需要特殊的调用语义并同时运行。
有点类似的情况是标准库中的 Queue
class。你有 get
和 get_nowait
方法,它们以不同的方式做同样的事情。第二个例子可以是 __iter__
和 __aiter__
方法。
所以我认为唯一“正确”的解决方案是重命名其中一种方法。这会产生副作用,您可以将它们全部写成一个 class,即减少移动部件的数量。
问题
您有两个 class 具有可以利用继承的重复代码,但不想覆盖 wait_meep。此外,wait_meep
和 do_sth
中存在无法自定义的硬编码文本。
解决方案
考虑以下内容以使您的代码更加可靠和干燥:
from time import sleep
import asyncio
class Bird:
DEFAULT_WAIT_MEEP_TEXT = "Meep"
DEFAULT_DO_STH_TEXT = "Dop Dop Do do ..."
def __init__(self, sleeptime=1):
self.sleeptime = sleeptime
def wait_meep(self, text=DEFAULT_WAIT_MEEP_TEXT):
sleep(self.sleeptime)
print(text)
def do_sth(self, text=DEFAULT_DO_STH_TEXT):
print(text)
class BirdAsync(Bird):
async def wait_meep_async(self, text=DEFAULT_WAIT_MEEP_TEXT):
await asyncio.sleep(self.var)
print(text)
备注
这里的想法是创建一个特定的方法来执行异步 (DRY) 并确保硬编码默认值改为在 args 中传递(Open-Closed 原则)。当然,这一切都可以在原始的 Bird class 中完成,而不需要 BirdAsync class
我有那个代码示例:
from time import sleep
import asyncio
class bird:
def __init__(self, sleeptime=1):
self.var = sleeptime
def wait_meep(self):
sleep(self.var)
print("Meep")
def do_sth(self):
print("Dop Dop Do do ...")
class bird_async:
def __init__(self, sleeptime=1):
self.var = sleeptime
async def wait_meep(self):
await asyncio.sleep(self.var)
print("Meep")
def do_sth(self):
print("Dop Dop Do do ...")
如您所见,两个客户端大部分相同,并且应包含相同的名称(以便每个人都知道会发生什么)。现在我要DRY写bird_async(bird)
。因为 bird
中的每个扩展名也应在 bird_async
中使用。这是可能的,但我的同事说,它不是 SOLID,因为我已经覆盖了 wait_meep
。现在我正在寻找不同的灵魂并找到抽象的 classes。
我不知道的是,如果创建一个抽象 class birdBase(ABC)
也是 SOLID。我也会在那里覆盖,因为首先它是一个函数,然后是一个协程,或者我在这里错了吗?
什么是 SOLID 和 DRY 解决方案,可以在不重命名方法的情况下将这两个 class 放在一起?
DRY 解决方案是某种子classing,就像您已经做过的那样。
我认为在您的条件下很难实现“SOLID”解决方案。事实上,您有两个函数 wait_meep
,它们实际上具有不同的签名和语义。即,第一个块用于睡眠间隔,可以是任意长。第二个 OTOH 是异步的,即需要特殊的调用语义并同时运行。
有点类似的情况是标准库中的 Queue
class。你有 get
和 get_nowait
方法,它们以不同的方式做同样的事情。第二个例子可以是 __iter__
和 __aiter__
方法。
所以我认为唯一“正确”的解决方案是重命名其中一种方法。这会产生副作用,您可以将它们全部写成一个 class,即减少移动部件的数量。
问题
您有两个 class 具有可以利用继承的重复代码,但不想覆盖 wait_meep。此外,wait_meep
和 do_sth
中存在无法自定义的硬编码文本。
解决方案
考虑以下内容以使您的代码更加可靠和干燥:
from time import sleep
import asyncio
class Bird:
DEFAULT_WAIT_MEEP_TEXT = "Meep"
DEFAULT_DO_STH_TEXT = "Dop Dop Do do ..."
def __init__(self, sleeptime=1):
self.sleeptime = sleeptime
def wait_meep(self, text=DEFAULT_WAIT_MEEP_TEXT):
sleep(self.sleeptime)
print(text)
def do_sth(self, text=DEFAULT_DO_STH_TEXT):
print(text)
class BirdAsync(Bird):
async def wait_meep_async(self, text=DEFAULT_WAIT_MEEP_TEXT):
await asyncio.sleep(self.var)
print(text)
备注
这里的想法是创建一个特定的方法来执行异步 (DRY) 并确保硬编码默认值改为在 args 中传递(Open-Closed 原则)。当然,这一切都可以在原始的 Bird class 中完成,而不需要 BirdAsync class