Комментарии 64
А зачем вам настолько слабая связность? Вам так хочется разбираться, как ваши компоненты взаимосвязаны через эти сигналы?
Честное слово, была у меня система на loosely coupled events — мы устали ее поддерживать.
Честное слово, была у меня система на loosely coupled events — мы устали ее поддерживать.
+3
Мне нравится что не приходится заботиться об асинхронности, все делается разметкой, нравится то что не приходится писать события и делегаты, проверять есть ли на них подписчики.
Это только попытка перехода, пока я запустил только один рабочий проект. Возможно окажется что действительно не стоит, но если код документорован и сигналы описаны, разбираться в нем проще чем в коде скрытом за паттернами.
Это только попытка перехода, пока я запустил только один рабочий проект. Возможно окажется что действительно не стоит, но если код документорован и сигналы описаны, разбираться в нем проще чем в коде скрытом за паттернами.
0
Мне нравится что не приходится заботиться об асинхронности
Если вы о ней не заботитесь — вы ее не контролируете. Иногда это критично.
все делается разметкой
Это не достоинство само по себе.
нравится то что не приходится писать события и делегаты, проверять есть ли на них подписчики.
Есть больше одного способа это не делать.
разбираться в нем проще чем в коде скрытом за паттернами
Просто не надо скрывать код за паттернами, вот и все.
+3
>> Если вы о ней не заботитесь — вы ее не контролируете. Иногда это критично.
Я о ней не забочусь явно, если мне нужна синхронная обработка, я использую один атрибут, если асинхронная — другой, если нужна неучтенная логика, ничего не мешает реализовать ее, только придется учесть что не смогу ловить окончание обработки сигналов.
Разметка просто удобно, опять таки не в ней смысл.
Способ понижать связность много, описан один из них, пока в качестве эксперимента.
Я о ней не забочусь явно, если мне нужна синхронная обработка, я использую один атрибут, если асинхронная — другой, если нужна неучтенная логика, ничего не мешает реализовать ее, только придется учесть что не смогу ловить окончание обработки сигналов.
Разметка просто удобно, опять таки не в ней смысл.
Способ понижать связность много, описан один из них, пока в качестве эксперимента.
0
только придется учесть что не смогу ловить окончание обработки сигналов.
А это как раз часто важно (например, транзакции).
Способ понижать связность много, описан один из них, пока в качестве эксперимента.
Вопрос, знаете ли, в том, всегда ли максимально низкая связность является нужной целью.
Если я не могу проследить путь выполнения — меня это расстраивает.
+2
А в этом подходе нет помех для отслеживания пути выполнения, как в отладчике так и в логике.
Насчет низкой связности, я не утверждаю что она нужна всегда, все от задачи и способа решения зависит, я еще не пробовал такой подход в большом проекте, в небольших нравится, в больших может и правда будет неудобна.
Насчет низкой связности, я не утверждаю что она нужна всегда, все от задачи и способа решения зависит, я еще не пробовал такой подход в большом проекте, в небольших нравится, в больших может и правда будет неудобна.
0
А в этом подходе нет помех для отслеживания пути выполнения, как в отладчике так и в логике.
Правда? Назовите мне простой способ понять, что произойдет, когда (в вашем примере) будет обнаружен файл в папке.
(и сравните трудоемкость с решением той же задачи при «стандартном» решении)
я еще не пробовал такой подход в большом проекте
Как я уже говорил, мы пробовали LCE в большом проекте. Реально он применим только для задач класса «событие, реакция на которое нам не важна»).
+1
Если в дебаге, я просто сталю бряк на обработчике сигнала. Если я беру код проекта и хочу понять что будет при таком-то сигнале, поиском нахожу все обработчики сигнала и вижу.
0
Если я беру код проекта и хочу понять что будет при таком-то сигнале, поиском нахожу все обработчики сигнала и вижу.
Поиском? Очень смешно. Особенно при желании понять очередность выполнения.
Сравните это с линейным чтением кода (и, по необходимости, нажатием F12) — и вы поймете, почему я считаю, что события/сигналы радикально влияют на читаемость.
+1
Так суть подхода том и заключается очередность может быть в обработке сигналов одним обработчиком, но ее не должно быть при обработке одного сигнала всеми подписчиками, этим и вызвано требование неизменности данных после передачи их в сигнале.
0
ее [очередности] не должно быть при обработке одного сигнала всеми подписчиками
Кто вам это сказал?
Ну и да, проблема-то даже не в этом, а в том, сколько усилий нужно на отслеживание пути выполнения (особенно с учетом потенциальных рантайм-модификаций).
0
Мне это не сказали, это я так определяю требование к своей системе. Я хочу чтобы сигнал о событии обрабатывался одновременно всеми кто хочет о нем знать, и в тоже время чтобы последовательность сигналов обрабатывалась последовательно при необходимости теми обработчиками которые в этом нуждаются. Сейчас так и работает.
0
Посмотрел, я стараюсь добиться максимальной производительности, а в указанном проекте, есть такие вещи:
Т.е на каждый вызов обработчика мы используем Refleсtion, что плохо скажется на скорости.
Ну и к тому же, насколько я понял, этот код не берет на себя распараллеливание работы обработчиков, и в нем можно отслеживать окончание обработки сообщения?
Type type = eventHandler.GetType();
foreach (var interfaceType in type.GetInterfaces()) {
if (String.Equals(interfaceType.Name, interfaceName, StringComparison.OrdinalIgnoreCase)) {
return TryInvokeMethod(eventHandler, interfaceType, methodName, arguments, out returnValue);
}
}
Т.е на каждый вызов обработчика мы используем Refleсtion, что плохо скажется на скорости.
Ну и к тому же, насколько я понял, этот код не берет на себя распараллеливание работы обработчиков, и в нем можно отслеживать окончание обработки сообщения?
0
Посмотрите вот от этой переменной
Этот код нет, но никто не мешает его слегка доработать. Но у него есть огромный плюс в плане строгой типизации сообщений. Так же можно централизованно обрабатывать ошибки. И немножко магии для того что бы не референсить интерфейс обработчика. Достаточно в своем нэймспэйсе назвать интерфейс таким же именем и сделать такие же сигнатуры. Это может быть очень критично, если у вас развитая система модулей и плагинов.
_interfaceMethodsCache
. Этот код нет, но никто не мешает его слегка доработать. Но у него есть огромный плюс в плане строгой типизации сообщений. Так же можно централизованно обрабатывать ошибки. И немножко магии для того что бы не референсить интерфейс обработчика. Достаточно в своем нэймспэйсе назвать интерфейс таким же именем и сделать такие же сигнатуры. Это может быть очень критично, если у вас развитая система модулей и плагинов.
0
Зачем делать ID сигналов? Почему не сделать их типизированными, заодно получив возможность сопоставлять сигналу какие-то данные?
+2
Если вы имеете в виду что стоило создать базовый класс сигнала, и от него определять собственный сигналы, то ушел от этого сознательно, чтобы не загромождать код. Все сводилось к максимальным упрощениям.
0
Ваш подход ведёт к тому, что сигнал не может иметь именованных атрибутов, что очень не удобно. А обойтись можно и без базового класса.
Так же из хотелок:
1) возможность вручную подписываться на сигнал, передавая делегат
2) возможность навешивания со стороны подписчика дополнительных фильтров, указывающих, когда его надо вызывать
Так же из хотелок:
1) возможность вручную подписываться на сигнал, передавая делегат
2) возможность навешивания со стороны подписчика дополнительных фильтров, указывающих, когда его надо вызывать
0
Первое скорее всего добавлю, по сути все есть для такой реализации кроме необходимости, декларативная разметка все таки удобна.
Второе — специально не стал добавлять, это я делаю в другом проекте по обмену сообщениями между компонентами, здесь посчитал лишним, т.к. снизит производительность.
Зачем сигналу именованные аттрибуты? Ведь по сути это однозначный идентификатор какого-то события, которое не должно толковаться по разному.
Второе — специально не стал добавлять, это я делаю в другом проекте по обмену сообщениями между компонентами, здесь посчитал лишним, т.к. снизит производительность.
Зачем сигналу именованные аттрибуты? Ведь по сути это однозначный идентификатор какого-то события, которое не должно толковаться по разному.
-1
Событие: со сканера отпечатков получена картинка.
Атрибуты, без которых событие совершенно бессмысленно:
1) полученная картинка,
2) номер сканера или ссылка на объект, его представляющий,
3) метка времени получения события.
Из уже три. Куда их положить? В массив — и во что превратится код после этого? В анонимный класс — не получится. Остается только создать свой составной тип данных, который по хорошему и надо называть сигналом.
Атрибуты, без которых событие совершенно бессмысленно:
1) полученная картинка,
2) номер сканера или ссылка на объект, его представляющий,
3) метка времени получения события.
Из уже три. Куда их положить? В массив — и во что превратится код после этого? В анонимный класс — не получится. Остается только создать свой составной тип данных, который по хорошему и надо называть сигналом.
+2
В статье это описано, данные описывающие все эти три поля лежат в объекте класса (ScanerStamp например), ссылка на который передается в сигнале, и не нужно никаких массивов, быстро и просто.
0
Я именно про это и написал. Появляется новый класс — ScanerStamp. Вопрос: почему надо обязательно оборачивать его в сигнал, и нельзя сделать его самого сигналом?
+1
Можно, но не стоит. Пример:
путь будет чат, у нас есть класс Message, с данными о сообщении, и есть сообщения от пользователя и от системы. Если я сделаю класс Message сигналом, я не смогу подписаться отдельно на сообщения пользователя, и на сообщения системы, придется делать базовый класс Message, наследовать UserMessage, SystemMessage. А если использовать идентификаторы сигналов, то я просто подпишусь на MessageSignal.User и на MessageSignal.System, не добавляя никакие дополнительные сущности.
путь будет чат, у нас есть класс Message, с данными о сообщении, и есть сообщения от пользователя и от системы. Если я сделаю класс Message сигналом, я не смогу подписаться отдельно на сообщения пользователя, и на сообщения системы, придется делать базовый класс Message, наследовать UserMessage, SystemMessage. А если использовать идентификаторы сигналов, то я просто подпишусь на MessageSignal.User и на MessageSignal.System, не добавляя никакие дополнительные сущности.
-1
> Если я сделаю класс Message сигналом, я не смогу подписаться отдельно на сообщения пользователя, и на сообщения системы, придется делать базовый класс Message, наследовать UserMessage, SystemMessage.
А что мешает иметь один класс на два сигнала?
А что мешает иметь один класс на два сигнала?
0
Тогда может я неправильно понял что имелось в виду, можно всевдокодом показать как стоит решить в вашем понимании описаную схему, когда я хочу в разных обработчиках получать разные сигналы но с данными описанными одним классом (Message)?
0
Схема совпадает с вашей, но вместо единого класса rtSignal можно указать любой свой.
0
Сейчас я посылаю сигнал так:
Signal(ID, state)
Ловлю так:
[SignalHanlder(ID)]
Обработчик(rtSignal signal)
{
/*достаю данные*/
SomeType data = (SomeType)signal.State;
}
По сути это аналог асинхронных операций в C#, где в асинхронную функцию передается object state. Я не могу все равно понять как вы предлагаете решить, при генерации сигнала я и сейчас могу указать в качестве передаваемых данных любой класс, и получить к нему доступ через signal.State
Signal(ID, state)
Ловлю так:
[SignalHanlder(ID)]
Обработчик(rtSignal signal)
{
/*достаю данные*/
SomeType data = (SomeType)signal.State;
}
По сути это аналог асинхронных операций в C#, где в асинхронную функцию передается object state. Я не могу все равно понять как вы предлагаете решить, при генерации сигнала я и сейчас могу указать в качестве передаваемых данных любой класс, и получить к нему доступ через signal.State
0
Проблема этого подхода в том, что нет ровным счетом никакой связи между тем, что вы положите в сигнал, и достанете из сигнала. Иными словами, у вас обмен данными между компонентами становится нетипизованным. Привет ошибкам времени выполнения.
0
Да, и еще есть такая вещь, которая с развитием процессоров становится все более актуальной, это асинхронность, microsoft делает много хорошего в этом направлении, тот же PLINQ, всякий сахар вроде await, но все это делается все равно в привычных рамках ООП, и нам все еще приходится самим создавать потоки, пускай и в виде тасков, но самим. Нужно отслеживать окончание исполнения задач, чтобы определить когда рессурсы станут ненужными.
Не желаете метнуться в сторону F# с его MailboxProcessor, AsyncWorkflows, и тысячей и одной других приятных штук?
0
В таком подходе было бы круто уйти от атрибутов и основываться на именовании методов и классов.
То есть в место
Писать что то типа
То есть в место
[rtSignalAsyncHanlder(BufferSignal.FileInBuffer)]
void ProcessFileInBuffer(rtSignal signal)
Писать что то типа
void AsyncSignalProcessFileInBuffer(rtSignal signal)
0
Я если чесно не совсем понял чего вы хотелись добиться, поэтому могу ошибаться.
Судя по всему вы изобретаете Actor Model, в .NET уже есть куча реализаций: уже упомянутый MailboxProcessor, TPL Dataflow, ActorFX и так далее. Плюс похожие схемы легко реализуются на Reactive Extensions.
Если суть в диспатчерах (команда-хендлер) то выделенных проектов насколько я знаю нема, но есть милионы cqrs фреймворков, которые это делают. Технически это четыре строки кода (особенно если пользоваться dynamic кейвордом).
Судя по всему вы изобретаете Actor Model, в .NET уже есть куча реализаций: уже упомянутый MailboxProcessor, TPL Dataflow, ActorFX и так далее. Плюс похожие схемы легко реализуются на Reactive Extensions.
Если суть в диспатчерах (команда-хендлер) то выделенных проектов насколько я знаю нема, но есть милионы cqrs фреймворков, которые это делают. Технически это четыре строки кода (особенно если пользоваться dynamic кейвордом).
+2
Я хочу попробовать изменить архитектуру приложений на .NET, перейти на более слабосвязную. Для этого сделал этот проект, в принципе он аналогичен указанным в статье сигналам и слотам. Сейчас я хочу определить насколько удачен этот подход, для чего перевожу на него часть рабочих проектов, и справшиваю здесь мнения и советы.
0
Подход с разделением на команды(ваши сигналы) и хендлеры ипользуется уже кучу времени в .NET. Можете взяглянуть на NCQRS, можно на ICommand в WPF, NServiceBus. Даж в моем микро проектике — комманд диспатчер — github.com/chaliy/zaz. Просто задача настолько простая что никто это в отдельные библиотеки не выносит.
Бай зе вей, можете еще очереди посмотреть, с балансировкой нагрузки, распределенностью и тому подобным. Тож самое разделение, на команды и хендлеры, только между ними еще мега умный диспатчер.
Бай зе вей, можете еще очереди посмотреть, с балансировкой нагрузки, распределенностью и тому подобным. Тож самое разделение, на команды и хендлеры, только между ними еще мега умный диспатчер.
0
построить взаимодействие исключительно через интерфейсы и фабрики (что увеличит размер кода раза в два, и существенно усложнит читабельность)
Дело в том, что использование паттернов и программирование от интерфейсов — это best practice, применяемый всеми нормальными разработчиками на протяжении долгого времени. Ваша схема, к сожалению, создаст эффект снижения читабельности, потому что велосипед. Я, честно говоря, не понял, зачем вы используете термин «сигналы» вместо «события», поясните разницу? Реализаций event-driven подхода в .net хватает.
Возможно, это неясно изложено в статье, но мне кажется, вы немного путаете проблемы. Plinq, await и асинхронность используется для параллельной обработки, а вовсе не для снижения связности кода. Если мы говорим о межпотоковом взаимодействии, то сигналы имеют смысл, но лучше Вам положиться на готовые реализации, чем использовать свою — это сложная тема и вы неминуемо наступите на грабли.
Гляньте ZMQ, не знаю, как там с .net интеграцией, но, может, концептуально будет интересно.
Удачи!
-2
1. События означают что один класс знает о другом и об его события, т.е. я говорю classInstance.EventName += ()=>{}, использование сигналов позволяет мне уйти от этого, класс теперь должен знать только о событии и ему неважно какая именно реализация какого класса его сгенерирует.
2. Я не путаю, асинхронность просто является следствием подхода, мне не нужно теперь писать в коде создание потоков, достаточно указать атрибут rtSignalAsyncHanlder, и обработка сигнала будет вестись асинхронно.
2. Я не путаю, асинхронность просто является следствием подхода, мне не нужно теперь писать в коде создание потоков, достаточно указать атрибут rtSignalAsyncHanlder, и обработка сигнала будет вестись асинхронно.
0
ZMQ — это очереди сообщений для распределенной архитектуры. У меня же очереди сигналов в рамках одного приложения (где возможна передача данных по ссылке).
0
В статье речь о С#. Меняем сигналы на евенты, а слоты на делегаты\методы — получаем Event Aggregator.
0
Зачем мне менять на Event'ы, если вся суть подхода чтобы от них уйти?
0
Я не о тех евентах о которых Вы. О типизированных. У нас же С#.
Например:
Вы видимо не прочитали что из себя представляет паттерн Event Aggregator.
Например:
public class UserSelected
{
private readonly int userId;
public UserSelected(int userId)
{
this.userId = userId;
}
public int UserId
{
get
{
return this.userId;
}
}
}
Вы видимо не прочитали что из себя представляет паттерн Event Aggregator.
0
В языке программирования C# есть похожая конструкция с другой терминологией и синтаксисом: события играют роль сигналов, а делегаты — роль слотов.
Это, собственно, цитата из ссылки, которую вы дали.
0
SourceForge? Серьезно?
0
А куда?
0
Ну если Гит/Меркуриал не знаете, то с Subversion — самое оно на GitHub.
+1
А в чем разница, чем SourceForge хуже? Я искал по .NET Open Source, чтобы и код выложить и можно было оставлять по багам замечания. Ну и на SourceForge также svn есть.
0
www.codeplex.com/ смотрели?
0
Злоупотребление эвентами ухудшает читабельность кода, так как трудно отследить всех подписчиков. Но можно найти хотя бы источник событий, так как они являются частью интерфейса. В вашем же случае с читабельностью еще хуже, так как отследить невозможно не только подписчиков, но и источник — сигналы не входят в интерфейс. Ctrl+F в данном случае костыль, так как ищет по строкам и не понимает семантики. Не совсем понял, как управлять областью видимости сигналов, и возможно ли это в принципе.
+4
Затронутая тема интересна, но извините, при чем тут жопа?
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Смена парадигмы программирования на C#, переход на сигналы и очереди (слоты)