Freitag, 4. Dezember 2015

Akka.NET Adventskalender – Tür 4

Verschiedene Aktor Typen

Gestern haben wir unseren ersten Aktor erzeugt und ihn von der Klasse UntypedActor abgeleitet. Es gibt aber noch mehr Basisklassen, die für die Erzeugung von Aktoren in Frage kommen. Heute werden wir uns die drei wichtigsten und grundlegendsten ansehen.

Der einfachste

Der UntypedActor, den wir gestern kennengelernt haben, ist der einfachste Typ von Aktor, den Du erstellen kannst. Letztlich ist es Geschmacksfrage, welcher Typ von Aktor von Dir als Basis verwendet wird. Also zeige ich Dir die drei wichtigsten und Du kannst dann von Fall zu Fall entscheiden, welcher Dir für welchen Zweck am besten gefällt.

Beim UntypedActor musst Du lediglich eine Methode bereit stellen, die sämtliche Nachrichten erhält, die an den Aktor gesandt werden.

protected override void OnReceive(object message)
{
    // handle all messages received
}

Du entscheidest, ob Du eine Nachricht annimmst und was Du mit ihr anfängst. Typischerweise wird eine mehr oder weniger lange if-else Kaskade innerhalb dieser Methode stehen. Allerdings kann dieser Aktor bereits verschiedene Verhalten bekommen. Mehr dazu gleich.

Etwas mehr C# bitte

Vermutlich war Dir das obige Beispiel zu wenig an die Fähigkeiten – insbesondere die Typsicherheit von C# angelehnt. Wenn Du einen typsicheren Aktor haben möchtest (dieser kann dann jedoch keine Verhalten bekommen) dann kannst Du Deinen Aktor von TypedActor ableiten. Dieser wird zuätzlich mit jeder Menge IHandle<> Interface Deklarationen verziert und erhält dem Interface entsprechend eine Menge Handle() Methoden, die sich jeweils um die Bearbeitung eines Typs von Nachricht kümmern. Unser HelloWorld-Aktor sähe dann so aus:

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class HelloTyped : TypedActor, IHandle<string>
    {
        public void Handle(string message)
        {
            Console.WriteLine("Received: '{0}'", message);
        }
    }
}

Auf den ersten Blick sehen solche Aktoren sehr vernünftig aus, vor Allem wenn man Typsicherheit im Hinterkopf hat. Jedoch können solche Aktoren kein Verhalten bekommen, da das verschiedene Reaktionen auf gleiche Nachrichten-Typen erfordert.

Und noch einer

Mein persönlicher Favorit (nein, nein beeinflussen will ich Dich hiermit nicht) ist der ReceiveActor. Es ist eine Mixtur aus Typsicherheit und Flexibilität. Und das häufig gepriesene Verhalten kann er auch. Aber schauen wir uns erst einmal an, wie unser einfacher Hello-Aktor aussehen würde:

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class HelloReceive : ReceiveActor
    {
        public HelloReceive()
        {
            Receive<string>(s => HandleStringMessage(s));
        }

        public void HandleStringMessage(string message)
        {
            Console.WriteLine("Received: '{0}'", message);
        }
    }
}


Mit diesem Wissen ausgestattet kannst Du künftig entscheiden, wie Du Deine Aktoren baust.

Wie war das jetzt mit dem Verhalten?

Wenn wir an uns Menschen denken, so sind wir durchaus in unterschiedlicher Verfassung. Manchmal sind wir ärgerlich, manchmal freundlich oder unterscheiden zwischen anderen Gemütszuständen. Je nach unserem Zustand reagieren wir auf gleiche Ereignisse möglicherweise unterschiedlich. Genau das ist es, wenn wir bei einem Aktor von Verhalten (behavior) sprechen.  Typischerweise nutzt man die Verhalten, um einen Zustandsautomaten zu modellieren.
Um zu verstehen, was ich meine, bauen wir uns einen einfachen Aktor, der zwischen Wahrheit erzählen und Lügen hin- und herschaltet. Wir fragen ihn nach der Zeit und erhalten gelegentlich eine wahre und gelegentlich eine gelogene Antwort.

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class TimeReader : ReceiveActor
    {
        private Random random;

        public TimeReader()
        {
            random = new Random();

            Become(TruthTelling);
        }

        #region behaviors
        private void TruthTelling()
        {
            Receive<string>(s => TellCorrectTime(s));
        }

        private void Lying()
        {
            Receive<string>(s => TellAnyTime(s));
        }
        #endregion

        #region message handlers
        private void TellCorrectTime(string _)
        {
            Console.WriteLine("Current time is: {0:hh:mm}-really",
                DateTime.Now
            );
            if (random.Next(100) < 50)
                Become(Lying);
        }

        private void TellAnyTime(string _)
        {
            Console.WriteLine("Current time is: 11:92-do you believe it?");
            Become(TruthTelling);
        }
        #endregion
    }
}


Ein mögliches Resultat könnte so aussehen:

Current time is: 10:26-really
Current time is: 11:92-do you believe it?
Current time is: 10:26-really
Current time is: 11:92-do you believe it?
Current time is: 10:26-really
Current time is: 10:26-really

Wenn Du in der Akka.NET Dokumentation zu diesem Thema nachschlägst, wirst Du noch weitere Informationen erhalten. Zum Beispiel auch zum "stashing", also dem Aufschieben von Nachrichten bis ein Aktor wieder im richtigen Zustand ist.

Aktoren können aber noch viel mehr – das sehen wir uns morgen an.

Keine Kommentare:

Kommentar veröffentlichen