О чём Центр сообщений Xamarin.Forms мне не сообщил

Центр Сообщений Xamarin.Forms - подписка

Не так дав­но я ска­зал, что каж­дый раз, когда исполь­зую Центр Сооб­ще­ний Xamarin.Forms — чув­ствую что жуль­ни­чаю.

В кон­це кон­цов, я же дол­жен уметь струк­ту­ри­ро­вать при­ло­же­ние так, что­бы ком­по­нен­ты мог­ли общать­ся друг с дру­гом без посред­ни­ка.

Но все­гда есть осо­бый слу­чай…

Случай с Центром Сообщений

В основ­ном про­ек­те может про­изой­ти что-то, о чём он хочет что­бы зна­ли про­ек­ты плат­форм… напри­мер, нача­ло про­дол­жи­тель­ной фоно­вой загруз­ки.

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

Так­же в одной вью-моде­ли может про­изой­ти что-то, о чём нуж­но знать дру­гой вью-моде­ли…

Вот это и есть вес­кие при­чи­ны для исполь­зо­ва­ния Цен­тра Сооб­ще­ний.

Кажет­ся, син­так­сис очень про­стой…

MessagingCenter.Subscribe<TSender>(object, string, Action<TSender>);

и

MessagingCenter.Send<TSender>(TSender, string);

Кажет­ся, что это жуль­ни­че­ство и… непра­виль­но.

На «Evolve 2016» в выступ­ле­нии о про­из­во­ди­тель­но­сти Xamarin.Forms, Джей­сон Смит (Jason Smith) ска­зал, что вме­сто стан­дарт­но­го цен­тра сооб­ще­ний пред­по­чи­та­ет исполь­зо­вать что-то дру­гое, типа Prism. Кто я, что­бы спо­рить с чело­ве­ком, создав­шим Xamarin.Forms? Но… Я бы пред­по­чел поль­зо­вать­ся базо­вы­ми воз­мож­но­стя­ми Xamarin.Forms, насколь­ко это воз­мож­но.

О чём Центр Сообщений мне не сообщил

Огром­ная поль­за Цен­тра Сооб­ще­ний в том, что он дела­ет воз­мож­ным обмен меж­ду дву­мя клас­са­ми, кото­рые ниче­го друг о дру­ге не зна­ют. Фак­ти­че­ски, не важ­но даже – суще­ству­ет ли экзем­пляр под­пис­чи­ка на момент отправ­ки сооб­ще­ния.

Дру­ги­ми сло­ва­ми — клас­сы не свя­за­ны друг с дру­гом.

Но глав­ное, о чём не сооб­щил мне Центр Сооб­ще­ний, и при­чи­на по кото­рой я нико­гда не исполь­зо­вал его пра­виль­но — то как я исполь­зо­вал аргу­мент TSender в функ­ции MessagingCenter.Send.

Я все­гда ста­вил туда имя клас­са-отпра­ви­те­ля.

Напри­мер, если сооб­ще­ние отправ­ля­ет­ся из LoginViewModel, а под­пи­сы­ва­ют­ся на него в UserDetailViewModel — под­пис­ка выгля­де­ла бы так:

MessagingCenter.Subscribe<LoginViewModel>(this, "successful_login", (lvm) => HandleLogin(lvm) );

Полу­ча­ет­ся что UserDetailViewModel зна­ет о LoginViewModel… и это не выгля­дит пра­виль­но.

Так и есть.

Не буду вда­вать­ся в при­чи­ны того, с чего я решил, что это пра­виль­ный спо­соб под­пис­ки…

Вер­ным реше­ни­ем будет создать класс, не име­ю­щий ниче­го обще­го ни с отпра­ви­те­лем, ни с под­пис­чи­ком.

Это моет быть класс-мар­кер или класс без свойств и функ­ций.

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

Отправ­ка ста­нет выгля­деть так:

    MessagingCenter.Send<LoginMessage>(new LoginMessage, "successful_login");

А под­пис­ка так:

    MessagingCenter.Subscribe<LoginMessage>(this, "successful_login", (lm) => HandleLogin(lm));

Во-от… намно­го луч­ше — теперь вью-моде­ли ниче­го не зна­ют друг о дру­ге.

И даже луч­ше — теперь сооб­ще­ние может исхо­дить от одно­го из про­ек­тов плат­фор­мы без вся­ких про­блем (Нет ника­кой воз­мож­но­сти реа­ли­зо­вать отправ­ку клас­са из про­ек­та плат­фор­мы в основ­ной про­ект).

В чём мораль сей басни?

В Цен­тре Сооб­ще­ний все­гда исполь­зуй­те общий или мар­кер­ный класс, кото­рый не свя­зан ни с отпра­ви­те­лем, ни с полу­ча­те­лем!

Дополнение от переводчика

Для упро­ще­ния под­пис­ки на сооб­ще­ния я создаю клас­сы со ста­ти­че­ски­ми тек­сто­вы­ми свой­ства­ми, зна­че­ние кото­рых сов­па­да­ет с их име­нем. Это поз­во­ля­ет не запо­ми­нать тек­сто­вые «име­на» сооб­ще­ний и пере­да­вать полез­ные дан­ные через объ­ект сооб­ще­ния, если необ­хо­ди­мо:

    public class LoginMessage
    {
        public static string SuccessfulLogin => nameof(SuccessfulLogin);
        public static string InvalidUserName => nameof(InvalidUserName);
        public static string SessionExpired => nameof(SessionExpired);

        public LoginMessageData Data { get; }
        
        public LoginMessage(LoginMessageData data)
        {
            Data = data;
        }
    }

тогда под­пис­ка ста­нет выгля­деть вот так:

MessagingCenter.Subscribe<LoginMessage>(this, LoginMessage.InvalidUserName, (lm) => HandleLogin(lm));

а отправ­ка так:

MessagingCenter.Send<LoginMessage>(new LoginMessage(new LoginMessageData()), LoginMessage.InvalidUserName);
Поде­лить­ся:

Оригинал: https://codemilltech.com/messing-with-xamarin-forms-messaging-center/