Недавно я начал читать «Чистую архитектуру» Р. Мартина, чтобы стать лучшим разработчиком и прочим, и решил переписать один из моих проектов, чтобы все было «правильно». TL; DR находится в конце вопроса.
Прежде чем я покажу некоторые схемы, мне нужно показать значение некоторых стрелок в случае, если я использую их неправильно.
![enter image description here](https://i.stack.imgur.com/2Lv0g.png)
Это означает, что я импортирую что-то из компонента B в компонент A только для проверки типа, то есть A.function возвращает B.class. Поэтому единственное, что меня волнует, это something
, это то, что он остается в том же месте и с тем же именем. Я думаю, что я мог бы удалить эти стрелки, так как я пишу в Python, и мне не нужно показывать типы, однако я пытаюсь помещать подсказки типов везде, где это возможно.
![enter image description here](https://i.stack.imgur.com/HT1h3.png)
Здесь комп. А использует что-то из комп. B и на самом деле зависит от его поведения и интерфейса. Если что-то меняется в комп. В таком случае мне, вероятно, придется что-то менять в комп. B, а также.
Итак, ниже вы можете увидеть часть системы, несколько компонентов (пакеты в моем случае): ![enter image description here](https://i.stack.imgur.com/R3c5E.png)
TopLevel
Компонент содержит определения двух структур данных (классов данных), интерфейс (абстрактный класс), который их использует, и реализации этого класса. Strategies
Компонент содержит интерфейс и его реализации, которые делают приложение полезным. * Компонент 1030 * DataProviders
содержит интерфейсы для извлечения данных и их реализации (мне нужно хранить разные вещи, которые можно хранить в разных местах, поэтому несколько интерфейсов). Разные компоненты могут нуждаться в использовании разных провайдеров. Config
компонент не содержит интерфейсов или классов; в нем хранятся параметры конфигурации для некоторых классов, а при запуске приложения создаются объекты, которые могут использоваться как для всей системы, так и для пары других.
Первое, что мне не понравилось в этой схеме, это Зависимости идут в обоих направлениях. Итак, во-первых, я решил присмотреться к Strategies<-->DataProviders
. Давайте расширим компоненты:
![enter image description here](https://i.stack.imgur.com/Fevt6.png)
Это выглядит не очень понятно, но я старался изо всех сил. Итак, я хотел сделать что-то с Strategies<-->DataProviders
. Я подумал:
Хорошо, мы можем переместиться IProviderB
в Strategies
и IProviderA
в TopLevel
. Я не уверен, что это хорошая идея, но, по крайней мере, я разрешу эту глупую зависимость.
После того, как я это сделал, зависимости начали выглядеть так:
![enter image description here](https://i.stack.imgur.com/7l40r.png)
Теперь DataProviders
зависит от Strategies
, а Strategies
зависит от TopLevel
, что выглядит нормально. Однако у нас все еще есть TopLevel<-->Config
и другие компоненты, в зависимости от того, что создано и хранится в Config
. Я думал о внедрении зависимости, но не уверен, что это применимо в моем случае:
from config import provider_a
@dataclass
class MoreSpecificDS(SomeDS):
bid: int
def business_object(self) -> BusinessData:
return provider_a.get_business_object_by_id(self.bid)
Итак, мой вопрос: могу ли я удалить эту зависимость из Config
? Должен ли я сделать что-то вроде этого:
@dataclass
class MoreSpecificDS(SomeDS):
def __init__(self, fieldA, fieldB, bid, provider_a):
.
.
def business_object(self) -> BusinessData:
return self.provider_a.get_business_object_by_id(self.bid)
и настроить MoreSpecificDS
в Config
? Или есть какой-то другой подход? Или у меня есть другие проблемы, о которых стоит подумать?
TL; DR: мне не нравится, что мой компонент верхнего уровня (и многие другие) зависят от компонента Config
. Я думал о внедрении зависимостей, но не уверен, что это применимо здесь (см. Примеры кода выше). Каковы хорошие подходы к настройке вашей конфигурации таким образом, чтобы ваши компоненты (возможно, не все, но все же) не зависели от этого? Желательно без введения новых фреймворков / библиотек в систему.