Re: Grundsatzfrage zum Klassen-Design

Tech-Archive recommends: Speed Up your PC by fixing your registry

From: Immo Landwerth (mail_ignored_at_web.de)
Date: 07/29/04


Date: Thu, 29 Jul 2004 10:02:30 -0700

Carsten Posingies wrote:

> Immo Landwerth wrote:
> > Genau. Aber die meisten Entwickler machen doch keine Design
> > Fehler, oder? Es ist doch immer der böse Kunde ;)
>
> Das nennt man nicht "Fehler", sondern freundlich "Lernkurve" oder
> etwas saftiger "was interessiert mich mein Gehacktes von gestern?!"
> ;-)

Schön gesagt :)

> >> Ich habe jetzt nur eins noch nicht mitgeschnitten, nämlich wie
> >> die Daten auf dem DAL konkret in die Tabellen kommen. "INSERT
> >> PersonenObjekt INTO Datenbank" klappt ja nicht so ganz... Ich
> >> meine, irgendwo muss doch eine SQL-Anweisung stehen.
> >
> > Im letzten Beispiel wäre das in der Klasse, die IPersonManager
> > implementiert.
> >
> > public class MyPersonManager : IPersonManager
> > {
> > public Person Neu(string name, DateTime geburtstag);
> > public void ÄndereDaten(Person person);
> > }
>
> Hmmmm...! Dann ist mir jetzt der Vorteil, den ich bis zu Deinem
> letzten Posting zu riechen glaubte, soeben wieder aus der Nase
> geschwebt... Weil:
>
> public Person Neu(string name, DateTime geburtstag)
> {
> Person p = new Person();
> p.Name = name;
> p.Geburtstag = geburtstag;
> string[] ary = name.Split(" ".ToCharArray());
> string vorname = ary[0];
> string nachname = ary[1];
> Guid guid = Guid.NewGuid();
> string sql = "INSERT INTO Persons ( ID, FirstName, LastName,
> Birthdate)
> VALUES ( " + guid + ", '" + vorname + "', '" + nachname + "',
> #" + geburtstag.ToString() + "#";
> // dataadapter, dataset, datatable, datarow,
> // dataconnection, oledbcommandbuilder, bla...
> }

So in etwa hatte ich mir es vorgestellt, ja.

> > Das schöne an Interfaces ist dann ja, dass Du sie nicht ändern
> > kannst, ohne dass der Compiler sich beschwert. Deswegen: Mut zum
> > Refactoring.
>
> Mut zum Refactoring immer. Was Interfaces betrifft, ist das Meckern
> des Compilers aber irgendwie das Einzige, was ich ihnen abgewinnen
> kann. Aber das ist ein anderes Thema ;-)

Daraus höre ich, dass Du eindeutig aus der Welt ohne GC kommst - also
nicht Java oder vergleichbares.

Meiner einer kommt von C++ bzw. Delphi. Dort waren Interfaces nicht
wirklich das, was ich wollte. Aber unter C# bin ich mir als zufrieden
mit ihnen.

Just FMI: Was stört Dich an ihnen?

> > Es geht ja nur darum, von der Datenbeschaffung und Speicherung zu
> > abstrahieren, das grobe Datenformat sollte identisch sein. Sollte,
> > nicht muss.
>
> Ja, sicher. Vom DAL bis zum PL ist es ein weiter Weg, und das BL rührt
> sowieso alles zusammen. Aber letztlich habe ich immer Speicherung,
> Verarbeitung und Repräsentation. Ich rede die ganze Zeit von der Ebene
> der Speicherung. Dass im BL ein Personen-Objekt anders aussieht als im
> DAL/DL, ist mir schon klar. Und dass es im PL zig Ansichten mit
> verschiedensten Manipulationsmöglichkeiten für Personen-Objekte und
> Personen-Objekt-Sammlungen und Untermengen und Übermengen und etc.
> geben kann, ebenfalls.

Tja und hier scheiden sich die Geister. Bisher habe ich mir nicht die
Mühe gemacht, einen kommerziellen Persistance Layer einzusetzen. Das
empfand ich immer als oversized bzw. gelinde gesagt umständlich.

Letztlich ist mein Vorschlag eine Fusion aus DAL und PL. Allzuviele
verschiedene Schichten - womöglich händisch programmiert - schaffen IMO
mehr Probleme als Lösungen.

Für die meisten Projekte wäre das allein schon ein Fortschritt. Die
meisten befüllen die Tabellen schließlich direkt aus den Event Handlern
der GUI :)

Die Frage ist halt, was man will. Ich will keine eierlegende
Wollmilchsau, sondern einfach nur eine Architektur, die relativ leicht
anpassbar (ich verzichte bewusst auf das Buzzwort "skalierbar") ist und
mir als Entwickler gewisse Freiheiten lassen ohne dabei aus Copy &
Paste verstrahltem Code zu bestehen.

> Aber nochmal zurück zu unserem gemeinsamen Beispiel "Kunde will was,
> das zu einer Änderung von 1:n zu m:n führt"! Es ist doch sicher klar,
> dass sich so eine Änderung zwangsläufig quer durch alle Tiers zieht,
> weil es dem Kunden ja eben wurscht ist (jedenfalls meistens), wie die
> Daten gespeichert werden, solange er in der Anwendung sieht, was er
> will. Bleiben wir bei den EMails:
>
> - Bisher war jede EMail genau einer Person zugeordnet (Feld
> "PersonPID" in der Tabelle "Emails"). Jetzt machen wir eine
> m:n-Zuordnung draus, sodass es eine neue Referenz-Tabelle
> ("xPersonsEmails") gibt mit drei Feldern: "ID", "PersonPID" und
> "EmailPID". Nun musst Du doch eindeutig Dein IEmailManager-Interface
> anfassen, oder etwa nicht? Und darüberhinaus Deine Klasse
> MyEmailManager!

Exakt. Das Problem an solchen Änderungen ist ja oftmals gar nicht, dass
es sich durch alle Tiers ziehen, sondern dass man nicht alle Stellen
enstsprechend der Änderung anpasst. Das liegt i.d.R. schlicht daran,
dass irgendwo in den Untiefen der BL (oder ehrlich gesagt dem Client)
SQL Statements liegen, die nicht angepasst wurden.

Bei Dir wären dass auch die Klassen, die mittels Reflection automagisch
auf Tabellen der DB gemapped werden.

Das Hauptproblem an solchen Abhängigkeiten liegt darin, dass man sie
nur durch gründliche Source Recherche oder per "Ups!"-Effekt zur
Laufzeit findet. Sie sind nicht durch den Compiler zu finden.

Mit meinem Vorschlag erreicht man sicher kann 100 % Abhänhigkeit von
den Daten (was m.E. im Ansatz auch grober Unfug wäre) sondern eine
gezielte Verlagerung der Problems und quasi Nebenbei ein gewisse
Unabhänigkeit vom Speichermedium.

Natürlich musst Du den Code in den Interfaces und Managern
gleichermaßen anpassen. In der Praxis wird man aber wohl nicht mehr als
zwei Manager haben, so dass der Arbeitsaufwand im Rahmen bleibt.

Zugegeben, der Code innerhalb der Manager, ist natürlich nicht ohne
weiteres durch den Compiler kontrollierbar (SQL im String), weswegen
ich auch stark auf SPs baue.

-- 
Immo Landwerth - Visual Studio 2003 - C# - XanaNews 1.16.3.1