Meistens geht es hier um Politik

Speziell, wie wahnsinnig und menschenverachtend die inzwischen geworden ist.

Es ist aber nicht mein einziges Steckenpferd ;-). Es klang sicher schon gelegentlich an, daß ich Programmierer bin. Ich pflege seit mehr als 10 Jahren eine Branchenlösung für Abfallbetriebe, Containerdienste. Das Ganze ist für kleine Betriebe mit weniger als sagen wir mal 5 AP für die EDV. Das sind nach meinem Wissen so ca maximal so um die 50 Leute die mit der Software bedient werden. Die meisten Dateneinträge dürften Wiegungen sein. Für jede komplette Wiegung braucht es, so gut wie immer zwei Wiegungen. Ich denke, die Software wurde schon zu DOS Zeiten entwickelt, vielleicht liege ich damit aber auch schief. Wie auch immer, die Programmiere haben m.E. a) relativ typisch in MS Access programmiert und b) wenig über die Menge an Code nachgedacht. Im Zweifel lief es wohl so. Da läuft was, wir kopieren es und passen es halt an. So gibt es Redundanzen noch und nöcher, aber natürlich nicht komplett, das wäre ja zu einfach. Wo man die Redundanzen sehr gut sehen kann, ist an folgendem Code:

Private Sub DruckAngeboSum_Click()
On Error GoTo Err_DruckAngeboSum_Click

Dim objOut As CMediaOutput
    
    Set objOut = generateMediaOutputObjects(ActiveControl.Name)
    objOut.doOutput

'Dim intAntwort As Integer
'Dim strNeueZeile As String
'strNeueZeile = Chr$(10) & Chr$(13)
'
'Dim intSchleife As Integer
'Dim lngNr As Long
'Dim lngDrucker As Long, strBericht As String
'
'Dim WSP1 As Workspace, DB1 As Database, Tabelle1 As Recordset
'Set WSP1 = DBEngine.Workspaces(0)
'Set DB1 = WSP1.OpenDatabase(strDatenbankGlo)
'
'Set Tabelle1 = DB1.OpenRecordset("HT_Nummernkreise", DB_OPEN_TABLE)     ' Tabelle öffnen.
'
'If Forms!HF_AuftrAnkErz!AngebotKz = True Then
'    ' Rückfrage mit Speicherung der Antwort
'    intAntwort = MsgBox("Für diesen Auftrag wurde bereits ein Angebot gedruckt!" & strNeueZeile & "Wollen Sie diesen Ausdruck wiederholen?", 33, "Angebot drucken?")
'    If intAntwort = 2 Then
'        GoTo Exit_DruckAngeboSum_Click
'    End If
'End If
'
'' Firma Beller
'If strKundeNr = "143" Then
'    strBericht = "BR_DruckAngebotAnkErzoSum_Bel"
'Else
'    strBericht = "BR_DruckAngebotAnkErzoSum"
'End If
'
'ordneDruckerZu "Angebot", strBericht, HAUPT_DRUCKER
'
'' Prüfen ob Kontrollkästchen für Seitenansicht aktiviert
'' wenn aktiviert dann Seitenansicht, ansonsten Ausdruck auf Drucker
'If Me!Seitenansicht = True Then
'    If strKundeNr = "143" Then
'        strBericht = "BR_DruckLiefschAuftrAnkErzNot_Bel"
'    Else
'        strBericht = "BR_DruckLiefschAuftrAnkErzNot"
'    End If
'    DoCmd.OpenReport strBericht, acViewPreview
'Else
'    Tabelle1.MoveLast ' Letzten Datensatz suchen.
'    setzeFelder Forms!HF_AuftrAnkErz, Tabelle1, DRUCK_BEREICH_ANGEBOT
'    erhoeheNummer Tabelle1, "AngebotNr"
'
'
'    druckeBericht "Angebot", strBericht, Me!AnzahlDrucke
'
'
'    Forms!HF_AuftrAnkErz!AngebotKz = True   ' Setzen der Druckkennziffer
'    Forms!HF_AuftrAnkErz.Refresh
'
'    ' Logbucheintrag
'    If DLookup("[Benutzerverwaltung]", "HT_SysParameter") = True Then
'        Call SetzeBenutzerLog("Drucke Angebot o. Summen Ankauf Erz.-Preis! Auftrag Nr: " & CStr(Forms!HF_AuftrAnkErz!AuftragNr) & ", Angebot Nr: " & CStr(Forms!HF_AuftrAnkErz!AngebotNr))
'    End If
'End If
  
Exit_DruckAngeboSum_Click:
    On Error Resume Next
'    Tabelle1.Close
'    DB1.Close
    Exit Sub

Err_DruckAngeboSum_Click:
    If err = 2501 Then
        Me!Schließen.SetFocus
    Else
        MsgBox Error$
    End If
    Resume Exit_DruckAngeboSum_Click

End Sub

Die drei Zeilen Code oder so sind das was nach dem Überarbeiten bleibt.
Ein paar Dinge dürften Programmierer in neueren Sprachen durchaus verunsichern, das sind die Label zu denen man springt. Ja, das kann man inzwischen anders lösen und nein, es ist recht typisch für VBA. Man sieht, nur für die Fehlerbehandlung kommen “locker” 5- 10 Zeilen Code zu jeder Funktion. Insgesamt sind es über 500 000 Zeilen an Code und viele Methoden gehen über mehrere Seiten.

Wie man sehen kann, gibt es keine Trennung der Bereiche. In einer Methode/Funktion gibt es Zugriffe auf die Datenbank, Manipulation der UI und Interaktion mit dem Benutzer. Ich befürchte, daß ist immer noch Stand der Dinge bei sehr vielen Access Programmen. Als wirklich ernsthaftes Problem sehe ich das Fehlen von brauchbaren Testmöglichkeiten an. AFAIK gibt es AccUnit zum Testen, das bekomme ich hier nicht installiert und ich bin derzeit immer noch zu faul um mich dahinterzuklemmen.

Die Klasse, in der ich es auslagerte, sieht der Code nun so aus:

Public Sub doOutput()
    Dim bAdjustNumber As Boolean
    Dim objControl As CControlNummernKreise
    
    

On Error GoTo HandleError
    Dim iAuswahl As Integer
    If outputMedia = outMediumPreview Then
        outputPreview
        GoTo HandleExit
    End If
    
    
    bAdjustNumber = True
    If NZ(actForm(textFieldNumberName), "") <> "" Then
        iAuswahl = askToReprintIfPrintedBefore()
        If iAuswahl = vbNo Then
            GoTo HandleExit
        Else
            bAdjustNumber = False
        End If
    End If
   
    Set objControl = actualizeFormDataBeforePrint()
         actualizeNumberCircle objControl, bAdjustNumber
         Set objControl = Nothing
        
         actionBeforePrint
             If outputMedia = outMediumPrinter Then
                 outputPrinter
             Else
                 outputEmail
             End If
         actionAfterPrint
    actualizeFormDataAfterPrint
    writeLog

    
    
HandleExit:
    On Error Resume Next
    Exit Sub


HandleError:
    If err.Number <> 0 Then
       MsgBox "Error " & err.Number & " (" & err.Description & ") in procedure doOutput."
    End If
    GoTo HandleExit


End Sub

Sicher noch reichlich zu kritisieren, aber ich denke, eine Verbesserung ist zu erkennen.
Der Witz an dieser einen Methode ist: Die kann ich so an ungefähr 8 Stellen so benutzen und muß da nichts mehr anfassen. Ich muß die Daten entsprechend vorbelegen

Case FRM_AUFTRAG_ANKAUF_ERZ
If sKundeNr = "143" Then
objResult.reportName = "BR_DruckAngebotAnkErz_Bel"
Else
objResult.reportName = "BR_DruckAngebotAnkErz"
End If            
objResult.logEntry = "Druck Angebot Ankauf Erz. m. Summen Auftrag Nr: " & NZ(objResult.orderNo, 0) & _
                                 ", Angebot Nr: " & NZ(objResult.actForm(objResult.textFieldNumberName), "")

            objResult.lblFuerKLNr = "Lieferant Nr:"

            Set objParameterAngebot = parDruckAngebot_WH()
            objParameterAngebot.mainTableName = "HT_AuftragAnkaufErz"
            objParameterAngebot.subTableName = "UT_AuftragAnkaufErz_Positionen"
            objParameterAngebot.fieldNameKLNo = "LieferantNr"

gen, aber die Logik dieses Teils geht nicht verloren. Ich denke, daß ist einer meiner Lieblingstechniken, einen Algorithmus austüfteln und dann möglichst nicht mehr anfassen zu müssen. Sollte was schiefgehen, dann habe ich hier eine zentrale Anlaufstelle, um es zu korrigieren und muß nicht die Korrektur 8 mal wiederholen.
Leider macht es einem VBA unglaublich schwer Code wiederzuverwenden, denn obwohl das hier eine Klasse ist, kann ich nicht einfach davon ableiten und nur die Sachen umschreiben, die ich brauche. Ja, es gibt Interfaces, aber die muß man dann von a-z in allen Klassen implementieren, das ist einfach schlecht und dürfte Leuten, die mit Java, Python, Ruby oder so gut wie jeder anderen OO-Sprache programmieren, die Tränen in die Augen treiben. Die Dinge sind aber nun mal, wie sie sind. Modularer bekäme man es nur hin, wenn man jede Methode in ein eigenes Interface auslagerte und dann die Interfaces implementierte, die einzige zugängliche Methode wäre dann z.B. für

 If outputMedia = outMediumPreview Then
        outputPreview
        GoTo HandleExit
    End If

outputTo aber man müsste halt all notwendigen Daten in diesem Interface hinterlegen und bräuchte zumindest ein paar Parameter oder ein Parameter Objekt.

Dann könnte man ein Interface haben für eine Klasse mit dem Namen OutputPreview
und den Wert so setzen

objIf as OutputInterface
if Test_If_Angebot then
  set objIf = new OutputInterfaceAngebot 
...
:..

und riefe es so auf
objIf.outputTo

Es geht leider nicht anders und dann bekommt man eben auch so etwas:

Case FRM_AUFTRAG_ANKAUF_ERZ
            If sKundeNr = "143" Then
                    objResult.reportName = "BR_DruckAngebotAnkErz_Bel"
                Else
                    objResult.reportName = "BR_DruckAngebotAnkErz"
                End If
               
                objResult.logEntry = "Druck Angebot Ankauf Erz. m. Summen Auftrag Nr: " & NZ(objResult.orderNo, 0) & _
                                     ", Angebot Nr: " & NZ(objResult.actForm(objResult.textFieldNumberName), "")
                                     
                objResult.lblFuerKLNr = "Lieferant Nr:"
            
                Set objParameterAngebot = parDruckAngebot_WH()
                objParameterAngebot.mainTableName = "HT_AuftragAnkaufErz"
                objParameterAngebot.subTableName = "UT_AuftragAnkaufErz_Positionen"
                objParameterAngebot.fieldNameKLNo = "LieferantNr"

Hier muß ich einfach festhalten MS hat sich einiges bei VBA gedacht aber a) nicht zu Ende und b) mangelhaft an bestimmten Stellen.

Es gibt durchaus eine Menge Fans von Access. Vom Standpunkt von guter OO-Programmierung ist VBA ein Sanierungsfall. Dabei könnte es so einfach sein. VBA als Mitglied der .NET Sprachfamilie und gut wär’s. Ich verstehe nicht, warum es MS nicht macht. Die Unterstützung durch Werkzeuge ist so in den 80 er Jahren des letzten Jahrtausends hängen geblieben. Ja, man kann in VBA auch riesige Programme schreiben, es tut aber weh ….

Wo MS-Access (mit VBA) vollkommen mangelhaft ist
1) Keine integrierten Tests
2) An Objekten orientiert, aber nicht OO-mässig zu programmieren
3) Grauenhafte Benutzung in einem RCS (hier benutze ich TortoiseGit) , Problem: Groß/Kleinschreibung geht munter durcheinander, es gibt IDS, die sich einfach ändern
4) Keine Unterstützung für die Trennung von UI, Daten und dem Programm. Wie geschrieben, der Code ist heute noch typisch für Ms Access. Man bekommt halt schnell was zusammen …
5) der SQL Editor ist eine Zumutung!

Wo es glänzt
1) mal eben was zusammenstoppeln ist schnell erledigt
2) der Direktbereich, man kann während der Laufzeit anhalten und sich alles anschauen, kein rekompilieren nötig
3) preisgünstig

Es ist klar MS will, daß wir MS-SQL mit C# benutzen, es ist nicht überzeugend, daß dies für kleine Betriebe eine bessere Lösung als eine MS-Access basierte wäre.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert