Ответ на вопрос
Я не видел еще модульных монолитов, как он должен выглядеть, чтоб считаться достаточно модульным ?) Как обеспечить low coupling ?)
Приведу небольшой пример(код рабочий, можете поиграться)
from dataclasses import dataclass
from typing import Any, Callable
# Тип так же может предоставлять интерфейс, а его внутренности могут быть чем угодно.
# В примере это упущенно, но в последней реализации костыльно показано, что это мы тоже используем.
@dataclass()
class State:
values: list[Any]
def extract_value(self) -> Any:
return self.values[0]
def __len__(self):
return len(self.values)
def __getitem__(self, key):
return self.values[key]
# predicates.py #
def is_done(state: State) -> bool:
return len(state) == 1
def is_even(state: State) -> bool:
return len(state) % 2 == 0
# predicates.py #
# transformers.py #
def transform(state: State) -> State:
new_state = State([state[0] + state[1]] + state[2:])
return new_state
def transform_by_head_plus_tail(state: State) -> State:
new_state = State([state[0] + state[-1]] + state[1:len(state) - 1])
return new_state
# transformers.py #
# app.py #
# "типами" ниже задан интерфейс для работы метода. По сути мы уже обеспечили модульность данного компонента.
def Iterate(
state: State,
is_done: Callable[[State], bool],
transform: Callable[[State], State]
) -> State:
if is_done(state):
return state
state = transform(state)
return Iterate(state, is_done,transform)
def iterable_factory(
iterate_func: Callable[..., State],
stop_predicate: Callable[[State], bool],
transform: Callable[[State], State],
) -> Callable:
def decorator(state: State) -> State:
if not state:
return State([])
return iterate_func(state, stop_predicate, transform)
return decorator
# app.py #
# main.py #
FoldL = iterable_factory(Iterate, is_done, transform)
print(FoldL(State([1,5,3,7,9])))
DummyFoldL = iterable_factory(Iterate, is_even, transform_by_head_plus_tail)
print(DummyFoldL(State([i for i in range(101)])))
GausFoldL = iterable_factory(Iterate, is_done, transform_by_head_plus_tail)
print(GausFoldL(State([i for i in range(101)])))
StringFoldL = iterable_factory(Iterate, is_done, transform)
result = StringFoldL(State(
"Вызываемые объекты, которые принимают другие вызываемые объекты в качестве аргументов,"
"могут указывать на то, что их типы параметров зависят друг от друга с помощью typing.ParamSpec.".split()
))
print(result)
# main.py #
В данном примере я хотел достаточно маленьким кусочком кода показать, что конкретнее я имел ввиду в предыдущем посте.
Давайте договоримся, что модулем мы будем называть сущность, которая группирует связанные по смыслу операции. В нашем случае мы имеем модулю: app.py, predicate.py, transformers.py и модуль для запуска main.py .
По сути, модуль app.py — единая точка входа. Для метода Iterate требуются зависимости, принимающие на вход состояние(State) и возвращающие либо bool, либо новое состояние. В данном контексте типизация является интерфейсом. Тут же используется так называемая «функциональная инъекция зависимостей».
Получается обобщенный программный компонент, который благодаря слабой связанности(за счет четких интерфейсов на типах) легко может заменить свои составляющие компоненты. Сложность кода измеряется как последовательная сумма сложностей компонентов этого кода.
Подобный подход хорошо расширяется в большие проекты. Вы можете заметить «это же по сути полиморфизм!» и формально будете правы. Но понятие модульного монолита все же шире, пускай оно и включает в себя статический или динамический полиморфизм.
Это был ответ на вопрос к предыдущей статье.
Задавайте вопросы в комментариях.