Python, IoC, исключения и слабая связь
Скажем, у нас есть два класса, Class A
с пользовательской ошибкой, которая часто выдается, и это является частью его функциональности.
#a.py
class AError(Exception):
"""This exception flags a functional error"""
pass
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError() #This exception is raised eventually, business code removed for clarity
Class B
, который использует класс A для выполнения некоторых операций.
#b.py
import a
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except a.AError:
pass #This was expected, here will go some business logic
Это прекрасно работает, но это становится проблемой, когда у меня разные типы A. В идеале, я бы хотел полностью отделить A от B, чтобы я мог передать любой класс, такой как A, который удовлетворяет тому же интерфейсу, но я не могу из-за исключения (так как он не является частью самого интерфейса)
В C++ у меня был бы заголовочный файл с определением моего интерфейса плюс исключения, которые будут реализовывать конкретные классы. Как это обычно решается в Python? Или сказал по-другому, что является наиболее питоническим подходом?
Я думал следующие варианты:
1. создать модуль с исключениями и, возможно, базовый класс / метакласс (способ C++/Java)
#common.py
class AErrorBase(Exception):
pass
class AIface(object):
def work(self):
raise NotImplemented()
,
#a.py
import common
class AError(common.AErrorBase):
pass
class A(common.AIface):
def work(self):
"""Throws AError when it is tired"""
raise AError()
,
#b.py
import common
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except common.AErrorBase:
pass #This was expected
2. передать исключение в качестве аргумента
#a.py
class AError(Exception):
pass
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError()
,
#b.py
class B(object):
def make_him_work(self, afected, ex_type):
try:
afected.work()
except ex_type:
pass #This was expected
3. Исключение как атрибут класса, поэтому он становится частью интерфейса.
#a.py
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError()
class AError(Exception):
pass
,
#b.py
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except afected.AError:
pass #This was expected
4. Не используйте исключение, просто код возврата. ! C дни вернулись!
Любой другой вариант? Что ты находишь более "питоническим"?
Изменить: Добавлены комментарии, чтобы уточнить цель исключения. Это должно быть обработано в B
Примечание. Это вполне может означать, что я подхожу к проблеме со своим старым фоном C++, я просто хочу знать, как вы применяете IoC в python, когда у нас есть исключения. Не стесняйтесь говорить, что все мои подходы являются мусором, и я должен сделать это по-другому
1 ответ
Мой класс будет выглядеть так:
class A(object):
def can_work(self):
"returns True if an A can work otherwise False (e.g. is an A is tired)"
return ...
def work(self):
assert not self._is_tired, "Test if self.can_work() first!"
...
Таким образом, вы позволяете пользователям A проверять, должны ли они использовать работу. Утверждение полезно для отладки и проверки того, что вы или другие пользователи не забыли об интерфейсе.
Класс B будет использовать A следующим образом:
class B(object):
def make_him_work(self, afected):
if afected.can_work():
afected.work()