Zusammenspiel von Klassen

Diskussionen über Programmierstil (Code-Formatierung, Namenskonventionen, ...)

Zusammenspiel von Klassen

Beitragvon Josef Pötzl » Fr 26. Mär 2010, 11:10

Hallo!

In diesem Thread möchte ich einmal versuchen herauszufinden, wie man abhängige Klassen am besten miteinander verbindet.

Gegeben sei folgende Grundstruktur:
  • Hauptklasse
    • DetailklasseABC
    • DetailklasseXYZ
"Detailklasse" soll dabei für eine Klasse stehen, die auf jeden Fall von der Hauptklasse verwendet wird.
Beispiel:
Hauptklasse = Access.Application
Detailklasse = CurrentProject

Beispiel für Verwendung:
Code: Alles auswählen
dim HK as Hauptklasse
HK.ABC.Machwas
 


Das könnte ich in der Hauptklasse so gestalten
Code: Alles auswählen
private m_ABC as DetailklasseABC

Public Property get ABC() As DetailklasseABC
   ...
   set ABC = m_ABC
End Property


Nachteil dieser Variante: man macht die Hauptklasse von der Detailklasse abhängig. Wenn ich nun die Detailklasse ABC durch eine andere Klasse "ABCerweitert" ersetzten will, ist das nicht mehr ganz so einfach. Ich muss dann auf jeden Fall in ABCerweitert als Schnittstelle die Klasse DetailklasseABC einbauen. ... und da beginnt dann das Problem bei Access: je mehr Schnittstellen eingebaut werden, desto höher ist die Wahrscheinlichkeit, dass der VBA-Binär-Code defekt wird - ohne regelmäßiges Decompile kracht es dann laufend während der Programmierung (zumindest nach meiner bisherigen Erfahrung).


Ein anderer Ansatz:
Nicht die Hauptklasse von der Detailklasse (bzw. einer Schnittstelle dieser Detailklassendefinition) abhängig machen, sondern die Detailklassen von der Hauptklasse abhängig machen.
Diesen Weg wählte ich für die Erweiterungen zur Klasse "ApplicationHandler". (Bei Erweiterungen ist aber die Ausgangssituation eine andere.)

Prinzip:
Code in der Hauptklasse:
Code: Alles auswählen
Public Event DetailklasseAbcWoBistDu(ByRef Detailklasse as object)

Public Property get ABC() As Object
   dim DetailklassenRef as Object
   RaiseEvent DetailklasseAbcWoBistDu(DetailklassenRef)
   set ABC = DetailklassenRef
End Property


Code in der Detailklasse:
Code: Alles auswählen
private WithEvents m_Hauptklasse as Hauptklasse

Private Sub m_Hauptklasse_DetailklassebcWoBistDu(ByRef Detailklasse as object)
    set Detailklasse = Me
end Sub


Vorteil: Damit kann ich die Detailklassen nach Belieben durch andere ersetzen, die auf das gleiche Ereignis reagieren.
Riesiger Nachteil: Kein Early Binding und somit keine Absicherung durch den Compiler und natürlich auch kein IntelliSense. => Fehleranfällig!


Ein anderer Weg wäre mit Hilfe des Import-Assistenten:
Ja nach Bedarf kopiert man sich das gewünschte Code-Modul aus der Code-Bibliothek, dass dann in der Anwendung die Detailklasse darstellt.
Vorteil: Code ist eindeutig und man kann im Prinzip auch von der Hauptklasse in die Detailklasse verweisen, da sich für die Hauptklasse nichts ändert, da ja beim Import der Code in der Detailklasse geändert wird, die Detailklasse aber immer den gleichen Namen behält, auch wenn der Code der erweiterten Detailklasse entspricht.
Nachteil: man kann nur eine einzige Erweiterung einsetzen und nicht während der Laufzeit entscheiden, welche Klasse an die Schnittstelle übergeben werden soll.


Wie seht ihr das?

mfg
Josef

PS: Ich hoffe obiges ist einigermaßen verständlich beschrieben. ;)

Aus Programmiersicht ist meiner Meinung nach die Variante mit der Schnittstelle die sauberste Lösung. Nur was hilft mir eine "saubere Lösung", wenn mir Access dann dauernd abschmiert. ;) Bei der Anwendungserstellung dreh ich bei meinen Anwendungen daher die Implements-Anweisung immer ab und stelle auf Late Binding um, sobald die Klasse fertig programmiert ist.
Und wenn ich später nur eine einzige Detailklasse benötige, neige ich mittlerweile dazu, das über den Import-Assistenten zu regeln.
Josef Pötzl
Moderator
 
Beiträge: 764
Registriert: Mo 30. Nov 2009, 10:08
Wohnort: Klagenfurt
Accessversion: 2010 (2013)

Re: Zusammenspiel von Klassen

Beitragvon Sten Schmidt » Fr 26. Mär 2010, 13:18

Servus,

Fehleranfälligkeit? => No Go!
IntelliSense? => Is halt schon ne schöne Sache, spart Zeit!
VBA-Binär-Code defekt? => No Go!

Und wenn ich später nur eine einzige Detailklasse benötige, neige ich mittlerweile dazu, das über den Import-Assistenten zu regeln.


Und die nutzt dann ausschließlich Late Binding?

In der Summe klingt das alles nach faulen Kompromissen, und wäre das hier kein Access-Projekt würde ich sagen man müsste die Programmiersprache wechseln.

Ich stimme dennoch für Variante A, da es die "natürlichste" ist. Und eine Hauptklasse ist "natürlich" von ihren Detailklassen abhängig, da sie auch nur dann ihre volle Funktionalität entfalten kann (Annahme: Sie wurde für einen bestimmten Zweck entworfen). Will man der Hauptklasse weitere, ähnlich Aufgaben übertragen, muss man von dieser eben eine weitere Hauptklasse ableiten.
Sten Schmidt
Entwickler
 
Beiträge: 140
Registriert: Do 18. Mär 2010, 22:24
Accessversion: 2007, 2010
Access-Erfahrung: Experte

Re: Zusammenspiel von Klassen

Beitragvon Josef Pötzl » Fr 26. Mär 2010, 14:02

Und die nutzt dann ausschließlich Late Binding?

Nein, das wäre dann Early binding, da ja der Klassenname erhalten bleibt. Das würde dann so wie bei den _config_Application-Modulen ablaufen. Davon gibt es mehrere in der Code-Bibliothek, aber nur eine in der Anwendung. Das funktioniert natürlich nur dann, wenn man in der Anwendung nur eine einzige Variante der jeweiligen Detailklasse benötigt und nicht während der Laufzeit der Hauptklasse über die Zuweisung von Instanzen von Detailklassen-Varianten unterschiedliche Funktionalitäten verpassen will.

Will man der Hauptklasse weitere, ähnlich Aufgaben übertragen, muss man von dieser eben eine weitere Hauptklasse ableiten.

Was unter VBA ein besonderer Spaß ist, weil man so gut vererben kann. ;)
Josef Pötzl
Moderator
 
Beiträge: 764
Registriert: Mo 30. Nov 2009, 10:08
Wohnort: Klagenfurt
Accessversion: 2010 (2013)

Erwischt

Beitragvon Sten Schmidt » Fr 26. Mär 2010, 14:56

Was unter VBA ein besonderer Spaß ist, weil man so gut vererben kann.


OK OK, ich nehme alles zurück und behaupte das Gegenteil. ;)
Sten Schmidt
Entwickler
 
Beiträge: 140
Registriert: Do 18. Mär 2010, 22:24
Accessversion: 2007, 2010
Access-Erfahrung: Experte

Re: Zusammenspiel von Klassen

Beitragvon raist10 » Mo 29. Mär 2010, 17:20

@ Josef P.

Ich weiss ja, dass ich mit Sicherheit nicht mit Deinem Fachwissen mithalten kann, aber mir wiederstrebt die Thematik Klassen zu programmieren die zum funktionieren Zugriff auf andere Klassen benötigen, die dann vllt wiederum Zugriff auf ein ganz andere Klasse benötigen. Wird damit nicht an sich das Thema Klasse adabsurdum geführt?

Irgendwann und irgendwo habe ich mal gelernt das eine Klasse komplett unabhängig zu funktionieren hat damit es eine gute Klasse ist. Also immer Late Binding anstatt Early Binding, soweit als möglich absolut unabhängig von Verweisen und absolut unabhängig von anderen Prozeduren/Klassen, benötigte Deklarationen (API z.B.) führt die Klasse selbst in sich, Zugriff auf Public Vars oder Prozeduren eines Projektes sind absolut zu meiden.

Dementsprechend baue ich auch meine Klassen und wenn dann mal eine Funktion benötigt wird, die sich aus der Funktionalität von Prozeduren verschiedener Klassen zusammen setzt, kommt die in ein eigenes Modul rein.

Oder sehe ich da mal wieder nur was falsch?

Gruß

Rainer
raist10
 
Beiträge: 7
Registriert: Mo 29. Mär 2010, 17:04
Wohnort: Seeham
Accessversion: Access 2007
Access-Erfahrung: Fortgeschritten

Re: Zusammenspiel von Klassen

Beitragvon Josef Pötzl » Mo 29. Mär 2010, 17:46

aber mir wiederstrebt die Thematik Klassen zu programmieren die zum funktionieren Zugriff auf andere Klassen benötigen

Da gebe ich dir im Prinzip auch Recht, dass Klassen so gut wie möglich vollkommen eigenständig funktionieren sollten. Allerdings sehe ich kein Problem, dass in Klassen andere Klassen (idealerweise über festgelegte Schnittstellen) verwendet werden. Wenn du das nicht willst, wie willst du dann aus einer Formular-Klasse eine andere Klasse verwenden? ;)

Ein typisches Beispiel: Recordset -> Fields -> Field
Da spielen 3 Klassen (bzw. Schnittstellen) zusammen. Wenn du nun in der Anwendung eine ähnliche Struktur aufbauen willst, müssen deine Klassen auch miteinander kommunizieren und somit irgendwie verbunden sein.
Ein ähnliches Beispiel mit Datenzugriffsklassen stellte ich vor kurzem in den draft-Zweig: Kapselung von Datenzugriffen
Bei diesen Klassen gibt es diese Struktur: DbConnection -> DaoHandler; DbConnection -> AdodbHandler; DbConnection -> OdbcHandler
DaoHandler, AdodbHandler und OdbcHandler sind eigenständige Klassen. Die Klasse DbConnection dient dazu, dass man in der Anwendung nicht mehrmals die Verbindungsdaten einstellen muss und für die Verwendung der Zugriffsklassen einen gemeinsamen Einstiegspunkt hat. In diesem Fall ist DbConnection von den anderen 3 Klassen abhängig, was auch nicht weiter schlimm ist, da sie ja als Aufgabe hat, diese 3 Klassen zu vereinen. Ein Nachteil ist, dass man alle 3 Klassen in die Anwendung mitnehmen muss, auch wenn man z. B. später nur DAO und ODBC nutzen will. Würde man in DbConnection den Bezug zu den anderen 3 Klassen nur als Object deklarieren, könnte man zwar die anderen Klassen auch löschen, ohne dass der Compiler meckert. Greift man dann doch auf eine Eigenschaft zu, die eine dieser nicht vorhandenen Klassen nutzt, kracht es aber.

Also immer Late Binding anstatt Early Binding

.. aber nur bei fremden Bibliotheken (wie Office-Verweise), die dazu führen können, dass ein Verweisproblem auftritt. Ich würde kein Late binding einsetzen, wenn Klassen der aktuellen Anwendung verwendet werden sollen.
Latebinding hat immer den schlechten Beigeschmack, dass Code entsteht, bei dem man erst bei der Ausführung die Tippfehler entdeckt.
Anm.: Aus diesem Grund programmiere ich z. B. auch bei Excel-Zugriffen immer zuerst mittels Early binding und stelle erst vor Auslieferung der Anwendung über Compiler-Anweisungen auf Late binding um.

Dementsprechend baue ich auch meine Klassen und wenn dann mal eine Funktion benötigt wird, die sich aus der Funktionalität von Prozeduren verschiedener Klassen zusammen setzt, kommt die in ein eigenes Modul rein.

.. wobei dieses "Modul" auch eine weitere Klasse sein könnte, oder?


Eigentlich existiert diese Abhängigkeitsproblematik nur wegen der wiederverwendbaren Klassen. Würde ich Klassen nur für eine spezielle Anwendung erstellen, würde die Klasse genau auf diese zugeschnitten sein.
In meinen eigenen Anwendungen helfe ich mir normalerweise mit einer COM-dll, in der ich die allgemeinen (wiederverwendbaren) Klassen einbette. Damit ist es für die Access-Anwendung egal, ob dort ein paar unbenötigte Klassen mitkommen.
Bei der access-codelib möchte ich allerdings versuchen, dass wir eine relativ flexibel einsetzbare Codemodulsammlung bekommen, damit man je nach Bedarf Teile davon in seine Anwendung übernehmen kann. Und da wird es bestimmt die eine oder andere Abhängigkeit zu Klassen geben undbestimmt auch hin und wieder den Wunsch eine Abhängigkeit mit einer anderen Klasse zu bedienen. Aus diesem Grund startete ich diesen Thread. ;)

Vor allem sollte man die Code-Module aber auch nicht jedes Mal nachbearbeiten müssen. ... aus diesem Grund nutzte ich z. B. schon länger die Variante mit dem "_config_Application"-Modul, in dem die Anpassung für die Anwendung stattfindet.

mfg
Josef

PS:
vielleicht noch ein Hinweis zu "Abhängigkeit": damit meinte ich in diesem Text, dass die Hauptklasse die Detailklassen benötigt, um funktionieren zu können. Aus Programmsicht sollten eigentlich die Detailklassen von der Schnittstelle der Hauptklasse abhängig sein. Soll heißen: die Hauptklasse gibt vor, welche Eigenschaften und Methoden sie von den Detailklassen erwartet und die Detailklassen erfüllen dann diese Schnittstelle. Damit könnten im Prinzip die Detailklassen auch durch andere ersetzt werden, die die Schnittstellendefinition erfüllen.
Josef Pötzl
Moderator
 
Beiträge: 764
Registriert: Mo 30. Nov 2009, 10:08
Wohnort: Klagenfurt
Accessversion: 2010 (2013)

Re: Zusammenspiel von Klassen

Beitragvon raist10 » Mo 29. Mär 2010, 19:18

Josef Pötzl hat geschrieben: ... Allerdings sehe ich kein Problem, dass in Klassen andere Klassen (idealerweise über Schnittstellen gekapselt) verwendet werden.


Lass mich kurz konkretisieren ... was definierst Du als Schnittstelle? Ein gefeurtes Klassen-Ereignis und/oder Get-/Set-/Let-Aufrufe?

Wie gesagt, Problem ... ob es ein Problem ist ist sicherlich Definition. Aber wie gesagt im Sinne der eigenständigen und völlig unabhängigen Verwendung einer Klasse läuft es dem Grundgedanken zu wieder.

Wenn du das nicht willst, wie willst du dann aus einer Formular-Klasse eine andere Klasse verwenden? ;)


Gar nicht? ^^ Es besteht grundsätzlich keine Notwendigkeit aus der Klasse Formular auf eine andere Klasse zu zu greifen … mit Ausnahme von Event-Handler. Wobei aber in meinen Augen die Formular-Klasse eine "besondere" Klasse ist. Sie wird i.d.R. ja immer in Projektabhängigkeit programmiert und von ihr wird auch nicht erwartet das man sie problemlos in eine anderes Projekt transportieren kann.

Ein typisches Beispiel: Recordset -> Fields -> Field
…. Ein Nachteil ist, dass man alle 3 Klassen in die Anwendung mitnehmen muss, auch wenn man z. B. später nur DAO und ODBC nutzen will. Würde man in DbConnection den Bezug zu den anderen 3 Klassen nur als Object deklarieren, könnte man zwar die anderen Klassen auch löschen, ohne dass der Compiler meckert. Greift man dann doch auf eine Eigenschaft zu, die eine dieser nicht vorhandenen Klassen nutzt, kracht es aber.


Ich kenne das Beispiel jetzt im Konkreten nicht, daher nur ein paar Grundgedanken zu Deiner Schilderung.

Würde ich eine Klasse für einen DAO-/ADO-Zugriff programmieren, würde jede Klasse einen eigenen Verbindungsaufbau für sich selbst spendiert bekommen, allerdings verbunden mit der Optionalen Möglichkeit bei Ansprache der Klasse eine bereits bestehende DbConnection mit zu geben. Wäre bei Zugriff auf die Klasse zwar konstent ein Parameter mehr zu übermitteln, aber würde alle Klassen so gestalten das sie völlig unabhängig von den anderen Klassen laufen könnten. Möglicherweise würde ich die DAO-/ADO-Zugriffe die die Klassen in sich selbst mitführen auf CurrentProject/CurrentDB beschränken und wer dann auf eine andere Verbindung zu greifen will, benötigt die Klasse DBConnection und gibt die dort erstellte Connection als zusätzlichen Parameter an den DAO-/ADO-Zugriff mit.

Würde zwar pro Klasse ein wenig mehr Aufwand bedeuten, würde aber die Klassen wesentlich flexibeler machen und vor allem kein Problem mehr wenn sich etwas an einer Klasse ändert … solange die Art der Übergabe stimmt juckt es keine der Klassen wenn sich eine andere Klasse geändert hat.


.. aber nur bei fremden Bibliotheken (wie Office-Verweise), die dazu führen können, dass ein Verweisproblem auftritt. Ich würde kein Late binding einsetzen, wenn Klassen der aktuellen Anwendung verwendet werden sollen.


Da sind wir wieder beim Thema … eine Klasse sollte unabhängig vom Projekt funktionieren. Also weiss man nie ob und wenn ja, welche Verweise vorhanden sind. In dem Augenblick wo man eine Klasse auf die bereits verwendeten Bausteine eines Projektes einstellen/umstellen muss gibt es zwangsläufig Probleme bei der Portation in andere Anwendungen.

Im Extrem habe ich sogar gelernt, dass eine Klasse die einen Verweis benötigt sich auch selber darum zu kümmern hat das der benötigte Verweise vorhanden ist und ihn wenn nicht vorhanden, dann auch beim Initiliaze selbst setzt. Daher auch die strikte Beachtung von Late Binding. ;)

Latebinding hat immer den schlechten Beigeschmack, dass Code entsteht, bei dem man erst bei der Ausführung die Tippfehler entdeckt.
Anm.: Aus diesem Grund programmiere ich z. B. auch bei Excel-Zugriffen immer zuerst mittels Early binding und stelle erst vor Auslieferung der Anwendung über Compiler-Anweisungen auf Late binding um.


Richtig, erst mit Early Binding und damit auch IntelliSense programmieren und dann wenn alles getestet ist und funzt, dann auf Late Binding umstellen … das natürlich auch durchtesten, zumindest bei mir nötig. ^^

.. wobei dieses "Modul" auch eine weitere Klasse sein könnte, oder?


Könnte … ja. Muss … definitiv Nein! Wofür auch? Was macht das für einen Unterschied, ausser das mir im restlichen Projekt der Zugriff durch Referenzierung der Klasse erschwert/kompliziert wird.

Eigentlich existiert diese Abhängigkeitsproblematik nur wegen der wiederverwendbaren Klassen. Würde ich Klassen nur für eine spezielle Anwendung erstellen, würde die Klasse genau auf diese zugeschnitten sein.


Wofür erstellt man eigentlich eine Klasse die man in keinem anderen Projekt mehr verwenden kann? Da will mir der Sinn solcher Klassen nicht einleuchten, eher unsinnig da man für die Verwendung der Klasse im Projekt ja immer referenzieren muss. Es gibt keinen Vorteil … ausser man benötigt die Feuerung von Klassen-Ereignissen.

In meinen eigenen Anwendungen helfe ich mir normalerweise mit einer COM-dll, in der ich die allgemeinen (wiederverwendbaren) Klassen einbette. Damit ist es für die Access-Anwendung egal, ob dort ein paar unbenötigte Klassen mitkommen.


Diskussionslos wohl die beste Variante um generell Prozeduren projektunabhängig portabel zu machen.

Bei der access-codelib möchte ich allerdings versuchen, dass wir eine relativ flexibel einsetzbare Codemodulsammlung bekommen, damit man je nach Bedarf Teile davon in seine Anwendung übernehmen kann. Und da wird es bestimmt die eine oder andere Abhängigkeit zu Klassen geben und vor allem auch den Wunsche eine Abhängigkeit mit einer anderen Klasse zu bedienen. Aus diesem Grund startete ich diesen Thread. ;)


Das hatte ich mir gedacht das das der Grund ist. ^^

Aber für genau so eine Code-Lib bietet es sich an die Grundregeln einer Klasse einzuhalten. Nämlich völlige Unabhängigkeit von anderen Klassen und damit absolute Portabilität in jedes x-beliebige Projekt ohne Vorraussetzung.

Was spricht dagegen Codes die von anderen Klassen abhängig sind, als Code-Module anstatt Code-Klassen einzustellen?

Vor allem sollte man die Code-Module auch nicht jedes Mal nachbearbeiten müssen. ... aus diesem Grund nutzte ich schon länger die Variante mit dem "_config_Application"-Modul, in dem die Anpassung für die Anwendung stattfindet.


Was muss man bei einer Klasse die nach dem Prinzip der Unabhängigkeit programmiert wurde anpassen um Sie in einem Projekt einzusetzen?

Wenn Du hier also Grundspielregeln für eingestellte Klassen festlegen willst, dann wäre wirklich die Frage ob es nicht sinniger wäre bei Einstellung einer Klasse als Code-Lib die Unabhängigkeit der Klasse von irgendwelchen weiteren Projektbausteinen als Basis festzulegen.

Gruß

Rainer

P.S.: Stell mal den Forums-Schnell-Editor um … das rumgehopse wenn man mal mehr schreiben will nervt. ^^
raist10
 
Beiträge: 7
Registriert: Mo 29. Mär 2010, 17:04
Wohnort: Seeham
Accessversion: Access 2007
Access-Erfahrung: Fortgeschritten

Re: Zusammenspiel von Klassen

Beitragvon Josef Pötzl » Mo 29. Mär 2010, 21:01

raist10 hat geschrieben:Lass mich kurz konkretisieren ... was definierst Du als Schnittstelle?

Eine festgeschriebene Definition wie zwei Elemente (z. B. Klassen) miteinander kommunizieren.

Würde ich eine Klasse für einen DAO-/ADO-Zugriff programmieren, würde jede Klasse einen eigenen Verbindungsaufbau für sich selbst spendiert bekommen, allerdings verbunden mit der Optionalen Möglichkeit bei Ansprache der Klasse eine bereits bestehende DbConnection mit zu geben.

Genau. Außerdem besitzen sie auch noch ein paar Ereignisse, mit denen sie notfalls fehlende Werte anfordern können bzw. anderen Steuerungsklassen die Möglichkeit geben einzugreifen.

Möglicherweise würde ich die DAO-/ADO-Zugriffe die die Klassen in sich selbst mitführen auf CurrentProject/CurrentDB beschränken und wer dann auf eine andere Verbindung zu greifen will, benötigt die Klasse DBConnection und gibt die dort erstellte Connection als zusätzlichen Parameter an den DAO-/ADO-Zugriff mit.

Die Verwendung von CurrentProject und CurrentDB sollte meiner Ansicht nach bei solchen Klassen tabu sein. Die passende ADODB.Connection bzw. DAO.Database sollten sie von außen erhalten, aber nicht selbstständig die Annahme treffen, dass mit den Standard-Verbindungen gearbeitet werden soll.

Würde zwar pro Klasse ein wenig mehr Aufwand bedeuten, würde aber die Klassen wesentlich flexibeler machen und vor allem kein Problem mehr wenn sich etwas an einer Klasse ändert … solange die Art der Übergabe stimmt juckt es keine der Klassen wenn sich eine andere Klasse geändert hat.

... zumindest ist es nicht deren Aufgabe auf Verbindungsänderungen zu horchen. Das ist eine Bringschuld der Verbindungssteuerung, diesen Klassen die passende Verbindung zu liefern.

Aber ich glaub, du musst dir die Klassen doch mal ansehen, bevor wir darüber (am besten im anderen Thread) weiter diskutieren. :) Bezüglich Flexibilität fehlt denen vermutlich nicht besonders viel.

Da sind wir wieder beim Thema … eine Klasse sollte unabhängig vom Projekt funktionieren.

Wenn jede Klasse unabhängig vom Projekt funktionieren muss, dann kannst du somit alle .net-Projekte wegwerfen, da es dort nur Klassen zur Gestaltung des Projekt-Codes gibt. :D
Oder: Eine Datenkapselung einer Tabelle in einer Klasse funktioniert auch ohne Datenbank und Tabelle?
.. andererseits funktionieren Klassen natürlich auch unabhängig vom Projekt, wenn in einem anderen Projekt die gleichen Voraussetzungen gegeben sind.
Die Frage ist für mich eher: muss eine Klasse unabhängig von anderen Klassen funktionieren? ... Meiner Ansicht nach muss sie das nicht, es ist aber durchaus gut, wenn sie eigenständig arbeiten kann.

Ich würde eine Zugriffsbeschränkung eher so formulieren:
Eine Detailklasse hat nichts in den Eigenschaften oder Methoden der übergeordneten Klasse zu suchen, sondern darf nur über Ereignisse von sich aus der übergeordneten Klasse etwas mitteilen. Im Standardfall hat die Detailklasse nur auf Anfragen (Methoden-/Eigenschaftsaufrufe) von der übergeordneten Klasse zu reagieren. Damit entsteht keine gegenseitige Abhängigkeit.

Möglicherweise reden wir aber auch aneinander vorbei. Ich glaube, wir beide verstehen unter eine Klasse unterschiedliche Dinge. ;)
Sprichst du eventuell nur dann von Klassen, wenn sie eine kleine in sich geschlossene Aufgabenstellung kapseln. Ich erstelle eine Klasse durchaus auch für komplexere Aufgabenstellungen, für deren Lösung sich diese Klasse weiterer Klassen bedient. Eine Klasse ist für mich einfach eine Blackbox zur Lösung einer Aufgabestellung, egal wie komplex diese ist.

Beispiel:
Klasse Auto:
Methoden
- schneller fahren
- langsamer werden
- ...

Diese Klasse bedient sich intern bei den Klassen: Motormanagement, Lenkung, ...

Für die Nutzung der Klasse Auto ist es mir aber vollkommen egal, wie die Klasse das macht. Ob sie das über Ereignisse, Callback-Aufrufe, Eigenschaftsübergaben usw. macht, hat mich als Nutzer der übergeordneten Klasse gar nicht zu interessieren, da für diese Dinge die Entwickler dieser Klasse dafür zuständig sind. Ich habe nur die Methoden und Eigenschaften, die über die Klasse "Auto" zur Verfügung gestellt werden, zu nutzen. Ich werden also nicht irgendwo ein Modul erstellen und Prozeduren programmieren, die die einzelnen Autoteile zuerst zu einem Auto zusammenbaut, um erst dann damit die Methoden nutzen zu können.
Ich würde höchsten die Klasse Auto in eine Klasse Ferrari ausbauen (Vererbung), um dort dann Feintuning zu betreiben und der Ferrari-Klasse einen anderen Motor geben, der dann über die ebenso ausgetauschte Motorsteuerung gesteuert wird. Die Schnittstelle "Fuß" müsste ich aber vermutlich auch beim Ferrari erfüllen. :D


Im Extrem habe ich sogar gelernt, dass eine Klasse die einen Verweis benötigt sich auch selber darum zu kümmern hat das der benötigte Verweise vorhanden ist und ihn wenn nicht vorhanden, dann auch beim Initiliaze selbst setzt.

So etwas halte ich zur Laufzeit für falsch. Außerdem: zeig mir bitte, wie du in einer mde einen Verweis hinzufügst. ;)

Ich baute das dafür in den Import-Wizard ein, der Verweise in den Access-Anwendungsentwurf einfügen kann, wenn die Bibliothek im Codelib-Block angegeben wurde.

Daher auch die strikte Beachtung von Late Binding. ;)

Late binding als allgemein gültige Vorschrift ist meiner Ansicht nach fahrlässig - vor allem bei Klassenmodulen, die in der Access-Anwendung gespeichert sind - da man damit jede Kontrolle durch den Compiler deaktiviert. Late binding sollte meiner Ansicht nach nur eine Notlösung sein.
... Oder deklarierst du auch alle Variablen generell als Variant und nicht als String oder Long usw.?

Eigentlich existiert diese Abhängigkeitsproblematik nur wegen der wiederverwendbaren Klassen. Würde ich Klassen nur für eine spezielle Anwendung erstellen, würde die Klasse genau auf diese zugeschnitten sein.


Wofür erstellt man eigentlich eine Klasse die man in keinem anderen Projekt mehr verwenden kann?

Damit man auch bei projektspezifischen Elementen die Vorteile von Klassen nutzen kann?
Und wer sagt dir eigentlich, dass du diese Klassen nicht später auch ein einem anderen Projekt verwendest, das auf dieses Projekt aufbaut?

Da will mir der Sinn solcher Klassen nicht einleuchten, eher unsinnig da man für die Verwendung der Klasse im Projekt ja immer referenzieren muss. Es gibt keinen Vorteil … ausser man benötigt die Feuerung von Klassen-Ereignissen.

Wie meinst du das? Meinst du damit das Instanzieren der Klasse? Ja klar ist das notwendig, aber das ist auch gleichzeitig der Vorteil zu einem allgemeinen Modul. Ich kann mehrere Instanzen parallel öffnen, ohne dass sie sich in die Quere komme. Würde ich nur Prozeduren aus allgemeinen Modulen verwenden, müsste ich in jeder Prozedur das mit einer nicht zu knappen Anzahl an Parametern abdecken.

Aber für genau so eine Code-Lib bietet es sich an die Grundregeln einer Klasse einzuhalten. Nämlich völlige Unabhängigkeit von anderen Klassen und damit absolute Portabilität in jedes x-beliebige Projekt ohne Vorraussetzung.

Das sehe ich nicht so. Ich will mir in meine Anwendung die Blackbox "Auto" holen. Wenn dann im Import andere Klassen mitgehen, ist mir das eigentlich egal. Hauptsache ich kann die Klasse Auto instanzieren und die Methode "Schneller fahren" aufrufen. Interessant wird es dann allerdings, wenn ich nun in meinem Projekt einzelne untergeordnete Klasse gegen Klassen mit projektspezifischer Erweiterung austauschen will. Sobald ich austausche, kann ich von den Weiterentwicklungen der Ursprungsklasse nicht mehr profitieren. (Anm.: Vererbung funktioniert ja leider unter VBA nicht.)
Die erzeugte Individualisierung kann allerdings doch so allgemein sein, dass sie auch in anderen Anwendung Verwendung findet. Also könnte ich sie wieder in die Code-Bibliothek stellen.

Problem: Doppelgleisigkeit bei der Entwicklung
Mögliche Lösung: die Ursprungsklasse mit ein paar weiteren Schnittstellen (z. B. in Form von Ereignissen) versehen, damit ich mich mit meiner Erweiterung dort einhängen kann.
Zur Sicherheit noch einmal: normalerweise wäre das ein Fall für Vererbung, der aber unter VBA nicht möglich ist. Daher kann man im Prinzip nur eine neue Klasse erstellen und die vorhandene Klasse darin als internes Objekt nutzen, um nicht jede Verbesserung auch noch in seiner eigenen Klasse einbauen zu müssen.
Nachteil: Die Anzahl der Klassenmodule in der Anwendung wird dadurch immer größer.

Was spricht dagegen Codes die von anderen Klassen abhängig sind, als Code-Module anstatt Code-Klassen einzustellen?

Grundsätzlich nichts ... aber was spricht dagegen, das auch als Klassenmodul einzustellen? ... meiner Ansicht nach genauso wenig. ;)
Beim Einsatz in Klassen habe ich den Vorteil, dass ich auf die Ereignisse der verwendeten Klassen reagieren kann und falls ich mehrere Kombinationen gleichzeitig nutzen will, ist das meiner Ansicht nach auch übersichtlicher und weniger fehleranfällig gestaltbar.
Allgemeine Module sind für mich eher Code-Sammler für Prozeduren, die nur auf Basis der übergebenen Parameter agieren. Irgendwelches Zwischenspeichern von Modul-Variablen, die dann in anderen Prozeduren wieder zum Einsatz kommen, vermeide ich. So etwas nutze ich nur in Klassen, da in Klassen der Zugriffsbereich eindeutig abgesteckt ist.

Vor allem sollte man die Code-Module auch nicht jedes Mal nachbearbeiten müssen. ... aus diesem Grund nutzte ich schon länger die Variante mit dem "_config_Application"-Modul, in dem die Anpassung für die Anwendung stattfindet.

Was muss man bei einer Klasse die nach dem Prinzip der Unabhängigkeit programmiert wurde anpassen um Sie in einem Projekt einzusetzen?

... nichts, wenn sie so programmiert wurde und genügend Einstellmöglichkeiten von außen aufweist.

Wenn Du hier also Grundspielregeln für eingestellte Klassen festlegen willst, dann wäre wirklich die Frage ob es nicht sinniger wäre bei Einstellung einer Klasse als Code-Lib die Unabhängigkeit der Klasse von irgendwelchen weiteren Projektbausteinen als Basis festzulegen.


oha .. bitte nicht falsch verstehen. Ich will hier keinesfalls irgendwelche Code-Konstrukte verbieten.
Und je unabhängiger eine Klasse ist, umso besser ist es natürlich. :)

P.S.: Stell mal den Forums-Schnell-Editor um … das rumgehopse wenn man mal mehr schreiben will nervt. ^^

Welches Rumgehopse meinst du? Soll ich ihn ausschalten? ;)

mfg
Josef

BTW: bevor jetzt viele befürchten, dass nur Klassen in die Code-Bibliothek kommen, deren Struktur keiner mehr versteht: die oben geschilderte Problemstellung wird bestimmt nur vereinzelt vorkommen, da ich aber schon länger über mögliche Konzepte für VBA und Einsatz von austauschbaren Klassen nachdenke, wollte ich das auch in Verbindung mit der Codelib betrachten.

Vielleicht noch ein Szenario, wie man das Auto vs. Ferrari-Problem mittels Import-Assistent lösen könnte.
Man baut in einer Access-Anwendung die Auto-Klasse direkt in eine Ferrari-Klasse um und behält den Klassenamen "Auto". Nur im Codelib-Block fügt man einen Dateinamen ein, der darauf hinweist, dass es sich um eine spezielle Auto-Klasse handelt (z. B. "Auto_Ferrari.cls").
Wenn man nun die so manipulierte Klasse "Auto" exportiert, entsteht die Datei Auto_Ferrari.cls. Beim Import dieser Datei in eine an der Anwendung heißt die Klasse aber wieder "Auto".
Damit gibt es auch bei den restlichen Code-Modulen kein Problem, da sich ja der Klassenname nicht geändert hat. Man kann somit die Klasse "Auto" von der Datei Auto.cls mit der Klasse "Auto" von der Datei Auto_Ferrari.cls ersetzen.
Nachteil: es funktioniert natürlich nur dann, wenn immer nur eine Auto-Klasse zum Einsatz kommen soll und man sieht am Klassennamen nicht, um welches Auto es sich handelt. ..
Größter Nachteil: Fehler im Code der Original-Klasse "Auto.cls" die in die Ferrari-Klasse ebenso noch existieren müssen in beiden Klassen korrigiert werden.
Vorteil: für Code, der einen anderen ersetzen soll, wäre damit eine Lösung geschaffen, die Early binding unterstützt, keine Schnittstellenklasse benötigt und keine weiteren Hilfsklassen benötigt.

Ein reales Beispiel, wo ich mir so einen Einsatz vorstellen könnte: Ein Klasse die SQL-Anweisungen abhängig vom verwendeten DBMS gestaltet. Da man in einer Access-Anwendung vermutlich eher nur auf ein DBMS ausrichtet, könnte man damit alle Code-Module die diese HIlfsklasse benötigen unabhänig vom DBMS gestalten.
(Natürlich wäre hier eine Schnittstellenklasse die bessere Lösung, aber wie schon öfter angedeutet ... Access/VBA ist eben nicht VB.net, C# oder C++ ;))
Josef Pötzl
Moderator
 
Beiträge: 764
Registriert: Mo 30. Nov 2009, 10:08
Wohnort: Klagenfurt
Accessversion: 2010 (2013)

Re: Zusammenspiel von Klassen

Beitragvon Sten Schmidt » Mi 12. Mai 2010, 07:30

Josef Pötzl hat geschrieben:Hallo!
Nachteil dieser Variante: man macht die Hauptklasse von der Detailklasse abhängig. Wenn ich nun die Detailklasse ABC durch eine andere Klasse "ABCerweitert" ersetzten will, ist das nicht mehr ganz so einfach. Ich muss dann auf jeden Fall in ABCerweitert als Schnittstelle die Klasse DetailklasseABC einbauen. ... und da beginnt dann das Problem bei Access: je mehr Schnittstellen eingebaut werden, desto höher ist die Wahrscheinlichkeit, dass der VBA-Binär-Code defekt wird - ohne regelmäßiges Decompile kracht es dann laufend während der Programmierung (zumindest nach meiner bisherigen Erfahrung).


<OT>

Ich habe in einem umfangreicherem Projekt das Problem, dass Access genau im Moment wo das Vorschlagsfenster von IntelliSense aufgehen will, Access (2007) sagt, dass es nicht mehr Funktioniert. Beende und öffne ich das Projekt in Access 2007 "funktioniert" es gleich nicht mehr - kein Weiterarbeiten möglich.

Interessant ist, dass die MDB durch einmaliges öffnen und komprimieren in Access 2003 wieder hergestellt werden kann und anschließend auch wieder mit Access 2007 bearbeitbar ist.

</OT>
Sten Schmidt
Entwickler
 
Beiträge: 140
Registriert: Do 18. Mär 2010, 22:24
Accessversion: 2007, 2010
Access-Erfahrung: Experte

Re: Zusammenspiel von Klassen

Beitragvon Josef Pötzl » Mi 12. Mai 2010, 17:35

... und das passiert noch viel öfter, sobald du implements verwendest und diese Klassen umschreibst.
Gerade bei umfangreicheren Anwendungen habe ich mir angewöhnt regelmäßig ein decompile durchzuführen. Bevor ich z. B. aus einer mit Ac07 (seit heute Ac2010 :D) erstellte mdb (nicht accdb!) mit Ac02 erzeuge, führe ich immer Decompile mit mit Ac02 aus. Damit bin ich bis jetzt ganz gut gefahren.

mfg
Josef
Josef Pötzl
Moderator
 
Beiträge: 764
Registriert: Mo 30. Nov 2009, 10:08
Wohnort: Klagenfurt
Accessversion: 2010 (2013)


Zurück zu Programmierstil

cron