none
Geschäftslogik von der Präsentationsschicht trennen RRS feed

  • Frage

  • Hallo,

    ich habe eine Frage zum o.g. Thema. Ich habe mein Formular mit einigen Methoden wie unten im Code schematisch dargestellt:

    Public Class Form1
    
        ' code
    
        Private Sub Method1()
            If Method2() Then
                ' code
            End If
            Me.TextBox1.Text &= "blabla1" & vbCrLf
            ' code
            Method3()
            Me.TextBox1.Text &= "blabla2" & vbCrLf
        End Sub
    
        Private Shared Function Method2() As Boolean
            Dim var As Boolean = False
            ' code
            Return var
        End Function
    
        Private Shared Sub Method3()
            ' code
        End Sub
    
    End Class

    Ich möchte nun die Methoden in eine eigene Klasse packen. Mein Problem ist, dass ich nicht weiss, wie ich Methode1 modifizieren muss, damit ich sie zusammen mit Methode2 und Methode3 in eine eigenständige Klasse packen kann. Methode1 greift ja auf die Textbox von Form1 zu. Muss ich dafür ein Interface definieren oder geht es über Events? Für die Profis unter euch dürfte die Frage leicht zu beantworten sein.

    Schönen Gruß,

    LittleBlueBird

    Montag, 9. Juli 2012 10:39

Antworten

  • Hallo LittleBlueBird,

    Du kannst z.B. ein Event benutzen, um Daten von einer Klasse in die andere zu transportieren.

    Ich habe das mal skizziert. Hoffentlich entspricht das Deinen Vorstellungen. (Beachte auch die Zugriffsmodihikatoren)

    Public Class Form1
        Private WithEvents myMethods As New divMethods
        Private Sub writeToBox(ByVal msg As String) Handles myMethods.upDateBox
            Me.TextBox1.Text &= msg
        End Sub
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Me.myMethods.Method1()
        End Sub
    End Class
    Public Class divMethods
        Public Event upDateBox(ByVal msg As String)
        Friend Sub Method1()
            If Method2() Then
                ' code
            End If
            RaiseEvent upDateBox("blabla1" & vbCrLf)
            ' code
            Method3()
            RaiseEvent upDateBox("blabla2" & vbCrLf)
        End Sub
        Private Shared Function Method2() As Boolean
            Dim var As Boolean = False
            ' code
            Return var
        End Function
        Private Shared Sub Method3()
            ' code
        End Sub
    End Class

    Man könnte das auch mit Property erledigen. Nur dann müsste man im Konstruktor von der Klasse divMethods die Instanz von Form1 übergeben. Dann kann man darüber wieder auf ein property von Form1 zugreifen, welches die Daten in die Textbox schreibt.

    Gruss Ellen


    Ich benutze/ I'm using VB2008 & VB2010


    Montag, 9. Juli 2012 11:11

Alle Antworten

  • Hallo LittleBlueBird,

    Du kannst z.B. ein Event benutzen, um Daten von einer Klasse in die andere zu transportieren.

    Ich habe das mal skizziert. Hoffentlich entspricht das Deinen Vorstellungen. (Beachte auch die Zugriffsmodihikatoren)

    Public Class Form1
        Private WithEvents myMethods As New divMethods
        Private Sub writeToBox(ByVal msg As String) Handles myMethods.upDateBox
            Me.TextBox1.Text &= msg
        End Sub
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Me.myMethods.Method1()
        End Sub
    End Class
    Public Class divMethods
        Public Event upDateBox(ByVal msg As String)
        Friend Sub Method1()
            If Method2() Then
                ' code
            End If
            RaiseEvent upDateBox("blabla1" & vbCrLf)
            ' code
            Method3()
            RaiseEvent upDateBox("blabla2" & vbCrLf)
        End Sub
        Private Shared Function Method2() As Boolean
            Dim var As Boolean = False
            ' code
            Return var
        End Function
        Private Shared Sub Method3()
            ' code
        End Sub
    End Class

    Man könnte das auch mit Property erledigen. Nur dann müsste man im Konstruktor von der Klasse divMethods die Instanz von Form1 übergeben. Dann kann man darüber wieder auf ein property von Form1 zugreifen, welches die Daten in die Textbox schreibt.

    Gruss Ellen


    Ich benutze/ I'm using VB2008 & VB2010


    Montag, 9. Juli 2012 11:11
  • Hallo Ellen,

    vielen Dank für die Lösung und insbesondere für den Code. Du hast mir damit sehr geholfen. Ich werde dein Beispiel implementieren, da die Möglichkeit mit den Eigenschaften meines Erachtens nicht so elegant ist, wobei sie durchaus performanter sein kann.

    Schöne Grüße,

    LittleBlueBird

    Montag, 9. Juli 2012 19:47
  • Hallo Ellen,

    vielen Dank für die Lösung und insbesondere für den Code. Du hast mir damit sehr geholfen. Ich werde dein Beispiel implementieren, da die Möglichkeit mit den Eigenschaften meines Erachtens nicht so elegant ist, wobei sie durchaus performanter sein kann.

    Schöne Grüße,

    LittleBlueBird

    Hallo LittleBlueBird,

    das freut mich sehr.

    Ich schwebe gerade auf Wolke 7. Mein neuer Rechner heute gekauft: HP OMMI27 1000eg. Das ist sowas von schick:-)

    Gruss Ellen


    Ich benutze/ I'm using VB2008 & VB2010

    Montag, 9. Juli 2012 20:01
  • Hallo Ellen,

    dann wünsche ich dir viel Vergnügen mit dem neuen All-in-One Rechner. Sieht in der Tat schick aus, wobei ich mich frage, wo da die Laufwerke (Festplatte, DVD) untergebracht sind? Bei 27" Bildschirmdiagonale kannst du ihn sogar als Fernseher verwenden ;-)

    Ich habe noch eine Frage im Zusammenhang mit deinem Beispiel. Ich habe im Internet ein ähnliches Konstrukt gesehen, da wurden jedoch Delegaten verwendet. Dein Beispiel würde also wie folgt aussehen:

    Public Delegate Sub UpdateMessageHandler(msg As String)
    
    Public Class DivMethods
    
        Public Event Message As UpdateMessageHandler
    
        Private Sub UpdateMessage(msg As String)
            RaiseEvent Message(msg)
        End Sub
    
        Friend Sub Method1()
            If Method2() Then
                ' code
            End If
            UpdateMessage("blabla1" & vbCrLf)
            ' code
            Method3()
            UpdateMessage("blabla2" & vbCrLf)
        End Sub
    
        Private Shared Function Method2() As Boolean
            Dim var As Boolean = False
            ' code
            Return var
        End Function
    
        Private Shared Sub Method3()
            ' code
        End Sub
    
    End Class

    Weisst du zufällig, ob es bei der Verwendung von Delegaten einen Vorteil gibt?

    Schöne Grüße,

    LittleBlueBird

    Montag, 9. Juli 2012 21:09
  • Hallo Ellen,

    dann wünsche ich dir viel Vergnügen mit dem neuen All-in-One Rechner. Sieht in der Tat schick aus, wobei ich mich frage, wo da die Laufwerke (Festplatte, DVD) untergebracht sind? Bei 27" Bildschirmdiagonale kannst du ihn sogar als Fernseher verwenden ;-)

    Ich habe noch eine Frage im Zusammenhang mit deinem Beispiel. Ich habe im Internet ein ähnliches Konstrukt gesehen, da wurden jedoch Delegaten verwendet. Dein Beispiel würde also wie folgt aussehen:

    Public Delegate Sub UpdateMessageHandler(msg As String)
    
    Public Class DivMethods
    
        Public Event Message As UpdateMessageHandler
    
        Private Sub UpdateMessage(msg As String)
            RaiseEvent Message(msg)
        End Sub
    
        Friend Sub Method1()
            If Method2() Then
                ' code
            End If
            UpdateMessage("blabla1" & vbCrLf)
            ' code
            Method3()
            UpdateMessage("blabla2" & vbCrLf)
        End Sub
    
        Private Shared Function Method2() As Boolean
            Dim var As Boolean = False
            ' code
            Return var
        End Function
    
        Private Shared Sub Method3()
            ' code
        End Sub
    
    End Class

    Weisst du zufällig, ob es bei der Verwendung von Delegaten einen Vorteil gibt?

    Schöne Grüße,

    LittleBlueBird

    Hallo LittleBlueBird,

    bei dieser Version wird vorher ein typisierter Funktionszeiger deklariert und damit das Event.

    Wo da jetzt der Vorteil ist, das kann ich Dir garnicht sagen. Eigentlich benutzt man so etwas eher für threading.

    Gruss Ellen

    P.S. Der Rechner ist ja fast ein ein Fernseher. das schmilzt heute alles zusammmen. Festplatte und DVD sind auch im Gehäuse verbaut.


    Ich benutze/ I'm using VB2008 & VB2010

    Dienstag, 10. Juli 2012 06:06
  • Hallo LittleBlueBird und Ellen,

    Vorteile hat das keinen - und mit Threading hat das nichts zu tun.
    Vielmehr hat jemand nur das nachgeahmt, was der VB Compiler ohnehin erzeugt
    (und als leiser Verdacht darf aufkeimen, der Code wurde von C# übernommen - ääh übersetzt).

    Es gilt: Events sind Delegaten - mit zusätzlichen Konventionen, insbesondere was Visual Basic angeht[1]

    Visual Basic erstellt aus dem

    Public Event upDateBox(ByVal msg As String)

    eine Klasse, die so aussehen würde:

    ' Pseudocode - nicht kompilierbar!
    Public Class upDateBoxEventHandler
       Inherits System.MulticastDelegate
    
       Public Overridable Sub Invoke(msg As String)
    
       ' sowie BeginInvoke, EndInvoke 
       ' der Kürze halber weggelassen
    End Class

    Würde deshalb, weil VB diese Schreibweise nicht erlaubt; ein Erben von [Multicast]Delegate ist nicht zulässig,
    das Erzeugen des (richtigen) Codes behält sich Visual Basic hier vor.

    Diese Klasse wird nun für das Ereignis verwendet, wieder als Pseudocode:

        ' Pseudocode!
    Public Custom Event upDateBox As divMethods.upDateBoxEventHandler AddHandler(value As upDateBoxEventHandler) Me.upDateBoxEvent = CType([Delegate].Combine(Me.upDateBoxEvent, value), divMethods.upDateBoxEventHandler) End AddHandler RemoveHandler(value As upDateBoxEventHandler) Me.upDateBoxEvent = CType([Delegate].Remove(Me.upDateBoxEvent, value), divMethods.upDateBoxEventHandler) End RemoveHandler End Event

    (Es fehlt ein RaiseEvent, was automatisch erzeugt wird, nur für Custom wäre es wiederum Pflicht).
    In der Summe kommt also einiges zusammen, was im Hintergrund erzeugt wird.

    Weil vom Einschmelzen bereits die Rede war: Das kann (und sollte) man auch hier tun ;-)
    Anstatt jedes Mal einen neuen Delegaten (eine neue Klasse) zu erzeugen,
    kann man in solchen Fällen auf die "vorgefertigten" Action Delegaten zurückgreifen
    oder falls eine Rückgabe benötigt wird auf einen Func-Delegaten.

    Ellens Code eingeschmolzen:

    Public Class divMethods
        Public Event upDateBox As Action(Of String)
        Friend Sub Method1()
            RaiseEvent upDateBox("blabla1" & vbCrLf)
    
            RaiseEvent upDateBox("blabla2" & vbCrLf)
    
        End Sub
    
        ' Für den Test
        Friend Shared Sub Run()
            Dim myMethods As New divMethods
            AddHandler myMethods.upDateBox, Sub(msg) Console.WriteLine("MyMethod UpdateBox {0}", msg)
    
            myMethods.Method1()
        End Sub
    End Class
    

    Den Dummycode habe ich weggelassen und Verwendung erfolgt der Kürze in einer Run-Methode.

    Gruß Elmar

    [1] Durch event wird RaiseEvent freigeschaltet und WithEvents ermöglicht den Einsatz von Handles -
    C# z. B. kennt diese Unterscheidung nicht. Aber das wäre ein weiteres Kapitel der Geschichte.

    Dienstag, 10. Juli 2012 09:00
  • Hallo Elmar,

    danke für die Aufklärung. Da habe ich wieder etwas dazu gelernt. Von Action- und Func-Delegaten hatte ich bisher keine Ahnung. Ich werde mich noch intensiver damit beschäftigen müssen.

    Zur Run-Methode habe ich noch einige Fragen:

    1. Wieso wird die Klasse divMethods nicht über "Private WithEvents ..." instantiiert?
    2. Weshalb erfolgt die Ereignisbehandlung dynamisch und nicht statisch?
    3. Wann sollte man Lambda-Ausdrücke verwenden? Ich gebe zu, den Konstrukt Sub(msg) ... noch nicht gekannt zu haben. Nach einiger Recherche habe ich aber herausbekommen, dass es sich um einen Lambda-Ausdruck handelt.

    EDIT: Auf Frage 1 habe ich selbst die Antwort gefunden. Du rufst die Run-Methode in der gleichen Klasse auf. Gleichzeitig frage ich mich aber, ob es generell besser ist, die Klasse divMethods in Form1 als "Private WithEvents ..." zu instantiieren. Oder doch lieber local in der Methode, in der ich Method1() aufrufe?

    Schöne Grüße,

    LittleBlueBird


    Dienstag, 10. Juli 2012 10:56
  • Hallo LittleBlueBird,

    Action und Func sind "vorbereitete" generische Delegaten; funktional sind sie identisch mit einem "selbstgemachten".

    1.) WithEvents ist nur notwendig, wenn man Handles verwenden will.
    Handles ist funktional mit AddHandler identisch, der Kompiler generiert dabei nur den Code dafür.
    Für den Test ist das nicht notwendig Und ich wollte nicht alles wiederholen.
    Außerdem kann man WithEvents nur auf Klassenebene verwenden.
    Du kannst die Klasse aber ebenso mit Ellens Ursprungsbeispiel verwenden.

    2.) Ereignisbehandlung muss nicht statisch sein - und typische Button_Click Handles sind es ja auch nicht.
    Der Unterschied von statischen (Shared) Methoden zu Instanzmethoden ist,
    dass Instanzmethoden der Zugriff auf die Mitglieder (Variablen, Eigenschaften, Methoden)
    der umgebenen Klasse möglich ist.
    Shared  sollte man Methoden kennzeichnen, wenn man diesen Zugriff nicht benötigt,
    weil es die Methode unabhängig verwendbar macht[1].
    Das ändert sich nicht bei Ereignissen / Delegaten.

    3.) Verwenden muss man Lambda-Ausdrücke nicht.
    Oben schien es mir z. B. angebracht, um das Beispiel kompakter zu halten
    (eine Methode hätte man erst wieder suchen müssen und langatmiger wäre es auch gewesen).

    Mehr Nutzen bringen sie u. a., wenn man mit lokalen Variablen arbeiten möchte -
    in dem Link findest Du einige Beispiele. Wobei dort Action/Func nicht konsequent genutzt wird
    und so manches Beispiel  unter den Möglichkeiten bleibt
    (mag aber Absicht sein, um keinen zu überfordern)

    Gruß Elmar

    [1] verwendet man sehr viele Shared Methoden sollte man seine Einstellung zu OOP prüfen ;-)

    Dienstag, 10. Juli 2012 12:49
  • Hallo Elmar,

    danke, dass du dir die Zeit genommen hast, meine Fragen zu beantworten. Ich bedanke mich ebenso für die Links zu den einzelnen Themen.

    zu 2) Ich bin davon ausgegangen, dass wenn die Eregnisbehandlung mit AddHandler dynamisch erfolgt, im Gegenzug die Handles-Klausel dann statisch sein muss. Das ist wohl ein Irrtum gewesen.

    zu 3) Aus den Beispielen von MSDN entnehme ich, dass Lambda-Ausdrücke meistens dann verwendet werden, wenn es sich um einzeilige bzw. einfache Methoden oder Funktionen handelt.

    Ob ich jemals alles lernen werde? Eher unwahrscheinlich! Aber ich gebe nicht auf ;-)

    Schöne Grüße,

    LittleBlueBird

    Dienstag, 10. Juli 2012 17:24
  • Hallo LittleBlueBird,

    Lambda-Ausdrücke können seit Visual Basic 2010 auch mehrzeilig sein,
    vorher waren sie auf einzeilige Funktionen beschränkt.[1]

    Lambdas wird man aber eher für kürzere Methode verwenden,
    wird es mehr hat man keine Vorteile mehr zu einer separaten Methode.

    Wobei man Methoden generell übersichtlich halten sollte,
    Pi * Daumen eine halbe Bildschirmseite - bzw. was man (mit etwas Übung) auf einen Blick erfassen kann;
    wird es mehr als eine Seite sollte man nachdenklich werden und den Code vereinfachen oder aufteilen.

    Wenn man erst mal weiß, dass es Lambdas gibt und sie grundsätzlich verstanden hat,
    erkennt man den einen oder anderen Einsatzbereich dafür -
    übers Knie brechen muss und sollte man es nicht.

    Zum Abschluss als Anregung die Klasse noch mal etwas anders:

    Public Class divMethods
        Private _notify As Action(Of String)
    
        Public Sub New(notify As Action(Of String))
            Me._notify = notify
        End Sub
    
        Friend Sub Method1()
            DoNotify("blabla1")
        End Sub
    
        Private Sub DoNotify(msg As String)
            If Me._notify IsNot Nothing Then    ' Prüfen auf Nothing, da optional
                Me._notify(msg)
            End If
        End Sub
    End Class
    
    Public Class MainForm
        ' Zwei Wege mit gleichem Ergebnis
        Private m1 As New divMethods(AddressOf Benachrichtigung1)
        Private m2 As New divMethods(Sub(msg) Console.WriteLine("Benachrichtigung 2:" & msg))
    
        Private Sub Benachrichtigung1(msg As String)
            Console.WriteLine("Benachrichtigung 1:" & msg)
        End Sub
    
        Private Sub executeButton_Click(sender As System.Object, e As System.EventArgs) Handles executeButton.Click
            m1.Method1()
            m2.Method1()
        End Sub
    End Class
    

    Gruß Elmar

    [1] C# kennt bereits seit 2.0 (VS 2005) anonyme (mehrzeilige) Methoden,
    und mit 3.0 (VS 2008) wurde die Syntax nochmal deutlich vereinfacht.
    Insofern ist man dort länger damit vertraut (und VB hinkte dort lange hinterher).

    Dienstag, 10. Juli 2012 21:38
  • Hallo Elmar,

    besten Dank für die Erklärung zu den Lambda-Asudrücken und auch für die Anregung. Möglichkeiten scheint es genug zu geben. Dann werde ich die für mich am Besten passende Lösung anwenden. Die Beispiele muss ich unbedingt aufbewahren.

    Schönen Gruß,

    LittleBlueBird

    Mittwoch, 11. Juli 2012 09:50