it-swarm.com.de

Wie strukturiere ich ein Projekt in Winform?

Vor einiger Zeit habe ich angefangen, eine Winform-Anwendung zu erstellen. Zu dieser Zeit war sie klein und ich habe mir keine Gedanken darüber gemacht, wie ich das Projekt strukturieren soll.

Seitdem habe ich nach Bedarf zusätzliche Funktionen hinzugefügt und der Projektordner wird immer größer. Jetzt denke ich, dass es Zeit ist, das Projekt auf irgendeine Weise zu strukturieren, aber ich bin mir nicht sicher, wie es richtig ist, daher habe ich nur wenige Fragen.

Wie kann ich den Projektordner richtig umstrukturieren?

Im Moment denke ich an so etwas:

  • Ordner für Formulare erstellen
  • Ordner für Utility-Klassen erstellen
  • Erstellen Sie einen Ordner für Klassen, die nur Daten enthalten

Wie lautet die Namenskonvention beim Hinzufügen von Klassen?

Sollte ich Klassen auch umbenennen, damit ihre Funktionalität durch einen Blick auf ihren Namen identifiziert werden kann? Benennen Sie beispielsweise alle Formularklassen um, sodass ihr Name mit Form endet. Oder ist dies nicht erforderlich, wenn spezielle Ordner für sie erstellt werden?

Was ist zu tun, damit nicht der gesamte Code für das Hauptformular in Form1.cs landet?

Ein weiteres Problem, auf das ich gestoßen bin, ist, dass die Codedatei (Form1.cs) sehr groß wird, da das Hauptformular mit jedem hinzugefügten Feature immer umfangreicher wird. Ich habe zum Beispiel ein TabControl und jeder Tab hat eine Reihe von Steuerelementen und der gesamte Code landete in Form1.cs. Wie vermeide ich das?

Kennen Sie auch Artikel oder Bücher, die sich mit diesen Problemen befassen?

27
user850010

Es sieht so aus, als wären Sie in einige der häufigsten Fallstricke geraten, aber keine Sorge, sie können behoben werden :)

Zuerst müssen Sie Ihre Anwendung etwas anders betrachten und sie in Stücke zerlegen. Wir können die Stücke in zwei Richtungen teilen. Zuerst können wir die Steuerungslogik (die Geschäftsregeln, den Datenzugriffscode, den Benutzerrechtscode, all diese Dinge) vom UI-Code trennen. Zweitens können wir den UI-Code in Blöcke zerlegen.

Also werden wir zuerst den letzten Teil machen und die Benutzeroberfläche in Teile zerlegen. Der einfachste Weg, dies zu tun, besteht darin, ein einziges Host-Formular zu haben, auf dem Sie Ihre Benutzeroberfläche mit Benutzersteuerelementen zusammenstellen. Jedes Benutzersteuerelement ist für einen Bereich des Formulars verantwortlich. Stellen Sie sich vor, Ihre Anwendung hatte eine Liste von Benutzern, und wenn Sie auf einen Benutzer klicken, wird ein Textfeld darunter mit dessen Details gefüllt. Sie können ein Benutzersteuerelement haben, das die Anzeige der Benutzerliste verwaltet, und ein zweites, das die Anzeige der Benutzerdetails verwaltet.

Der eigentliche Trick dabei ist, wie Sie die Kommunikation zwischen den Steuerelementen verwalten. Sie möchten nicht, dass 30 Benutzersteuerelemente im Formular zufällig Verweise aufeinander enthalten und Methoden aufrufen.

Sie erstellen also für jedes Steuerelement eine Schnittstelle. Die Schnittstelle enthält die Operationen, die das Steuerelement akzeptiert, sowie alle Ereignisse, die es auslöst. Wenn Sie an diese App denken, ist es Ihnen egal, ob sich die Listenauswahl der Listenfelder ändert. Sie sind daran interessiert, dass sich ein neuer Benutzer geändert hat.

Bei Verwendung unserer Beispiel-App würde die erste Schnittstelle für das Steuerelement, das die Listbox der Benutzer hostet, ein Ereignis namens UserChanged enthalten, das ein Benutzerobjekt ausgibt.

Das ist großartig, denn wenn Sie sich jetzt von der Listbox langweilen und eine 3D-Zoomy-Magic-Eye-Steuerung wünschen, codieren Sie sie einfach auf derselben Oberfläche und schließen sie an :)

Ok, also Teil zwei, der die UI-Logik von der Domänenlogik trennt. Nun, dies ist ein abgenutzter Weg und ich würde empfehlen, dass Sie sich hier das MVP-Muster ansehen. Es ist wirklich einfach.

Jedes Steuerelement wird jetzt als Ansicht (V in MVP) bezeichnet, und wir haben bereits das meiste behandelt, was oben benötigt wird. In diesem Fall die Steuerung und eine Schnittstelle dafür.

Alles, was wir hinzufügen, ist das Modell und der Präsentator.

Das Modell enthält die Logik, die Ihren Anwendungsstatus verwaltet. Sie kennen das Zeug, es würde in die Datenbank gehen, um die Benutzer zu erhalten, in die Datenbank zu schreiben, wenn Sie einen Benutzer hinzufügen, und so weiter. Die Idee ist, dass Sie all dies völlig isoliert von allem anderen testen können.

Der Moderator ist etwas schwieriger zu erklären. Es ist eine Klasse, die zwischen dem Modell und der Ansicht liegt. Es wird von der Ansicht erstellt und die Ansicht wird über die zuvor beschriebene Schnittstelle an den Präsentator übergeben.

Der Moderator muss keine eigene Oberfläche haben, aber ich erstelle trotzdem gerne eine. Legt fest, was der Präsentator explizit tun soll.

Der Präsentator würde also Methoden wie ListOfAllUsers verfügbar machen, mit denen die Ansicht die Liste der Benutzer abruft. Alternativ können Sie eine AddUser-Methode in die Ansicht einfügen und diese vom Präsentator aufrufen. Ich bevorzuge letzteres. Auf diese Weise kann der Präsentator jederzeit einen Benutzer zur Listbox hinzufügen.

Der Presenter verfügt auch über Eigenschaften wie CanEditUser, die true zurückgeben, wenn der ausgewählte Benutzer bearbeitet werden kann. Die Ansicht fragt dies dann jedes Mal ab, wenn sie es wissen muss. Möglicherweise möchten Sie bearbeitbare in Schwarz und lesen Sie nur solche in Grau. Technisch gesehen ist dies eine Entscheidung für die Ansicht, da sie sich auf die Benutzeroberfläche konzentriert. Ob der Benutzer überhaupt bearbeitet werden kann, ist für den Präsentator. Der Moderator weiß es, weil er mit dem Modell spricht.

Verwenden Sie also zusammenfassend MVP. Microsoft bietet SCSF (Smart Client Software Factory) an, das MVP in der von mir beschriebenen Weise verwendet. Es macht auch viele andere Dinge. Es ist ziemlich komplex und ich mag nicht, wie sie alles machen, aber es kann helfen.

24
Ian

Ich persönlich bevorzuge es, verschiedene Problembereiche zwischen mehreren Baugruppen zu trennen, anstatt alles zu einer einzigen ausführbaren Datei zusammenzufassen.

Normalerweise bevorzuge ich es, eine absolut minimale Menge an Code im Einstiegspunkt der Anwendung zu behalten - Keine Geschäftslogik, kein GUI-Code und kein Datenzugriff (Datenbanken/Dateizugriff/Netzwerkverbindungen/usw.); Normalerweise beschränke ich den Einstiegspunktcode (d. H. Die ausführbare Datei) auf etwas in der Art von

  • Erstellen und Initialisieren der verschiedenen Anwendungskomponenten aus allen abhängigen Assemblys
  • Konfigurieren von Komponenten von Drittanbietern, von denen die gesamte Anwendung abhängt (z. B. Log4Net für die Diagnoseausgabe)
  • außerdem werde ich wahrscheinlich ein Codebit vom Typ "Alle Ausnahmen abfangen und den Stack-Trace aufzeichnen" in die Hauptfunktion aufnehmen, mit dessen Hilfe die Umstände unvorhergesehener kritischer/schwerwiegender Fehler protokolliert werden können.

Was die Anwendungskomponenten selbst betrifft, strebe ich normalerweise mindestens drei in einer kleinen Anwendung an

  • Datenzugriffsschicht (Datenbankverbindungen, Dateizugriff usw.) - Abhängig von der Komplexität der von der Anwendung verwendeten persistenten/gespeicherten Daten gibt es möglicherweise mehrere dieser Assemblys. Ich würde wahrscheinlich eine separate Assembly für die Datenbankverarbeitung erstellen (möglicherweise sogar mehrere Assemblys, wenn die Interaktion mit der Datenbank etwas Komplexes beinhaltet - z. B. wenn Sie mit einer schlecht gestalteten Datenbank stucx sind, müssen Sie möglicherweise DB-Beziehungen im Code behandeln, daher ist es möglicherweise sinnvoll, mehrere Module zum Einfügen und Abrufen zu schreiben.

  • Logic Layer - das wichtigste "Fleisch", das alle Entscheidungen und Algorithmen enthält, mit denen Ihre Anwendung funktioniert. Diese Entscheidungen sollten absolut nichts über die GUI wissen (wer sagt, dass es eine GUI gibt?) Und absolut nichts über die Datenbank wissen (Huh? Es gibt eine Datenbank? Warum nicht eine Datei?). Eine gut gestaltete Logikschicht kann hoffentlich "herausgerissen" und in eine andere Anwendung verschoben werden, ohne dass sie neu kompiliert werden muss. In einer komplizierten Anwendung gibt es möglicherweise eine ganze Reihe dieser Logikbaugruppen (weil Sie möglicherweise nur "Teile" herausreißen möchten, ohne den Rest der Anwendung mit sich zu ziehen).

  • Präsentationsschicht (d. H. Die GUI); In einer kleinen Anwendung gibt es möglicherweise nur ein einziges "Hauptformular" mit einigen Dialogfeldern, die alle zu einer einzigen Baugruppe gehören können. In einer größeren Anwendung gibt es möglicherweise separate Baugruppen für ganze Funktionsteile der GUI. Die Klassen hier werden kaum mehr tun, als die Benutzerinteraktion zum Laufen zu bringen - es wird kaum mehr als eine Shell mit einigen grundlegenden Eingabevalidierungen, der Verarbeitung von Animationen usw. sein. Alle Ereignisse/Schaltflächenklicks, die "etwas tun", werden an weitergeleitet die Logikschicht (meine Präsentationsschicht enthält also streng genommen keinerlei Anwendungslogik, aber sie belastet auch nicht die Logikschicht mit GUI-Code - daher befinden sich auch Fortschrittsbalken oder andere ausgefallene Dinge in der Präsentationsbaugruppe/ies)

Mein Hauptgrund für die Aufteilung der Präsentations-, Logik- und Datenebenen auf separate Assemblys lautet: Ich bin der Meinung, dass es vorzuziehen ist, die Hauptanwendungslogik ohne Ihre Datenbank oder Ihre GUI ausführen zu können.

Um es anders zu sagen; wenn ich eine andere ausführbare Datei schreiben möchte, die sich genauso verhält wie Ihre Anwendung, aber eine Befehlszeilenschnittstelle oder eine Webschnittstelle verwendet; und tauscht den Datenbankspeicher gegen Dateispeicher (oder eine andere Art von Datenbank), dann kann ich dies tun, ohne die Hauptanwendungslogik berühren zu müssen - alles, was ich tun muss, ist ein wenig ein Befehlszeilentool zu schreiben und ein anderes Datenmodell, dann "alles zusammenstecken", und ich bin bereit zu gehen.

Sie denken vielleicht: "Nun, ich werde niemals wollen, um irgendetwas davon zu tun, also spielt es keine Rolle, ob ich diese Dinge nicht austauschen kann" - der eigentliche Punkt ist, dass einer der Kennzeichen einer modularen Anwendung ist die Möglichkeit, "Chunks" zu extrahieren (ohne dass etwas neu kompiliert werden muss) und diese Chunks an anderer Stelle wiederzuverwenden. Um Code wie diesen zu schreiben, müssen Sie im Allgemeinen lange und gründlich über Designprinzipien nachdenken. Sie müssen viel mehr Schnittstellen schreiben und sorgfältig über die Kompromisse zwischen verschiedenen SOLID-Prinzipien nachdenken (in derselben) so wie Sie es für Behavior-Driven-Development oder TDD tun würden)

Manchmal ist das Erreichen dieser Trennung von einem vorhandenen monolithischen Codeklumpen etwas schmerzhaft und erfordert viel sorgfältiges Refactoring - das ist in Ordnung, Sie sollten dies schrittweise tun können - Sie können sogar einen Punkt erreichen, an dem zu viele Assemblys vorhanden sind, und Sie entscheiden Um in die andere Richtung zurückzukehren und die Dinge wieder zusammenzufassen (zu weit in die entgegengesetzte Richtung zu gehen, kann dazu führen, dass diese Baugruppen für sich genommen eher unbrauchbar werden).

8
Ben Cottrell

Gemäß der Ordnerstruktur ist das, was Sie vorgeschlagen haben, im Allgemeinen in Ordnung. Möglicherweise müssen Sie Ordner für Ressourcen (manchmal erstellen Benutzer Ressourcen so, dass jeder Ressourcensatz unter einem ISO-Sprachcode gruppiert wird, um mehrere Sprachen zu unterstützen), Bilder, Datenbankskripte, Benutzereinstellungen (sofern nicht als Ressourcen behandelt) und Schriftarten hinzufügen , externe DLLs, lokale DLLs usw.

Was ist die Namenskonvention beim Hinzufügen von Klassen?

Natürlich möchten Sie jede Klasse außerhalb des Formulars trennen. Ich würde auch eine Datei pro Klasse empfehlen (obwohl MS dies beispielsweise in Code, der für EF generiert wurde, nicht tut).

Viele Leute verwenden bedeutungsvolle kurze Substantive im Plural (z. B. Kunden). Einige geben an, dass der Name nahe am singulären Namen für die entsprechende Datenbanktabelle liegt (wenn Sie eine 1-1-Zuordnung zwischen Objekten und Tabellen verwenden).

Für die Benennung von Klassen gibt es viele Quellen. Sehen Sie sich beispielsweise Folgendes an: . Net Naming Conventions and Programming Standards - Best Practices und/oder STOVF-C # Coding Guidelines

Was zu tun ist, damit nicht der gesamte Code für das Hauptformular in Form1.cs landet

Der Hilfecode sollte zu einer oder mehreren Hilfsklassen gehören. Anderer Code, der bei GUI-Steuerelementen sehr häufig vorkommt, z. B. das Anwenden von Benutzereinstellungen auf ein Raster, kann aus dem Formular entfernt und entweder zu Hilfsklassen hinzugefügt oder durch Unterklassifizieren des betreffenden Steuerelements und Erstellen der erforderlichen Methoden dort erstellt werden.

Aufgrund der Ereigniswirkung von MS Windows Forms gibt es nichts, von dem ich weiß, dass es Ihnen helfen könnte, Code aus dem Hauptformular zu entfernen, ohne Mehrdeutigkeit und Aufwand hinzuzufügen. MVVM kann jedoch eine Wahl sein (in zukünftigen Projekten), siehe zum Beispiel: MVVM für Windows Forms .

4
NoChance

Ziehen Sie MVP als Option in Betracht, da es Ihnen hilft, die Präsentationslogik zu organisieren, die in großen Geschäftsanwendungen alles ist. Im wirklichen Leben befindet sich die App-Logik hauptsächlich in der Datenbank, so dass Sie selten eine Business-Schicht in nativem Code schreiben müssen, sodass nur eine gut strukturierte Präsentationsfunktion erforderlich ist.

Eine MVP-ähnliche Struktur führt zu einer Reihe von Präsentatoren oder Controllern, wenn Sie dies bevorzugen, die sich aufeinander abstimmen und sich nicht mit der Benutzeroberfläche oder dem Code hinter den Dingen vermischen. Sie können sogar verschiedene Benutzeroberflächen mit demselben Controller verwenden.

Durch einfaches Organisieren von Ressourcen, Entkoppeln von Komponenten und Festlegen von Abhängigkeiten mit IoC und DI sowie einen MVP-Ansatz erhalten Sie die Schlüssel zum Aufbau eines Systems, das häufige Fehler und Komplexität vermeidet, rechtzeitig geliefert wird und für Änderungen offen ist.

2
Panos Roditakis

Die Struktur eines Projekts hängt vollständig vom Projekt und seiner Größe ab. Sie können jedoch nur wenige Ordner hinzufügen, z.

  • Allgemein (Enthält Klassen, z. B. Dienstprogramme)
  • DataAccess (Klassen für den Zugriff auf Daten mit SQL oder einem anderen von Ihnen verwendeten Datenbankserver)
  • Stile (Wenn Sie CSS-Dateien in Ihrem Projekt haben)
  • Ressourcen (z. B. Bilder, Ressourcendateien)
  • WorkFlow (Klassen, die sich auf den Workflow beziehen, falls vorhanden)

Sie müssen Formulare nicht in einen Ordner legen, sondern benennen Sie Ihre Formulare entsprechend um. Ich meine, es ist gesunder Menschenverstand, niemand weiß, welcher Name Ihre Formen besser sein sollte als Sie.

Die Namenskonvention ist so, als ob Ihre Klasse eine "Hello World" -Nachricht druckt, dann sollte der Klassenname etwas mit der Aufgabe zu tun haben und der entsprechende Name der Klasse sollte HelloWorld.cs sein.

sie können auch Regionen erstellen, z.

#region Hello und dann raus endregion am Ende.

Sie können Klassen für Registerkarten erstellen, da sind Sie sich ziemlich sicher, und eine letzte Sache ist, Ihren Code nach Möglichkeit wiederzuverwenden. Die beste Vorgehensweise besteht darin, Methoden zu erstellen und sie bei Bedarf erneut zu verwenden.

Bücher? ähm.

Es gibt keine Bücher, die Ihnen die Struktur von Projekten erklären, da jedes Projekt anders ist. Sie lernen diese Art von Dingen aus Erfahrung.

Hoffe es hat geholfen!

1
Muhammad Raja