Laden...

Brauche Beispiel für 3-Schichten Model

Erstellt von #coder# vor 15 Jahren Letzter Beitrag vor 15 Jahren 3.870 Views
#coder# Themenstarter:in
395 Beiträge seit 2008
vor 15 Jahren
Brauche Beispiel für 3-Schichten Model

Hallo, ich möchte ein Getränke Automat in WinForms erstellen, welches im 3 Schichten Model realisiert werden soll. Im Automaten werden verschiedene Getränke zu verschiedenen Preisen angeboten, nun soll man Geld einschmeißen können, durch klicken der "Münz" Buttons (10, 20, 50 cent, 1€) soll der counter erhöht werden. Wenn ausreichend Geld eingeworfen wurde sollte das Getränk dann ausgegeben werden.

Nun wie fängt man hier an, zuerst sollte man wahrscheinlich ein Klassendiagramm erstellen!?
Was müsste in den einzelnen Schichten nun enthalten sein, also welcher Teil des Automaten kommt wohin?
Wenn ich meine Daten z.B. Preise in einer XML habe, werden diese dann doch im Data Layer verarbeitet oder?

Da ich sowas zum ersten mal mache wollte ich bei euch um Rat helfen 😃

C
401 Beiträge seit 2007
vor 15 Jahren

Der Data Access Layer bietet dir nur Zugriff auf die Daten. Jegliche Verarbeitung sollte im BLL passieren. Hört sich irgendwie nach Schulaufgabe an und da solltest du dir zunächst selber Gedanken machen. Informationen gibt es ja genug. Benutze doch einfach die Forumssuche.

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo #coder#,

Mach doch einfach ein Klassendiagramm oder allgemein ein Designe (Klassen, Methoden, Properties, Dokumentation, etc.).
Das "fertige" Design kannst du dann hier posten und wenn etwas grob Falsch ist wird man dir mit freude helfen.

Gruß
Juy Juka

888 Beiträge seit 2007
vor 15 Jahren
E
151 Beiträge seit 2007
vor 15 Jahren

In deinem Fall eigentlich sehr einfach zu beantworten:

Präsentationsschicht:
Die Maske, da kannst du dein Getränk (per Button oder wie auch immer) auswählen. Hier wird aber nichts gemacht als die Auswahl! Einfach das ausgewählte Getränk an die nächste Schicht weiter geben.

Verarbeitungssschicht oder Buisness-Schicht:
Hier kannst du jetzt alles machen was dein Automat können soll.
Bestellungen sammeln, Preise berechnen,....

Datenzugriffsschicht:
Im wesentlichen ist das nur eine Schicht, die von der Buisnesslogik eine Anfrage bekommt und die entsprechenden Daten aus der Datenbank bzw Datenquelle liefert. Bsp: Buisness will Preis berechnen, sie fragt die Datenschicht was die Getränke kosten, die schaut nach und liefert die Preise zurück. Vorsicht hier: Summe aller Preise wäre wieder Buisness, nicht Daten! Aber das ist Geschmackssache wie genau man sich daran hält.

Sinn und Ziel der Schichten ist nur, einfach Dinge austauschen zu können.
Zb brauchst du, wenn du es gut implementierst, nicht die Logik ändern wenn du eine neue Maske erstellst, oder das ganze zb als WebApplication anbieten willst.

Wenn ich jetzt etwas falsch erklärt habe wird mich sicherlich jemand verbessern g

LG & GL

#coder# Themenstarter:in
395 Beiträge seit 2008
vor 15 Jahren

Muss ich für jede Schicht eine Library erstellen, z.b. bei Business & Data Layer ?
Im Prinzip sind es doch 3 Projekte oder?

E
107 Beiträge seit 2008
vor 15 Jahren

Weil das hier gerade einigermaßen passt und den Threadersteller eventuell auch interessiert:

Wenn meine Business-Layer (BL) mehrere Klassen hat, dürfen diese Klassen innerhalb der Schicht ja soviel miteinander kommunizieren, wie ich es will. Binde ich dann in jeder Klasse der BL alle anderen Klassen der BL ein, ... sollte ich das dann via Objekte machen, oder über using... ?! Das ist mir noch nicht so ganz klar.

Danke.

Ich lasse mich gerne korrigieren! (:

3.003 Beiträge seit 2006
vor 15 Jahren

Binde ich dann in jeder Klasse der BL alle anderen Klassen der BL ein, ... sollte ich das dann via Objekte machen, oder über using... ?!

Hm? Beweg dich einfach im selben Namespace. "in jede Klasse alle anderen Klassen einbinden" - was meinst du damit?

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo #coder#,

müssten tust du das nicht, aber gerade wenn du neu bist, ist es schon günstig, drei Projekte zu machen, damit du klare Zuständigkeiten hast und nicht versehentlich in einer Schicht Klassen benutzt, die diese eigentlich nicht kennen darf.

herbivore

#coder# Themenstarter:in
395 Beiträge seit 2008
vor 15 Jahren

@herbivore: An genau so einer Situtation befinde ich mich nun, also ich habe 3 Projekte erstellt für die Schichten. Nun möchte ich eine Klasse befüllen mithilfe der XML Datei, die Frage die sich mir nun stellt wo kommt die Klasse hin, Business oder Data? Ich brauche das Objekt in der Business Schicht

Wie sieht die Verbindung zwischen den Schichten aus, sind das normale Klassen oder muss man Interfaces verwenden zwischen den Schichten verwenden??

3.728 Beiträge seit 2005
vor 15 Jahren
3-Schichten

Hallo #coder#,

die **Datenzugriffsschicht **kümmert sich nur darum, Daten zu lesen und Daten zu speichern. Im Falle Deiner XML-Preisliste könnte ich mir das etwa so vorstellen:


public class DataAccess
{
    public SodaMachineDataSet GetItemsAndPricesFromFile(string fileName)
    {
        ...
    }

    public void SaveItemsAndPricesToFile(SodaMachineDataSet dataSet, string fileName)
    {
        ...
    }    
}

Das Datenmodell brauchst Du in allen drei Schichten. Deshalb solltest Du sogar noch ein viertes Visual Studio-Projekt mit dem Namen **Contracts **(zu Deutsch: Verträge) anlegen. Dort packst Du Klassen des Datenmodells und alle sonstigen Klassen rein, die in allen drei Schichten benötigt werden. Ein typisiertes DataSet eignet sich sehr gut als Datenmodell, da es standardmäßig XML speichern und lesen kann.

In der **Geschäftsschicht **muss die eigentliche Funktion des Automaten abgebildet werden. Die Prüfung, ob z.B. genug Geld eingeworfen wurde, ist z.B. ein Fall für die Geschäftslogik. Da die Geschäftslogik möglichst Statuslos sein sollte (also keine Variablen über mehrere Funktionsaufrufe - aus anderen Schichten heraus - hinweg halten!), benötigst Du noch eine Klasse, die den Status beschreibt. Diese "Status" Klasse gehört ins Geschäftsschicht-Projekt. Sie darf nur Daten enthalten, keine Logik! Daten und Logik sollten bei einer 3-Schicht Anwendung immer getrennt werden. Also ++nicht ++eine Klasse schreiben, die Daten und Logik des kompletten Getränkeautomaten verschmilzt. Der vollständig objektorientierte Ansatz passt nicht zum 3-Schichten-Modell.

So könnte eine Status-Klasse aussehen:


public class SodaMachineState
{
    public int SelectedBeverageID
    {
        get { ... }
        set { ... }
    }

    public int InsertedMoney
    {
        get { ... }
        set { ... }
    }

    public bool CanDumped
    {
        get { ... }
        set { ... }
    }

}

Die eigentliche Geschäftslogik könnte man z.B. so ausdrücken:


public class SodaMachineLogic
{
    public void InsertCoin(int coinValue, SodaMachineState state)
    {
        ...
    }

    public void RequestCanDump(SodaMachineState  state)
    {
        ...
    }

    public void SelectBeverage(int itemID, SodaMachineState  state)
    {
        ...
    }

    public void Abort(SodaMachineState  state)
    {
        ...
    }
}

Die Präsentationsschicht (wahrscheinlich ein Windows.Forms-Formular) hält über die Laufzeit der Anwendung hinweg das DataSet mit den Getränkearten und Preisen und zu zusätzlich eine Instanz der Status-Klasse. Wenn der Benutz nun "Geld einwirft", ruft das Formular die Geschäftsschicht auf, übergibt den Betrag und einen Verweis auf die Status-Klasse an die InsertCoin-Methode. Diese prüft, ob die eingeworfene Münze gültig ist (10, 20, 30, 100) und addiert falls ja den Wert zu InsertedMoney der Status-Klasse. So kann das Formular nach dem InsertCoin-Aufruf sofort die Anzeige fürs eingeworfene Geld aktualisieren. Wenn die Münze ungültig ist, könnte die Geschäftslogik eine Ausnahme werfen. Diese wird vom Formular gefangen und als Fehlermeldung anzeigt.

Nun solltest Du eine Vorstellung davon haben, wie man eine Anwendung in 3-Schichten-Architektur entwirft.

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo #coder#,

Man muss keine interfaces verwenden, es ist aber zu empfehlen.

Um mal eine Lösungen für dein Problem zu zeigen:


namespace ...Datenzugriffsschicht
{
  public delegate T KonstruktionsMethode<T>();

  public interface IDatenquelle
<T>

  {
    void IEnumerable<T> Get(KonstruktionsMethode<T> konstruktion);
  }

  public class DatenQuelle<T> : IDatenQuelle<T>
  {
    public void IEnumerable<T> Get(KonstruktionsMethode<T> konstruktion)
    {
      if ( konstruktion == null ) throw new ArgumentNullException("konstruktion");
      foreach(DataRow dr in GetDataRowsFromXml())
      {
        T re = konstruktion();
	foreach(PropertyInfo property in typeof(T).GetProperties())
        {
          // hier werden halt im endefekkt alle Properties aus dem XML in das Objekt übertragen.
          property.SetValue(re,dr[re.Name],new object[]{});
        }
        yield return re;
      }
    }
  }
}

Das ganze ist natürlich nur grob und ziemlich schlechter Code. 😉 Aber so ungefähr könnte man's machen.

Alternativ könnte man auf die Generics (<T>) verzichten und stadt dessen ein interface deklarieren, was jedoch eigentlich durch die Businessschicht definiert aber in der Datenzugriffsschicht benötigt wird:


namespace ...Datenzugriffsschicht
{
  public delegate T KonstruktionsMethode<T>();

  public interface IDatenquelle
<T>

  {
    void IEnumerable<T> Get(KonstruktionsMethode<T> konstruktion);
  }

  public class GetraenkDatenQuelle : IDatenQuelle<IGetraenk>
  {
    public void IEnumerable<IGetraenk> Get(KonstruktionsMethode<IGetraenk> konstruktion)
    {
      if ( konstruktion == null ) throw new ArgumentNullException("konstruktion");
      foreach(DataRow dr in GetDataRowsFromXml())
      {
        IGetraenk re = konstruktion();
        re.Preis = dr["preis"];
        // ...
        yield return re;
      }
    }
  }
}

Ist dann natürlich nicht mehr so sauber getrennt, dafür halt einfacher.

Gruß
Juy Juka

[EDIT]War ja klar, das mal wieder wer schneller ist: Zu Rainbird's Post:
Das mit den DataSet ist ungefähr wie mit dem Interface aus meinem Vorschlag.
Das mit der State-Klasse und der getrennten Funktionsklasse will ich hier nicht ausschweifen, dazu gibts schon mindestens einen Thread [EDIT]Gefunden: 3-Schichten-Design?? Hilfe bei der Umsetzung[/EDIT]
[/EDIT]

E
107 Beiträge seit 2008
vor 15 Jahren

edit: link wurde bereits gepostet.

@LaTino:
Z.B. habe ich in der Business-Layer vier Klassen im gleichen Namespace und alle public. Jede der vier Klassen hat ein Objekt der anderen drei Klassen übergeben bekommen, um deren Funktionen nutzen zu können, da es ohne Objekt trotz public nicht geht. Ich frage mich halt, ob das der richtige Weg ist, oder ob ich die anderen Klassen irgendwie über using... einbinden kann/sollte/müsste?!

Ich lasse mich gerne korrigieren! (:

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo eveN,

Vielleicht solltest du einen eigenen Thread eröffnen!

Als Erstes: using hat erst mal 0 und nichts mit dem Verwenden von Objekten zu tun. Über using kann man nur Klassennamen "abkürzen", z.B. Button stadt System.Windows.Forms.Button.

Das andere Problem was du ansprichst ist lose Koppelung beziehungsweise deren Fehlen. Da ist irgend wo der Wurm im Klassendesign, wenn du solche seltsamen netze von Objekten hast. Das muss man aber im Detail-Fall anschauen und kann dann mit den Passenden Werkzeugen (Entwurfsmusster / Patterns) abhilfe schaffen.

Gruß
Juy Juka

#coder# Themenstarter:in
395 Beiträge seit 2008
vor 15 Jahren

Noch eine Frage, wenn ich eine Klasse mit Daten auffüllen will z.B. Produkt, Preis aus der XML Datei, wo muss sich die Klasse befinden im Business oder Data Layer? Eigentlich müsste die in der Business Schicht enthalten sein, da ich diese dort verwenden muss.

3.728 Beiträge seit 2005
vor 15 Jahren
Datenklassen

Hallo #coder#,

Datenklassen (Produkt, Preis, etc.) werden von allen Schichten gleichermaßen verwendet. Deshalb müssen sie in einer separaten Contract-Assembly abgelegt werden. Die anderen Projekte benötigen einen Verweis auf diese Assembly. Du benötigst die Datenklassen auch in der Benutzerschicht. Woher soll denn das Formular sonst die Daten nehmen, um die Steuerelemente zu füllen?

Zugeordnet werden Datenklassen der Datenzugriffsschicht. Trotzdem dürfen sie nicht mit der eigentlichen Datenzugriffslogik in der selben Assembly liegen.

Generell ist die Vorstellung 1 Schicht = 1 Assembly, 3 Schichten = 3 Assemblies falsch.

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo Rainbird,

Leider muss ich deiner Aussage wiedersprechen.
Die "Datenklassen" müssen nicht in ein extra Assembly, das man überall referenziert!
Im allgemeinen wiederspreche ich ja dem Ansatz, dass man Daten und Funktion in zwei Klassen trennt.
Die Daten zu extrahieren braucht man ausschließlich wenn man Grenzen (Programm, Framework, Prozessor, etc.) überschreiten muss, im rest eines OO-Programmes sollte so etwas nur versteckt passieren...

Egal, das weicht zu weit ab, dazu haben wir ja mindestens einen anderen Thread (3-Schichten-Design?? Hilfe bei der Umsetzung).

@ #coder# : Mit etwas aufwand kann man solche Klassen in der Logik-Schicht definieren, die Datenzugriffs-Schicht muss dafür halt allgemeingültig werden. Also ich würde die Klassen in der Business-Schicht definieren.

Gruß
Juy Juka

3.003 Beiträge seit 2006
vor 15 Jahren

Deshalb müssen sie in einer separaten Contract-Assembly abgelegt werden. Die anderen Projekte benötigen einen Verweis auf diese Assembly.

Kann man sicher mit einer überall referenzierten Assembly machen. Wenn, würde ich in dieser referenzierten Assembly maximal die Schnittstellendefinitionen halten. Mehr müssen die referenzierenden Schichten wirklich nicht wissen 😃.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

3.728 Beiträge seit 2005
vor 15 Jahren

Wenn, würde ich in dieser referenzierten Assembly maximal die Schnittstellendefinitionen halten. Mehr müssen die referenzierenden Schichten wirklich nicht wissen 😃.

Der DataSet-Designer erzeugt keine Schnittstellen, sondern nur Klassen. Ich habe #coder# oben vorgeschlagen, typisierte DataSets zu verwenden. Außerdem benötigt man die Klassen, wenn man die Lösung einmal verteilbar machen will, denn Schnittstellen kann man nicht Serialisieren.
Deshalb macht es Sinn das so zu machen.

Wenn man keine typisierten DataSets einsetzt und alles mit POCOs und Schnittstellen macht, hast Du aber völlig recht, dann reichen die Schnittstellen (Solange man nicht verteilt!).

3.003 Beiträge seit 2006
vor 15 Jahren

@rainbird hast natürlich recht, typisierte DataSets sind schon seit paar Jahren außerhalb meines geistigen Ereignishorizontes, dh vergesse immer wieder, dass es die Dinger ja auch noch gibt...

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

#coder# Themenstarter:in
395 Beiträge seit 2008
vor 15 Jahren

Hallo nochmal, habe mal eine UML Klassendiagramm erstellt, ist aber noch nicht fertig!
Ich habe den State Pattern verwendet, nun habe ich noch paar fragen:

Jeder Zustand ist eine Klasse in der unterschiedliche Funktionen behandelt werden oder?

Bin mir nicht so sicher bei den Zuständen ob die richtig gesetzt sind. Auch bei den Beziehungen bin ich mir nicht sicher, zudem fehlt noch die Daten Schicht, die ich noch weggelassen habe. Hoffe ihr könnt mir weiterhelfen.

EDIT: Wo werden die einzelnen Abfragen wie IsValidMoney() abgefragt, passiert es in dem jeweiligen Zustand oder im Getraenkeautomat ??