Nasza droga do Reactive Extensions – cz. 1 – Wstęp

Artykuł w wersji anglojęzycznej jest dostępny na blogu firmy Grape Up

Jeden z produktów jakie ostatnio rozwijaliśmy w naszym zespole, jest aplikacją desktopową opartą o rozbudowaną bibliotekę, która dostarczała spójne API do komunikacji, opartej o różne protokoły związane z telekomunikacją, co sprawiało że generowała naprawdę dużo sygnałów.

Architektura biblioteki

Większość klas w tej bibliotece pasowała do takiego wzorca:

public class Class1
{
    public void SetDescription(string description, CompletionHandler handler)
    {
         /* Some logic here */
    }

    public void SetSize(int size, CompletionHandler handler)
    {
        /* Some logic here */
    }

    public string Description { get; private set; }

    public int Size { get; private set; }

    public event EventHandler DescriptionChanged;

    public event EventHandler SizeChanged;
}
  • Metody SetDescription i SetSize wysyłają wartość przekazaną jako pierwszy parametr i wywołują metodę przekazaną jako Handler gdy serwer potwierdzi albo odrzuci żądanie
  • Właściwości Description i Size zwracają ostatnią znaną wartość
  • Zdarzenia DescriptionChanged i SizeChanged są uruchamiane gdy serwer powiadomi aplikację o zmianie wartości – niezależnie czy zmiana była spowodowana przez wywołanie metody Set* czy przez inne interakcje z serwerem np. działanie drugiej aplikacji

Biblioteka przetwarza wszystkie żądania na swojej własnej wewnętrznej puli wątków co ma swoje zalety i wady. Podstawową zaletą był fakt iż biblioteka, sama z siebie, nie wpływała na działanie wątku głównego aplikacji. Wadą był fakt iż wszystkie callbacki i zdarzenia podnoszone przez bibliotekę były uruchamiane na innym wątku, co wymuszało opracowanie jakiegoś mechanizmu do komunikacji między wątkami.

Ręczne zarządzanie wątkami – Nie chcesz tego robić

Już od początku rozwoju aplikacji zorientowaliśmy się, że ręczna komunikacja między wątkami kończy się powstaniem dużej ilości długiego i powtarzalnego kodu, który wygląda mniej więcej tak:

public class SomeOtherClass : IDisposable
{
    private readonly SomeClass _someObject;
    private readonly DispatcherService _dispatcherService;

    public SomeOtherClass(
        SomeClass someObject,
        DispatcherService dispatcherService)
    {
        _someObject = someObject;
        _dispatcherService = dispatcherService;

        _someObject.DescriptionChanged += SomeObjectOnDescriptionChanged;
        _someObject.SizeChanged += SomeObjectOnSizeChanged;
    }
    
    private void SomeObjectOnDescriptionChanged(object sender, EventArgs e)
    {
        _dispatcherService.Invoke(LoadState);
    }

    private void SomeObjectOnSizeChanged(object sender, EventArgs e)
    {
        _dispatcherService.Invoke(LoadState);
    }

    private void LoadState()
    {
        PropertyVisibleOnUI = $"Description: {_someObject.Description} Size: {_someObject.Size}";
    }
    
    public string PropertyVisibleOnUI { get; set; }

    public void Dispose()
    {
        _someObject.DescriptionChanged -= SomeObjectOnDescriptionChanged;
        _someObject.SizeChanged -= SomeObjectOnSizeChanged;
    }
}

Kod staje się jeszcze bardziej złożony jeżeli założymy, że niektóre właściwości klasy są złożonymi obiektami, które mają właściwości, zdarzenia, metody asynchroniczne. A takich warstw może być znacznie więcej.

API do obsługi wiadomości tekstowych napisane w tej konwencji mogło wyglądać tak:

public interface IConversationService
{
    event EventHandler ConverstionStarted;
    event EventHandler ConversationEnded; 
    IEnumerable Conversations { get; }
}

public interface IConversation
{
    event EventHandler MessageAdded;
    void SendMessage(string body, CompletionHandler handler); 
    IEnumerable Messages { get; }
}

public interface IMessage
{
    event EventHandler IsReadChanged;
    bool IsMine { get; }
    string Body { get; }
    bool IsRead { get; }
}

Koniecznie potrzebowaliśmy lepszego rozwiązania. W drugiej części artykułu opiszę jak wzorzec event-aggregator pomógł nam pokonać te problemy.

2 odpowiedzi do “Nasza droga do Reactive Extensions – cz. 1 – Wstęp”

  1. Pingback: dotnetomaniak.pl

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *