Asynchrone Kommunikation mit dem Async-Pattern (Refactored)
Geschrieben von Thomas Christian in Architektur, Pattern, TippsNach dem ich meinen vorherigen Artikel zum Thema “Asynchrone Kommunikation mit dem Async-Pattern” vorgestellt hatte, hat Ralf Westphal auf seinem Blog “One Man Think Tank Gedanken” das Vorgehen zur Implementierung einer asynchronen Kommunikation mit Hilfe von Event-Based-Components vorgestellt, welches eine sehr gute Alternative zum Async-Pattern ist. Seinen Eintrag nehme ich zum Anlass, um meine Beispiel-Implementierung des Async-Pattern zu refaktorisieren, um eine bessere Trennung der Verantwortlichkeiten und somit eine bessere Lesbarkeit zu erreichen.
In das Form wird die Abhängigkeit “CalcProxy” injected.
static void Main()
{
CalcProxy calcProxy = new CalcProxy(new Calculator());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(calcProxy));
}
Im Gegensatz zur vorherigen Version wird in der Form nun nicht mehr der Calculator direkt erzeugt und verwendet, sondern auf den injekteten CalcProxy zugegriffen.
public partial class Form1 : Form {
private readonly ICalcProxy m_calcProxy;
public Form1(ICalcProxy calcProxy) {
InitializeComponent();
m_calcProxy = calcProxy;
m_calcProxy.CalcCompleted += CalculatorCalcCompleted;
}
private void Run_Click(object sender, EventArgs e) {
int number;
if (Int32.TryParse(txbEingabe.Text, out number)) {
m_calcProxy.CalcAsync(number, number);
}
}
void CalculatorCalcCompleted(object sender, CalcEventArgs eventArgs) {
lblCounter.Text = eventArgs.UserState.ToString();
}
}
Der CalcProxy wiederum bekommt die Abhängigkeit zum Calculator injected und stellt für die Calculator.Calc-Methode sowohl eine synchrone als auch eine asynchrone Methode zur Verfügung.
public class CalcProxy : ICalcProxy {
private readonly ICalculator m_calculator;
public event CalcCompletedEventHandler CalcCompleted;
private AsyncOperation m_asyncOperation;
private bool m_isRunning;
public CalcProxy(ICalculator calculator) {
m_calculator = calculator;
}
public int Calc(int number) {
return m_calculator.Calc(number);
}
public void CalcAsync(int number, object userState) {
lock (this) {
if (m_isRunning) {
throw new InvalidOperationException("Diese Operation wird bereits ausgeführt");
}
m_isRunning = true;
m_asyncOperation = AsyncOperationManager.CreateOperation(userState);
ThreadPool.QueueUserWorkItem(ExecuteCalc, number);
}
}
private void ExecuteCalc(object state) {
var result = Calc((int)state);
m_asyncOperation.PostOperationCompleted(CalcCompletedSuccessful, result);
}
private void CalcCompletedSuccessful(object result) {
if (CalcCompleted != null) {
CalcCompleted(this, new CalcEventArgs(null, false, (int)result, result));
}
}
}
Nun enthält der Calculator nur noch die Methode die für den Calculator notwendig ist, nämlich die Calc-Methode.
public class Calculator : ICalculator {
public int Calc(int number) {
Thread.Sleep(10000);
return number * number;
}
}
Freut mich, dass du meinen Blogartikel hilfreich fandest. Du hast jetzt den Async-Infrastrukturcode herausgezogen. Die Businesslogik ist wieder deutlich sichtbar. Super.
Aber ich frage mich: Was machst du, wenn du drei verschiedene Klassen wir Kalkulator in der Weise async machen willst? Willst du dann drei Proxies basteln?
-Ralf
[...] This post was mentioned on Twitter by .NET German Bloggers, DeveloperBlogs. DeveloperBlogs said: Asynchrone Kommunikation mit dem Async-Pattern (Refactored): Nach dem ich meinen vorherigen Artikel zum Thema “Asy… http://bit.ly/9eV2kr [...]
Hallo,
ich hab ein paar Fragen zu deinem Demo.
1. Warum benutzt du immer noch m_ als Prefix für lokale Objekte?
2. Warum benutzt du englische UND deutsche Membernamen?
3. Warum hat der CalcEventArgs-Constructor vier Parameter, obwohl de Facto nur eines im Demo benutzt wird? Ist das YAGNI oder spielt das in anderen Szenarien eine Rolle? Wofür wären dann die anderen Parameter gedacht? Kannst du das mal erläutern?
4. Wo ist der ganze Rest von dem Code?
Hallo,
um deine Fragen zu beantworten:
zu 1) Das ist halt alles eine Frage des Geschmacks. Ich persönlich mag es, wenn ich auf dem ersten Blick erkennen kann, dass es sich um eine globale Variable handelt.
zu 2) Ich gehe mal davon aus, dass du auf das txtEingabe anspielst. Naja, abgesehen, von den Controls, sind alle Variablen englisch. Die Controls sind das Frontend und dort wollte ich eigentlich deutsch bleiben. In der eile ist mir wohl der lblCounter als englischer Name durchgerutscht.
zu 3) Das liegt daran, dass CalcEventArgs von AsyncCompletedEventArgs ableitet und die zusätzlichen Parameter benötigt.
zu 4) Ich weiß zwar nicht genau welchen Code du vermisst, aber ich denke mal du meinst CalcEventArgs. Bei diesem Blogeintrag handelt es sich um eine refaktorisierte Version meines vorherigen Eintrags. Dort ist auch das CalcEventArgs vorhanden.
http://blog.aztec-project.org/2010/05/05/asynchrone-kommunikation-mit-dem-async-pattern/
Ich hoffe ich konnte dir die Fragen ausreichend beantworten.
Gruß Tom