События и ухудшение навигации

6yrop

Использование событий ухудшает навигацию по коду. В общем-то, это почти тривиальный факт. И он является частным случаем того факта, что навигация ухудшается при использовании изменяемого состояния. События это изменяемый массив колбеков, т.е. изменяемое состояние. Тем не менее, хочу привести код, на котором явно видно ухудшение навигации.
Код с событием:
 
 
class Program
{
static void Main
{
var a = new A;
var b = new B;
a.Event1 += b.M1;
a.M1;
}
}

class A
{
public void M1
{
Event1;
}

public event Action Event1 = => { };
}

class B
{
public void M1
{
}
}

Код без события:
 
 
class A
{
private readonly B b;

public A(B b)
{
this.b = b;
}

public void M1
{
b.M1;
}
}

class B
{
public void M1
{
}
}

В первом варианте нам надо самим проследить controll flow от создания экземпляра класса A до вызова метода a.M1 и найти в этом flow подписку на событие. Controll flow может быть довольно сложным.
Во втором варианте современная IDE показывает тело метода A.M1:

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

marat7256

Что же делать, если вся задача именно в обработке событий (часто внешних)?

6yrop

Что же делать, если вся задача именно в обработке событий (часто внешних)?
Что такое внешние события? События у классов из сторонней библиотеки, сорци которой вы не можете править? Если так, то вопрос о выборе решения не стоит, у вас просто нет выбора.

Maurog

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

marat7256

Бери шире!
События от внешних устройств, клавиатура там, мышь, еще что-нибудь... И нет никаких внешних библиотек, все надо писать самому.

6yrop

События от внешних устройств, клавиатура там, мышь, еще что-нибудь... И нет никаких внешних библиотек, все надо писать самому.
Если весь код полностью твой, то не обязательно делать события у своих классов/типов.

marat7256

Если весь код полностью твой, то не обязательно делать события у своих классов/типов.
Ну, так вопрос как раз в том "как делать".
Спрятать голову с песок, вынеся событийность из классов/типов и делать вид, что их (событий) у нас нет?

6yrop

как бы тут вроде тема и раскрывается: динамический полиморфизм
а события и изменяемое состояние упомянуты лишь что бы запутать читателя имхо
Ситуация аналогичная вот этой:
 
class A1
{
public void M1
{
Console.WriteLine(P1);
}

public string P1 { get; set; }
}

class A2
{
private readonly string p1;

public A2(string p1)
{
this.p1 = p1;
}

public void M1
{
Console.WriteLine(p1);
}
}

Для A2 навигация Value Origin легко находит значения P1. Для A1 придется отслеживать controll flow от создания экземпляра до вызова метода M1.

6yrop

Ну, так вопрос как раз в том "как делать".
Спрятать голову с песок, вынеся событийность из классов/типов и делать вид, что их (событий) у нас нет?
Реализовывать требуемый алгоритм. Если в самом алгоритме нет динамичности (а такое часто бывает то и события (на классах или объектах) не нужны.
Событие от мышки не есть событие на объекте. Тут у слова "событие" две различные семантики.

kokoc88

Для любителей динамических языков эта проблема малозаметна (поскольку там с навигацией итак плохо но для тех, кто использует преимущества статической типизации, это весьма существенный вопрос.
Я согласен. Но как эта тема будет reducto ad controllable query?

marat7256

Событие от мышки не есть событие на объекте. Тут у слова "событие" две различные семантики.
Так ты можешь объяснить словами свою семантику?

6yrop

Так ты можешь объяснить словами свою семантику?
Совсем грубо семантика события на объекте выглядит так: у объекта есть поле-коллекция, в которую можно добавлять/удалять колбеки. Более точно и детально смотри спецификацию своего языка.

marat7256

Ага. То есть вся тема о том, что указатели сильно могут запутать код, а указатели на функции сделают это в два раза быстрее. Спасибо Кэп.

6yrop

Ага. То есть вся тема о том, что указатели сильно могут запутать код, а указатели на функции сделают это в два раза быстрее. Спасибо Кэп.
Вышесказанное относится и к языкам, в которых нет явных указателей.

6yrop

Ага. То есть вся тема о том, что указатели сильно могут запутать код, а указатели на функции сделают это в два раза быстрее. Спасибо Кэп.
Это намекает на то, что у тебя ООП головного мозга. У тебя либо РЕАЛЬНЫЕ объекты, либо низкоуровневые указатели, так?
От фразы "объекты, которые отражают наш реальный мир" меня начинает подташнивать.

Ivan8209

>> То есть вся тема о том, что указатели сильно могут запутать код,
>> а указатели на функции сделают это в два раза быстрее. Спасибо Кэп.
> Это намекает на то, что у тебя ООП головного мозга.
Это намекает на то, что у тебя IDE головного мозга.
(Между прочим, первый и второй примеры у тебя заведомо неравнозначны,
поэтому сравнивать их просто так --- бессмысленно.)
---
"Университет развивает все способности, в том числе и глупость."

6yrop

 
Это намекает на то, что у тебя IDE головного мозга.

IDE по крайней мере из реального мира, а не из бредней
сравнивать их просто так
просто так их никто и не сравнивает
ты пост прочитал?

Ivan8209

> IDE по крайней мере из реального мира, а не из бредней
ООП тоже из реального мира.
>> сравнивать их просто так
> просто так их никто и не сравнивает
> ты пост прочитал?
Да. Ты написал два кривых примера, которые неравнозначны,
а потом сравнил их по произвольно взятому малоосмысленному признаку.
Более того, потом и ещё бреда добавил, как будто мало было.
---
"Университет развивает все способности, в том числе и глупость."

6yrop

ООП тоже из реального мира.
Его увидеть можно? IDE можно.

Serab

А как же cohesion/coupling? События помогают развязать объекты, снять зависимости. Навигация страдает, конечно.

kokoc88

События помогают развязать объекты, снять зависимости.
Помогают, но в C# control flow становится такой кашей, что лучше развязывать не через события.

Serab

просто тоже тут над одним местом думаю. Тут плюсы, но я сделал подписывание. Но терзают смутные сомнения. Раньше было жестко зашито, код трудно понимался.

kokoc88

Но терзают смутные сомнения.
Ты ещё сомневаешься? Смесь event driven с изменяемым состоянием и без корутин - самый адский вариант, который только можно придумать в области разработки ПО.
Раньше было жестко зашито, код трудно понимался.
Мне вообще не ясно, как будет пониматься большой объём кода, в котором много неявных вызовов.

Serab

Возможно. Надо видимо вообще какой-то отдельный класс, отвечающий за состояние выделить. А то когда кучка объектов должна синхронно изменять состояние, начинает поднадоедать.

kokoc88

Надо видимо вообще какой-то отдельный класс, отвечающий за состояние выделить. А то когда кучка объектов должна синхронно изменять состояние, начинает поднадоедать.
Я не понял, о чём ты хотел сказать, и как это связать с событиями.

Serab

Да я так, сам с собой.
Есть кучка объектов, которые слабо связаны между собой, через простые интерфейсы. Но они должны менять состояние одновременно. Раньше этих тонких интерфейсов вообще не было, все были повязаны. Я развязал, но вот эту часть, которая отвечает за смену состояния, сделал через событие. Надо видимо сделать по-другому.

kokoc88

Но они должны менять состояние одновременно.
Главный вопрос в том, как это происходит. Если смена состояния происходит последовательно, то проблем обычно не возникает. Если хаотично, когда разные объекты могут менять одни и те же данные в любом порядке, и их поведение зависит от этих данных, то проблемы будут в любом случае. Даже в stateless архитектуре. В этом случае лучше явно выстраивать объекты в очередь для выполнения изменений.
Я развязал, но вот эту часть, которая отвечает за смену состояния, сделал через событие.
Когда возникнет ошибка, ты просто заебёшься выяснять, какие условия к ней привели.

Serab

Не, в этом месте все последовательно. Просто грубо говоря инициирует смену состояния объект A (всегда причем, только он но он знает только про B, B знает про C и D. И все они должны при этом понять, что что-то изменилось (и еще получить какой-то контекст этого нового состояния). Вот эти промежуточные уведомления и сделаны через событие. Просто этих B, C и D много на одном A висит, он выбирает одного B и ему говорит "поехали".
Когда возникнет ошибка, ты просто заебёшься выяснять, какие условия к ней привели.
Ну логика тут довольно железная, причем код основной, работает всегда. Тут баг легко будет отловить. Но от событий попробую избавиться.

kokoc88

И все они должны при этом понять, что что-то изменилось (и еще получить какой-то контекст этого нового состояния).
Они что-нибудь делают по факту смены состояния или нет?

Serab

Ну если бы не делали, я бы этого не городил :) Но там мало действий, они все связаны именно с осознанием смены состояния.

kokoc88

Ну если бы не делали, я бы этого не городил Но там мало действий, они все связаны именно с осознанием смены состояния.
А почему нельзя явно сделать что-то типа foreach ... some_interface->state_changed без события?

Serab

Да, возможно на это и поменяю. Благо общий интерфейс для этого есть. По сути это будет так же, как сейчас, но все должны будут подписаться у A напрямую, а не друг за дружку.
Т.е. сейчас
B->NotifyMe(A)
C->NotifyMe(B)
D->NotifyMe(B)
А ты предлагаешь
B->NotifyMe(A)
C->NotifyMe(A)
D->NotifyMe(A)
Хотя так не выйдет, ибо C и D много, как я говорил. Каждый из них приписан одному B, а все B к одному A.
Видимо просто реально сделаю это отдельным классом для смены состояния.

kokoc88

Хотя так не выйдет, ибо C и D много, как я говорил. Каждый из них приписан одному B, а все B к одному A.
Я опять не до конца понял, что ты делаешь. Как я понял, твои объекты ссылаются друг на друга, зачем здесь события, если родители могут уведомлять потомков, реализуя Composite.

Serab

Ну Composite — это то же самое. Т.е. я "событием" в данном случае называю схему, когда есть интерфейс уведомления и подписывания. Те, кому надо, подписываются (= добавляются узлами Composite). Это же плюсы, тут реальных событий нет.
Т.е. есть Composite. Все равно по коду сложнее понять, кто конкретно будет уведомлен в итоге по сравнению с прямым вызовом.

kokoc88

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

Serab

Такое реверсивное поведение только усложняет логику программы. Добавлять должен либо родитель, либо третье лицо. Лучше избегать ситуаций, когда дочерний объект добавляется в родителя.
Но родитель не может добавить, потому что не хочет знать о конкретных детях, его конфигурирует "кто-то другой". Так вот этот кто-то другой теперь должен при добавлении этих детей еще и не забывать их подписать? Что-то сложно поверить. Т.е. мне кажется, это не лучше, чем если дети самоподписываются. Лучше — по-другому сделать вообще.

kokoc88

Так вот этот кто-то другой теперь должен при добавлении этих детей еще и не забывать их подписать?
При чём тут подписывание, у родителя же есть список детей, который наполняется "этим кем-то". Из этого сразу же получается Composite. Здесь не нужно никакого отдельного "подписывания".

6yrop

Но родитель не может добавить, потому что не хочет знать о конкретных детях, его конфигурирует "кто-то другой". Так вот этот кто-то другой теперь должен при добавлении этих детей еще и не забывать их подписать? Что-то сложно поверить. Т.е. мне кажется, это не лучше, чем если дети самоподписываются. Лучше — по-другому сделать вообще.
Напиши код, условно говоря в одном методе. Где есть ветвление вводишь полиморфизм (возможно несколько раз). Потом раскидаешь с помощью IDE код по нужным типам. Код сам тебе подскажет правильную структуру, поскольку в коде уже есть все связи. А так, ты пытаешься все связи проворачивать в голове. Это как решать систему уравнений в голове, хотя на бумаге она решается тривиально.
Оставить комментарий
Имя или ник:
Комментарий: