#python #python-3.x #dry #solid-principles
#python #python-3.x #сухой #solid-принципы
Вопрос:
У меня есть этот пример кода:
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 ...")
Как вы можете видеть, оба клиента в основном идентичны и должны содержать одно и то же имя (чтобы все знали, чего ожидать). Теперь я хочу быть СУХИМ и писать bird_async(bird)
. Потому что каждое расширение в bird
также должно использоваться в bird_async
. Это возможно, но мой коллега говорит, что это ненадежно, потому что я перезаписал wait_meep
. Теперь я ищу разные soultions и нашел абстрактные классы.
Чего я не знаю, так это того, будет ли создание абстрактного класса birdBase(ABC)
также надежным. Я бы также перезаписал их, потому что сначала это была функция, а затем подпрограмма, или я здесь ошибаюсь?
Что может быть НАДЕЖНЫМ и сухим решением для объединения этих обоих классов без переименования методов?
Ответ №1:
Сухое решение — это своего рода подкласс, как вы уже делали.
Я думаю, что «твердого» решения очень трудно достичь в ваших условиях. Факт в том, что у вас есть две функции wait_meep
, которые имеют фактически разную сигнатуру и семантику. А именно, первый блокирует интервал ожидания, который может быть произвольно длинным. Второй OTOH является асинхронным, то есть требует специальной семантики вызова и выполняется одновременно.
Несколько сопоставимым случаем является Queue
класс из стандартной библиотеки. Там у вас есть get
и get_nowait
методы, которые делают то же самое, но по-разному. Вторым примером могут быть методы __iter__
и __aiter__
.
Поэтому я думаю, что единственным «правильным» решением было бы переименование одного из методов. Что имело бы побочный эффект, заключающийся в том, что вы могли бы записать все это как один класс, то есть уменьшить количество движущихся частей.
Ответ №2:
Проблема
У вас есть два класса с дублирующимся кодом, которые могли бы использовать наследование, но не хотят перезаписывать 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)
Примечания
Идея здесь состоит в том, чтобы создать конкретный метод для выполнения async (DRY) и гарантировать, что жестко заданные значения по умолчанию вместо этого передаются в аргументах (принцип Open-Closed). Конечно, все это можно сделать в исходном классе Bird без использования класса BirdAsync
Комментарии:
1. Предлагаемая реализация не вызывает
sleep(..)
вBirdAsync
‘swait_meep_async(..)
асинхронно — она должна была быть вызванаawait asyncio.sleep(..)
.2. @Yoel извините, исправлено.