@Kirill-Gorelov
С ума с IT

Проектирование и архитектура приложений?

Парни, всем привет.
Я немного запутался, у меня каша в голове после прочтения Макконела, Банды четырех и Роберта Мартина. В планах еще прочитать Фаулера и про DDD.

По отдельности, я понимаю как все работает, но как собрать воедино, у меня разбегаются глаза от выбора разных возможностей, которые мне предоставляет ООП.

Теперь к примеру.
Беру конкретную ситуацию, которую мне нужно решить - это логирование в программе.
Я хочу вести trace лог, как исполняется алгоритм программы, писать в файл. Ловить исключения и отправлять на почту и в телеграмм.

Что я для этого сделал, приведу условный код.(python)
from classes import Logger

def example_code():
	Logger.log('старт скрипта')

	try:
		r = 1+1
		Logger.log('Нормально все прошло идем дальше')
	except Exception as e:
		Logger.log('Исключение')

	Logger.log('конец работы скрипта')


Так как это учебный пример, я бы все оставил. Но в реальном приложении, я бы так не делал, доработал, особенно блок except.
Т.к. по условию, я хочу ловить исключения и записывать в файл, а так же отправлять себе уведомления на почту и телеграмм.

Я тут вижу два варианта.
Первый:
Просто добавить еще класс MyTelegramm и MyException, и вызывать их в блоке except. Класс MyException буде отдельно сам писать еще исключения.
from classes import Logger
from classes import MyEmail
from classes import MyTelegramm
from classes import MyException

def example_code():
	Logger.log('старт скрипта')

	try:
		к = 1+1
		Logger.log('Нормально все прошло идем дальше')
	except MyException as e:
		Logger.log('Исключение')
		MyEmail.send()
		MyTelegramm.send()

	Logger.log('конец работы скрипта')


Вариант второй.
И всю логику по обработки исключений запихнуть в класс MyException.
И уже в классе MyException, унаследовать три класса и там уже записывать и отправлять логи мне на почту.
class MyExeption(Logger, MyEmail, MyTelegramm):

Я понимаю, что это далеко не предел, и можно оставить и первый, и второй вариант и даже придумать еще что-то. Так же понимаю, что я в поиске идеального варианта, но почему бы и нет?).
  • Вопрос задан
  • 151 просмотр
Решения вопроса 1
@MarkusD
все время мелю чепуху :)
Самое первое и самое главное: клиентский код должен оставаться чистым, состоять как можно больше из кода реализации алгоритма и как можно меньше из кода обслуживающего эту реализацию.
Клиентский код ничего не должен знать о том, как ты хочешь, например, оповещать пользователя об исключительных ситуациях. Ему просто незачем это знать, ему важно просто работать.

Поэтому, from classes import MyTelegramm - думаю, не стоит. Мне там и try-catch тоже не нравится. Пусть пользователь блоки try-catch пишет только для себя и только тогда, когда ему это надо. Когда он точно может словить исключение и выкрутиться из ситуации. Про такие исключения тебе, по-хорошему, и знать-то не стоит, т.к. они лишь меняют ветвь исполнения, но не обрывают его.
Клиентский код стоит выполнять в контролируемой среде, внутри специального защитного блока try-catch, в котором ты уже будешь ловить все доступные тебе исключения и производить их диспетчеризацию.

Скажем, doit все действия задачи выполняет внутри своего защитного контура. При этом, таких защитных контуров у doit несколько, включая и самый базовый, отвечающий за оповещение о критических сбоях работы. Что мне в doit малость не нравится, так это что обработка исключения делается по месту вместо того, чтобы организовать некоторый диспетчер, в котором это исключение уже и обрабатывать.

Свой защитный контур я бы связал с диспетчером (Dispatcher) исключений. Если контуров делать несколько, то и диспетчеров я тоже сделал бы несколько, разместив все диспетчеры в локаторе (Service locator).

Диспетчер начал бы свою работу с того, что завернул бы исключение в конверт (Envelope, [2], [3]). Конверт просто легче читать, чем исключение непредсказуемого типа. Заворачивание я бы сделал с помощью абстрактной фабрики (Abstract factory) конвертов, в которой исключение каждого типа и от каждого источника было бы завернуто в правильный конверт согласно конфигурации фабрики.

Получив конверт, диспетчер посетил(Visitor) бы с этим конвертом почтовый ящик каждого адресата. Почтовый ящик адресата можно снабдить стратегией (Strategy) приема конвертов, согласно которой почтовый ящик или перепишет конверт к себе, или оставит без внимания, в зависимости от написанных на конверте данных. Управлять почтовыми ящиками может локальный реестр (Registry) диспетчера, в котором так же может быть реализована и функция обхода (Visit) для посещения почтовых ящиков.

Почтовый ящик обслуживает адресата. Адресат может подписаться (Observer) на поступление письма, а может приходить к ящику за новыми письмами по расписанию и доставать только первые несколько писем, а не все. Как выгребать почтовый ящик - вопрос конфигурации адресата.
Когда адресат забирает письма, он раздает их в конечные точки, в роли которых уже и будут выступать все эти MyTelegramm или MyLog, но строго сконфигурированные и принимающие уже только копию письма.

Весь этот ворох шаблонов проектирования тянет на целый отдельный слой с набором подсистем, в каждой из которых будет твориться своя незабываемая чертовщина:
- Подсистема защитного контура и только диспетчера исключений (вместе с локатором, если он нужен).
- Подсистема обертывания исключения в конверт, фабрики конвертов и конфигуратора этой фабрики.
- Подсистема стратегии поведения клиента и, возможно, конфигуратора стратегии для конкретного клиента.
- Подсистема клиентов конверта, реестра клиентов, фабрики клиентов и конфигура этой фабрики.

Ну и, конечно же, любую часть всего этого безобразия можно отбросить и написать проще. :)
На начальных этапах можно заглушить стратегии и свести конфигураторы к виду линейной функции.
MVP тут, на мой взгляд, будет выглядеть как цельная система с гарантированной доставкой конверта с любым исключением во все созданные конечные точки, проводя их от защитного контура, через диспетчер и до адресата через почтовый ящик.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
dmitriylanets
@dmitriylanets
веб-разработчик
И всю логику по обработки исключений запихнуть в класс MyException.
- не верно, логика обработки исключений должна быть в коде который использует ваш функционал и ловит эти исключения,
поэтому первый вариант
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы