Autor |
Beitrag |
Machiavelli
Hält's aus hier
Beiträge: 7
|
Verfasst: So 01.06.14 19:51
Hallo,
ich arbeite zurzeit an einer Konsolenanwendung, welche Bundesligatabellen erstellen kann (auch Tabellen von anderen Ligen, daher ist der Programname eigentlich falsch). Ich weiß nicht so ganz, wie ich das ganze umsetzen soll. Ich habe bereits eine Klasse Club entwickelt, die die nötigen Infos speichert für die Tabelle (Punkte, Tordifferenz, etc.) und diese Infos wieder ausgeben kann auf Bedarf. Ich wollte ein Feld erzeugen mit einer Anzahl von 18, wobei alle Elemente dieses Feldes eine Instanz der Klasse Club ist. So wollte ich diese ganze Sache implementierten, ich halte diese Option aber für zu kompliziert. Daher ist meine Frage, ob man das noch vereinfachen könnte.
Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Bundesliga { class Program { static void Main(string[] args) { String[] Menü = new String[4] { "Tabelle ansehen", "Tabelle hinzufuegen", "Tabelle aendern", "Programm schließen" };
for (int i = 0; i < 4; i++) { Console.WriteLine("[{0}] {1}", i, Menü[i]); } String Eingabe = Console.ReadLine(); switch (Eingabe){ case "0": break; case "1": break; case "2": break; case "3": break; }
} }
}
public class Club { private String _name; private int _games; private int _wins; private int _draws; private int _defeats; private int _goals; private int _goaldifference; private int _points;
public Club(String name, int games, int wins, int draws, int goals, int goaldifference) { _name = name; _games = games; _wins = wins; _draws = draws; _defeats = _games - _wins - _draws; _goals = goals; _goaldifference = goaldifference; _points = _wins * 3 + _draws; }
public int Points { get { return _points; } set {_points = value; } } public int Games { get { return _games; } set { _games = value; } } public int Wins { get { return _wins; } set { _wins = value; } } public int Draws { get { return _draws; } set { _draws = value; } } public int Defeats { get { return _defeats; } set { _defeats = value; } } public int Goals { get { return _goals; } set { _goals = value; } } public int Goaldifference { get { return _goaldifference; } set { _goaldifference = value; } } public String Name { get { return _name; } set { _name = value; } } } |
Danke!
Moderiert von Th69: Code- durch C#-Tags ersetztModeriert von Th69: Topic aus C# - Die Sprache verschoben am Mo 02.06.2014 um 10:10
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mo 02.06.14 10:20
Hallo und
ja, ein Array oder aber noch besser eine List<Club> ist schon die richtige Datenstruktur dafür. Was empfindest du denn daran als zu kompliziert?
Wenn du mehrere Tabellen (bzw. Ligen) verwalten möchtest, dann solltest du dafür auch eine Klasse erzeugen (welche dann intern die obige Liste enthält).
PS: bzgl. Codevereinfachung kannst du deine Club-Klasse mit automatischen Eigenschaften (automatic properties) versehen:
C#-Quelltext 1: 2:
| public int Points { get; private set; } |
Ich habe den Setter explizit auf private hier gesetzt, damit man von außen nicht willkürlich diese Werte ändern kann. Erzeuge dir dafür dann entsprechende Methoden in dieser Klasse (denn die Eigenschaften hängen ja zusammen).
Edit: und bitte Crosspostings (entsprechend unserer Richtlinien) immer angeben: myCSharp.de - Programmidee: "Bundesligatabellenersteller" - Fragen zur Umsetzung (auch wenn der Thread dort schon wieder geschlossen wurde)!
Zuletzt bearbeitet von Th69 am Do 05.06.14 19:55, insgesamt 1-mal bearbeitet
|
|
Machiavelli 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mo 02.06.14 19:00
Hallo,
Zitat: | PS: bzgl. Codevereinfachung kannst du deine Club-Klasse mit automatischen Eigenschaften (automatic properties) versehen: |
Das geht so nicht.
Ich habe folgendes darauf in Google recherchiert:
Ehrlich, ich finde den Fehler einfach nicht. Ich habe mir dies alles durchgelesen, aber wenn ich den Code nach Deinen Angaben verkürze, kommt immer 0 zurück! Ich habe danach getetsted und musste feststellen, nachdem ich dem ich der Eigenschaft einen Wert zugewiesen habe, konnte ich den Wert abrufen, aber ich will ja ein Attribut per get; , dass schon einen Wert besitzt, zurückgeben.
Ich habe das jetzt mal so gelassen wie es ist, denn so funktioniert es (mehrfach getestet!)
Ich beschäftige mich schon seit Stunden mit der Modellierung dieses Projektes. Der Code, der hier gepostet wurde, funktioniert einwandfrei, ist aber laut den Meinungen der Forenmitglieder unschön modelliert. Da das Forum "Neulinge" nicht akzeptieren mag, frage ich aus Neugier hier nach, stets in der Hoffnung gute Hilfe zu bekommen. Die Idee von Lars Schmitt (User im anderen Forum) gab mir die Idee, das Programm in mehrere Klassen aufzubauen.
Ich habe bishert zwei Klassen "modelliert", habe dennoch wieder das Gefühl, dass ich es mir zu kompliziert mache. Ich habe eine , ich nenn sie mal Managerklasse, namens GameLogic. Diese Klasse enthält ein Feld des Types Club , welches Vereine speichert in den Elementen. Die Klasse Club ist Dir ja bekannt, sie speichert wichtige Infos über den Club selber (also : wie viele Tore das Team geschossen hat, wie viele Punkte das Team erreicht hat, etc.) und man kann diese Daten mit Hilfe von Eigenschaften (set & get) ausgeben und modifizieren.
Das Menü selber enthält 4 Punkte:
[0] Tabelle ansehen
[1] Tabelle erstellen
[2] Tabelle ändern
[3] Exit
Option [0] und [3] habe ich kein Probleme, [2] habe mich noch nicht beschäftigt, habe zurzeit Probleme (nicht wirkliche Probleme, eher Umsetzungsfragen) mit Option [1]. Später dazu mehr.
Ich habe bereits, etwas konfuse, Klassendiagramme erstellt, siehe hier:
Mir ist dieses Projekt sehr wichtig, denn ich will und möchte mich unbedingt in Sachen Modellierung eines Projektes verbessern.
Aber, nun zum wichtigsten, der aktuelle Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Bundesliga { class Program { static void Main(string[] args) { ProgramLogic Logic = null;
String[] Menü = new String[4] { "[0] Tabelle ansehen", "[1] Tabelle erstellen", "[2] Tabelle ändern", "[3] Exit" }; for (int i = 0; i < 4; i++) { Console.WriteLine(Menü[i]); }
String Eingabe = Console.ReadLine(); switch (Eingabe){ case "0": if (Logic == null) { Console.WriteLine("Tabelle noch nicht erstellt! Programm wird nach Tasteneingabe geschlossen."); Console.ReadKey(); Environment.Exit(0); } else { String[] table = Logic.generate_table(); for (int i = 0; i < 18; i++) { Console.WriteLine(table[i]); } } break;
case "1": break; case "2": break; case "3": Environment.Exit(0); break; }
} }
}
public class Club { private String _name; private int _games; private int _wins; private int _draws; private int _defeats; private int _goals; private int _goaldifference; private int _points; private int _rank;
public Club(int rank, String name, int games, int wins, int draws, int goals, int goaldifference) { _rank = rank; _name = name; _games = games; _wins = wins; _draws = draws; _defeats = _games - _wins - _draws; _goals = goals; _goaldifference = goaldifference; _points = _wins * 3 + _draws; }
public int Rank { get { return _rank; } set { _rank = value; } } public int Points { get { return _points; } set { _points = value; } } public int Games { get { return _games; } set { _games = value; } } public int Wins { get { return _wins; } set { _wins = value; } } public int Draws { get { return _draws; } set { _draws = value; } } public int Defeats { get { return _defeats; } set { _defeats = value; } } public int Goals { get { return _goals; } set { _goals = value; } } public int Goaldifference { get { return _goaldifference; } set { _goaldifference = value; } } public String Name { get { return _name; } set { _name = value; } } }
public class ProgramLogic { private Club[] _clubs;
public ProgramLogic(int [,] Infos) { for (int i = 0; i < 18; i++) { _clubs[i] = new Club(Infos[i, 0], Convert.ToString(Infos[i, 1]), Infos[i, 2], Infos[i, 3], Infos[i, 4], Infos[i, 5], Infos[i, 6]); } }
public String[] generate_table() { String[] Table_line = new String[18]; for (int i = 0; i < 18; i++) { Table_line[i] = Convert.ToString(_clubs[i].Rank + " " + _clubs[i].Name + " " + _clubs[i].Games + " " + _clubs[i].Wins + " " + _clubs[i].Draws + " " + _clubs[i].Defeats + " " + _clubs[i].Goals + " " + _clubs[i].Goaldifference + " " + _clubs[i].Points); } return Table_line; } } |
Es sei angemerkt, dass ich weiss, dass das Programm ohne GUI kaum Sinn hat, jedoch geht es mir nur um das richtige Modellieren eines Projektes!
Mein Anliegen wäre:
(1) Gäbe es weitere (schönere) Modellierungsideen?
(2) Nun zur Menüoption [1] Tabelle erstellen.
Ich hätte mir gedacht, dass der Benutzer es so eingibt (ich simuliere mal das Program):
Quelltext 1: 2: 3: 4: 5: 6: 7:
| Geben Sie bitte folgende Daten in richtiger Reihenfolge an, getrennt von einem ",". Beginnen Sie mit Rang 1 und beenden Sie mit Rang 2. Rang,Name,Spiele,Gewinne,Unentschieden,Tore,Tordifferenz
1,FC Bayern,34,23,33,22, 33 2,BVB,34,22,11,11,34 ... 18,ddd,34,22,11,44,55 |
Diese Daten, welcher der User eingibt, soll in einem mehrdimensionalen Array gespeichert werden. Dieses mehrdimensionale Array wird dem Konstruktor der Klasse ProgramLogic als Übergabeparameter übergeben.
So dieses Array würde so aussehen:
Array = [["1", "FC Bayern", "34", "23", "33", "22", "33"], ["2", "BVB", "34", "22", "11", "11", "34"], ... , ["18", "ddd", "34" , "22", "11", "44", "55"]];
Der Konstruktor von ProgramLogics sieht so aus:
C#-Quelltext 1: 2: 3: 4: 5: 6:
| public ProgramLogic(int [,] Infos) for (int i = 0; i < 18; i++) { _clubs[i] = new Club(Infos[i, 0], Convert.ToString(Infos[i, 1]), Infos[i, 2], Infos[i, 3], Infos[i, 4], Infos[i, 5], Infos[i, 6]); } } |
Ich freue mich über eine ausführliche Antwort. Vielen, vielen Dank bisher. Ich habe wirklich sehr viel Recherche und Mühe in dieses Projekt gesteckt (bisher).
Danke!
Machiavelli
|
|
Yankyy02
      
Beiträge: 138
Erhaltene Danke: 21
Win 11 x64
C# (VS 2022 - Rider)
|
Verfasst: Mo 02.06.14 20:25
Hallo Machiavelli,
ich kann deine Aussage
Zitat: | Das geht so nicht. |
nicht nachvollziehen. In allen 3 Quellen die du angegeben hast ist es genau so beschrieben wie von TH69 "gefordert". Lösche deine private Felder und ersetze sie jeweils folgendermasen.
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
| internal class Club { public int Rank { get; set; } public string Name { get; set; } public int Games { get; set; } public int Wins { get; set; } public int Draws { get; set; } public int Defeats { get; set; } public int Goals { get; set; } public int GoalDifference { get; set; } public int Points { get; set; }
public Club(int rank, String name, int games, int wins, int draws, int goals, int goaldifference) { Rank = rank; Name = name; Games = games; Wins = wins; Draws = draws; Defeats = games - wins - draws; Goals = goals; GoalDifference = goaldifference; Points = wins * 3 + draws; } } |
Wenn du Visual Studio verwendest kannst du über Eingabe von prop Tab Tab dir den Rumpf automatisch generieren lassen.
MfG
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mo 02.06.14 20:37
Hallo,
bei den automatischen Eigenschaften mußt du diese natürlich auch benutzen (und die eigens angelegten privaten Felder löschen):
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| public class Club { public Club(string name, int games, int wins, int draws, int goals, int goaldifference) { Name = name; Games = games; Wins = wins; }
public string Name { get; private set; } public int Games { get; private set; } public int Wins { get; private set; } } |
Der hauptsächliche Vorteil ist, daß es jetzt kürzer und damit lesbarer ist.
Und nun zum Einlesen der Daten in ein Array (bzw. einer Liste). Da die Eingabedaten nur als String vorliegen, mußt du diese erst parsen. Hierfür bietet sich die String.Split-Methode an (mit dem Komma als Trennzeichen) und zurück erhältst du dann ein string[] (also ein String-Array).
(Ein int-Array - so wie du es bisher angedacht hast - kann nicht funktionieren, da ein String, wie z.B. der Name, nicht aus einem int erzeugt werden kann  )
Ich würde dir aber vorschlagen gleich die Daten aus einer Datei einzulesen (damit der User diese nicht jedesmal von Hand eingeben muß bzw. du selber beim Testen(!)). Dafür kannst du dann einfach File.ReadAllLines verwenden (bzw. ab .NET 4 auch File.EnumerateAllLines). Wenn du bisher noch nicht mit Dateien gearbeitet hast, dann empfehle ich dir mal
[Tutorial] Dateioperationen: Alles über Dateien 2.2 (das Forum kennst du ja  )
Und dann noch ein paar Hinweise zur Modellierung (bzw. deinem bisherigen Code):
Das wichtigste bei der Modellierung ist die Trennung von Funktionalität (als Stichwort: "Separation of Concern"), insbesondere die sog. Schichtentrennung ([G]UI, Logik und Datenzugriff).
Daher ist dein jetziger Ansatz mittels der ProgramLogic-Klasse auf jeden Fall schon mal deutlich besser als der im myCSharp-Forum gepostete Code).
Aber du scheinst noch ein bißchen mit den Daten (und den Datentypen) durcheinander zu kommen, denn der Datenfluß ist bisher eigenartig:
Quelltext 1:
| string (text) -> int[,] -> string[] -> Club |
Wie schon oben geschrieben, kann das so nicht funktionieren, sondern es sollte so sein:
Quelltext 1:
| string (text) -> string[] -> Club |
Du mußt jetzt ersteinmal schauen, daß deine gesamte Programmfunktionalität korrigiert wird. Ich empfehle dir dazu die Top-Down-Methodik, d.h. versuche ersteinmal das Grundgerüst der Klassen und Methoden zu erzeugen (ohne großen Code-Inhalt), aber inklusive Aufrufe (evtl. ersteinmal mit Dummy-Parametern), so daß es ersteinmal kompiliert. Und dann baust du nach und nach die Funktionalität in die einzelnen Methoden ein.
PS: Einzelne Stichworte solltest du dann mal bei der MSDN oder Wikipedia etc. nachlesen.
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Machiavelli 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mo 02.06.14 21:04
Bevor ich mich richtig an die Arbeit "schmeisse", wollte ich mich bei euch allen bedanken. Ich werde mir in Ruhe das Tutorial hier durchlesen und mich mit Parsing eines Strings (mit Hilfe meines Buches) bemühen. Dazu, werde ich in den nächsten Tagen mein Projekt besser modellieren (ich denke , die Klasse Club ist mir bereits gut gelungen, ich muss , wie Du schon sagtest, dies beachten: "Trennung von Funktionalität". Ich werde diesen Begriff ergoogeln müssen.) Ich werde mich um einen besseren Datenfluss kümmern.
Vielen, vielen Dank für das Feedback.
|
|
Machiavelli 
Hält's aus hier
Beiträge: 7
|
Verfasst: Di 03.06.14 18:45
Hallo,
hier bin ich schon wieder. Habe weitere Studen in das Projekt hineingesteckt und habe Erfolge erzielt (zumindestens meiner Meinung nach).
Heute habe ich mich vor allem mit der Schichtentrennung | Trennung der Funktionalität und den Datenfluss beschäfitgt (intensivst!). Ich habe mir jetzt eine ToDo-Liste erstellt, die in etwa so aussieht:
Meine zwei Hauptquellen zum Thema Schichtentrennung waren folgende:
Um den Datenfluss zu verbessern, habe ich all die Typen in der Klasse Club auf String umgeändert, das Restliche dann auch noch angepasst.
Ich habe das Programm in folgende Schichten getrennt: UI, ProgramLogic, DataAccess, Club
All diese Schichten repräsentiere ich in vier Klassen.
Ein Diagramm dazu findet man hier:
Der Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Bundesliga { class Program { static void Main(string[] args) { UI UserInterface = new UI(); UserInterface.generate_menu(); UserInterface.enter(Console.ReadLine()); } }
}
internal class Club { public string Rank { get; set; } public string Name { get; set; } public string Games { get; set; } public string Wins { get; set; } public string Draws { get; set; } public string Defeats { get; set; } public string Goals { get; set; } public string GoalDifference { get; set; } public string Points { get; set; }
public Club(string rank, string name, string games, string wins, string draws, string defeats, string goals, string goaldifference, string points) { Rank = rank; Name = name; Games = games; Wins = wins; Draws = draws; Defeats = defeats; Goals = goals; GoalDifference = goaldifference; Points = points; } }
internal class ProgramLogic { private Club[] _clubs;
public ProgramLogic(string [,] Infos) { for (int i = 0; i < 18; i++) { _clubs[i] = new Club(Infos[i, 0], Infos[i, 1], Infos[i, 2], Infos[i, 3], Infos[i, 4], Infos[i, 5], Infos[i, 6], Infos[i,7],Infos[i,8]); } }
public string[] generate_table(int length) { string[] Table_line = new string[length]; for (int i = 0; i < 18; i++) { Table_line[i] = _clubs[i].Rank + " " + _clubs[i].Name + " " + _clubs[i].Games + " " + _clubs[i].Wins + " " + _clubs[i].Draws + " " + _clubs[i].Defeats + " " + _clubs[i].Goals + " " + _clubs[i].GoalDifference + " " + _clubs[i].Points; } return Table_line; }
public void create_table() { }
public void change_table() { } }
internal class UI { private ProgramLogic _logic;
public void enter(String Eingabe) { switch (Eingabe) { case "[0]": look_at_table(); break;
case "[1]": break; case "[2]": break; case "[3]": Environment.Exit(0); break; } } public void generate_menu() { String[] menu_items = new String[4] { "[0] Tabelle ansehen", "[1] Tabelle erstellen", "[2] Tabelle ändern", "[3] Exit" }; for (int i = 0; i < 4; i++) { Console.WriteLine(menu_items[i]); } Console.ReadKey(); }
public void look_at_table() { string[] lines = _logic.generate_table(18); for (int i = 0; i < 18; i++) { Console.WriteLine(lines[i]); } } }
internal class DataAccess { } |
Das nächste, was ich machen werde, ist mich im Thema Stringoperationen und Dateioperationen einzulesen. Ich würde mich um ein Feedback über meinen bisherigen Fortschritt freuen.
Vielen Dank bisher!
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Di 03.06.14 19:17
Hallo,
gegenüber deinem ersten Code (im anderen Forum) sieht das ja schon richtig gut aus.
Ein Sache solltest du jedoch wieder rückgängig machen, nämlich das Setzen aller Club-Eigenschaften als string. Du hattest vorher doch schon Convert.ToString geschrieben gehabt - für string nach int gibt es analog dazu Convert.ToInt32 (noch besser wäre jedoch Int32.TryParse!) - am besten du schreibst dir eine eigene Methode dafür, welche du dann im ProgramLogic-Konstruktor (bzw. meiner Meinung nach sogar als eigenständige Methode) dafür aufrufst (so eine Methode fällt dann technisch gesehen unter den Begriff "Validierung von (Eingabe-)Daten"), denn du kannst ja nicht sicher sein, daß im String auch wirklich nur zu einer Zahl konvertierbare Zeichen, d.h. Vorzeichen + Ziffern, enthalten sind).
PS: Der ersten Quelle bzgl. "partial classes" kann ich jedoch nicht zustimmen (die anderen beiden Quellen sind aber in Ordnung), denn getrennte Funktionalität sollte auch in getrennte Klassen (mit unterschiedlichen Namen). Partielle Klassen sind in C# eigentlich nur zur Trennung von eigenem und generiertem Code (VS-Designer) eingebaut worden.
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Di 03.06.14 20:28
Deinem ProgramLogic Konstruktor übergibst du ein 2D Array mit beliebigen Dimensionen im Konstruktor gehst du dann aber zwingenden von 18*9 aus das wird in vielen Fällen knallen.
So wie du das im Moment machst hast du das Einlesen der Daten unglücklich in 2 Teile geteilt. Einmal das Einlesen der Datei und Umwandlung in in 2D string Array. Dann das Umwandeln des 2D string Arrays in die Club Liste. Der Zwischenschritt mit dem string Array kompliziert das unnötig, insbesondere da du auch XML beim Datenzugriff dran geschrieben hast. Nur eine simple csv artige Plaintext Datei läßt sich einfach in ein string Array wandeln. Danach solltest du dich eher nicht richten.
Das Einlesen und Erzeugen eines Club Arrays oder List<Club> sollte die Aufgabe einer Klasse, Methode sein. Dieses fertige Club Liste solltest du dann in die ProgramLogic Klasse schieben. Falls du das bisher anders machst weil du irgendwas von Geheimnisprinzip in deinen Quellen gelesen hast und die Club Klasse in der ProgramLogik verstecken wolltest dann ist das hier fehl am Platz. Club ist eine Datenklasse die man nicht genau einer Schicht zuordnen kann sondern etwas das sich zwischen den Schichten (zumindest 2en) bewegt. Damit das funktioniert sollte die öffentlich sein. Da die Schichten oft in verschiedenen Assemblies stecken sollte die auch nicht nur internal sein sondern tatsächlich public.
Und nebenbei das ist c# und nicht Java. Hier tummeln sich vor allem C# Programmierer. Die sich am leichtesten tun wenn du dich auch an die entsprechenden üblichen Styleguides hältst. Also zumindest Namespaces, Klassen, Methoden, Properties in Großbuchstaben beginnen und Felder, Parameter in klein. Ja wir können das hier alle bestimmt adaptieren aber es irritiert trotzdem da es unserer Konditionierung widerspricht.
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Machiavelli 
Hält's aus hier
Beiträge: 7
|
Verfasst: Mi 04.06.14 15:00
Hallo,
Zitat: | Deinem ProgramLogic Konstruktor übergibst du ein 2D Array mit beliebigen Dimensionen im Konstruktor gehst du dann aber zwingenden von 18*9 aus das wird in vielen Fällen knallen. |
Ja, denn die Elemente im 2D-Array haben eine feste Anzahl, siehe hier:
Array_2D = [ [ rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte ] , [ rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte ] , ... ]
Zitat: | So wie du das im Moment machst hast du das Einlesen der Daten unglücklich in 2 Teile geteilt. Einmal das Einlesen der Datei und Umwandlung in in 2D string Array. Dann das Umwandeln des 2D string Arrays in die Club Liste. Der Zwischenschritt mit dem string Array kompliziert das unnötig, insbesondere da du auch XML beim Datenzugriff dran geschrieben hast. Nur eine simple csv artige Plaintext Datei läßt sich einfach in ein string Array wandeln. Danach solltest du dich eher nicht richten. |
Ich habe mir das neu überlegt:
Datenspeicherung erfolgt via textfile (.txt), d.h. das Textdokument soll in etwa so aussehen:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte rang, name, spiele, gewinne, niederlagen, remies, tore, tordifferenz, punkte ... |
Mit Hilfe von String Parsing (Durch , werden die Daten getrennt) speichere ich diese Daten im zweidimensionalen Array. Im Menüpunkt "[1] Tabelle erstellen" sollen also dann folgende Optionen erscheinen können:
[0] Tabelle hier erstellen
[1] Tabelle von Datei einlesen
Zitat: | Das Einlesen und Erzeugen eines Club Arrays oder List<Club> sollte die Aufgabe einer Klasse, Methode sein. |
OK, das wird einer meiner nächsten Punkte werden.
Zitat: | Damit das funktioniert sollte die öffentlich sein. Da die Schichten oft in verschiedenen Assemblies stecken sollte die auch nicht nur internal sein sondern tatsächlich public. |
Verstanden & Gemacht.
Zitat: | Und nebenbei das ist c# und nicht Java. Hier tummeln sich vor allem C# Programmierer. Die sich am leichtesten tun wenn du dich auch an die entsprechenden üblichen Styleguides hältst. Also zumindest Namespaces, Klassen, Methoden, Properties in Großbuchstaben beginnen und Felder, Parameter in klein. Ja wir können das hier alle bestimmt adaptieren aber es irritiert trotzdem da es unserer Konditionierung widerspricht. |
OK, ich habe dazu folgende Quellen gefunden:
Zitat: |
Ein Sache solltest du jedoch wieder rückgängig machen, nämlich das Setzen aller Club-Eigenschaften als string. Du hattest vorher doch schon Convert.ToString geschrieben gehabt - für string nach int gibt es analog dazu Convert.ToInt32 (noch besser wäre jedoch Int32.TryParse!) - am besten du schreibst dir eine eigene Methode dafür, welche du dann im ProgramLogic-Konstruktor (bzw. meiner Meinung nach sogar als eigenständige Methode) dafür aufrufst (so eine Methode fällt dann technisch gesehen unter den Begriff "Validierung von (Eingabe-)Daten"), denn du kannst ja nicht sicher sein, daß im String auch wirklich nur zu einer Zahl konvertierbare Zeichen, d.h. Vorzeichen + Ziffern, enthalten sind).
|
Hmm, ich habe alle auf String gesetzt, um den Datenfluss zu korrigieren. Kannst Du mir genau erläutern, warum ich es rückgängig machen soll? Nach meinem Verständnis ist String die bessere Wahl, denn ich gebe ja diese Daten aus (auf dem Bildschirm), und führe keine Rechenoperationen durch.
Unter Datenvalidierung fand ich dies hier und verstehe es.
Vielen Dank für alles. Ich will dieses Projekt dazu nutzen, um mein Durchhalte - / Recherche - "vermögen" zu verbessern. Dazu möchte ich gerne erfahren, wie man ein Projekt richtig modelliert (Trennung der Funktionalität, Schichtentrennung, etc.). Ich hoffe, dass ich in den nächsten Projekten nicht mehr so viel Hilfe beanspruchen muss. Aber: Übung macht den Meister.
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mi 04.06.14 15:22
Hallo,
bzgl. der Club-Eigenschaften hast du doch im ersten Beitrag hier schon die m.E. richtigen Datentypen verwendet. Und du willst ja teilweise mit den Werten doch rechnen, z.B. _points = _wins * 3 + _draws; (bei Strings könnte ja alles mögliche angegeben werden).
Und die Ausgabe (bzw. Konvertierung nach string) von Zahlen ist ja kein Problem, da ja alle Datentypen von Object abgeleitet sind, welche die Methode ToString() anbietet.
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Mi 04.06.14 15:56
Zitat: | Hmm, ich habe alle auf String gesetzt, um den Datenfluss zu korrigieren. Kannst Du mir genau erläutern, warum ich es rückgängig machen soll? Nach meinem Verständnis ist String die bessere Wahl, denn ich gebe ja diese Daten aus (auf dem Bildschirm), und führe keine Rechenoperationen durch. |
Wenn du nix anderes tust als die Daten in der Datei platt wieder auszugeben könnte man die auch gleich passend formatiert in die Datei schreiben auf der Konsole ausgeben und sich die gesamte Logik des Programms sparen  Das man nichts mit den Daten macht ist ein absoluter Spezialfall für den es sich gar nicht lohnt irgendwas strukturiertes zu programmieren das man Architektur nennen kann. Um es etwas sinnvoller zu machen entferne aus den gespeicherten Daten vielleicht einmal die Redundanzen. Bei "spiele, gewinne, niederlagen, remies" ist ein Wert redundant und kann weggelassen werden. Und denn Rang kann man genauso aus den Daten berechnen denn brauch man nicht speichern. Da du dann dazu genötigt wirst auch mit den Daten zu arbeiten wird dir schnell auffallen das es ungünstig ist die als string zu hinterlegen.
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Machiavelli 
Hält's aus hier
Beiträge: 7
|
Verfasst: Do 05.06.14 16:59
Hallo,
habe mich wieder an die Arbeit gemacht:
Was ich bisher gemacht habe:
- C# Styleguide meiner Meinung eingehalten
- Alten Konstruktor verwenden
- In der Klasse Programmlogik werden jetzt zwei Parameter übergeben, die zwei Datentypen werden somit voneinander getrennt (Ich bin mir unsicher, ob das so stimmt).
- Klasse Club ist nun öffentlich
Frage:
Zitat: | Das Einlesen und Erzeugen eines Club Arrays oder List<Club> sollte die Aufgabe einer Klasse, Methode sein. |
Ich würde eine Methode in der Klasse ProgramLogic wählen, keine extra - Klasse , oder?
Aktueller Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace Bundesliga { class Program { static void Main(string[] args) { UI UserInterface = new UI(); UserInterface.Generate_menu(); UserInterface.Enter(Console.ReadLine()); } }
}
public class Club { public int Rank { get; set; } public string Name { get; set; } public int Games { get; set; } public int Wins { get; set; } public int Draws { get; set; } public int Defeats { get; set; } public int Goals { get; set; } public int GoalDifference { get; set; } public int Points { get; set; }
public Club(int rank, String name, int games, int wins, int draws, int goals, int goaldifference) { Rank = rank; Name = name; Games = games; Wins = wins; Draws = draws; Defeats = games - wins - draws; Goals = goals; GoalDifference = goaldifference; Points = wins * 3 + draws; } }
internal class ProgramLogic { private Club[] _clubs;
public ProgramLogic(int [,] Infos, string [] vereine) { for (int i = 0; i < 18; i++) { _clubs[i] = new Club(Infos[i, 0], vereine[i], Infos[i, 1], Infos[i, 2], Infos[i, 3], Infos[i, 4], Infos[i, 5]); } }
public string[] Generate_table(int length) { string[] Table_line = new string[length]; for (int i = 0; i < 18; i++) { Table_line[i] = _clubs[i].Rank + " " + _clubs[i].Name + " " + _clubs[i].Games + " " + _clubs[i].Wins + " " + _clubs[i].Draws + " " + _clubs[i].Defeats + " " + _clubs[i].Goals + " " + _clubs[i].GoalDifference + " " + _clubs[i].Points; } return Table_line; }
public void Create_table() { }
public void Change_table() { } }
internal class UI { private ProgramLogic _logic;
public void Enter(string eingabe) { switch (eingabe) { case "[0]": Look_at_table(); break;
case "[1]": break; case "[2]": break; case "[3]": Environment.Exit(0); break; } } public void Generate_menu() { String[] menu_items = new String[4] { "[0] Tabelle ansehen", "[1] Tabelle erstellen", "[2] Tabelle ändern", "[3] Exit" }; for (int i = 0; i < 4; i++) { Console.WriteLine(menu_items[i]); } Console.ReadKey(); }
public void Look_at_table() { string[] lines = _logic.Generate_table(18); for (int i = 0; i < 18; i++) { Console.WriteLine(lines[i]); } } }
internal class DataAccess { } |
Ich weiss nicht, ob das was wird. Ich versuche eure Ideen umzusetzen, gebe mein Bestes, habe aber das Gefühl, dass ich mir selbst, das Leben zu schwer mache. Vom Syntax her denke ich kann ich Sprache mit der Zeit, nur die Modellierung (was eher weniger in Bücher besprochen wird, stimmts?) beherrsche ich noch nicht. Kennt ihr irgendwelche gute Tutorials/Bücher etc.?
|
|
Palladin007
      
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: Do 05.06.14 19:16
Das Problem bei dem Thema ist, dass es keine festen Regeln gibt, die immer stimmen.
Es gibt die sog. Patterns, also Entwicklungsmuster, die für verschiedene Anwendungsfälle verschiedene Möglichkeiten beschreiben, wie ein Problem gelöst werden kann.
Das Problem an diesen Patterns ist aber, dass sie allgemein gehalten sind, also Sprachunabhängig und oft auch nur ein grobes Muster, das dann noch geformt werden muss.
Wie das dann am Ende aus sieht, muss aber der Entwickler entscheiden, wie das geht kann man sich aber nicht anlesen und auswendig lernen.
Die beste Möglichkeit (so mache ich das seid bald einem Jahr) ist, dass du einfach versuchst, so viele Patterns zu kennen und (ganz wichtig) auch wirklich zu verstehen. Letzteres ist nicht unbedingt immer einfach, weil die gerne ziemlich abstrakt gehalten sind.
Auch das Thema CleanCode ist wichtig. Du kannst dir mal den Namen Robert C. Martin merken, er hat den Begriff im Grund geprägt. Von dem haben wir hier ein Buch liegen und meine Kollegen sind ganz großer Fan.
Das sind so die Dinge, die man wirklich lernen kann.
Patterns, Strategien, etc., aber deshalb kannst du kein gutes Programm-Design entwerfen.
Das Beste ist und bleibt dabei einfach Erfahrung, da kommst du nicht drum herum.
Du musst dir aber auch im klaren sein: Es gibt nicht das richtige Design.
Anfangs mag das Design vielleicht richtig klingen, später auch noch, aber dann kommt auf einmal eine große Anforderung und das ganze System fällt wie ein Kartenhaus zusammen.
Es gibt aber so die Faustregel: Wenn du das Programm gut testen kann (Unit-Tests), dann hast fu auf jeden Fall schon mal einiges richtig gemacht.
Dann gibt es auch noch die eine oder andere Regel, wie z.B. das Open-Closed-Prinzip (offen für Erweiterungen, geschlossen für Änderungen), Single Responsibility Principle, KISS (Keep it simple, stupit), ...
Wenn du dazu lange genug im Internet liest, wirst du noch einige Weitere kennen lernen. Eines meiner Lieblings-Patterns ist z.B. das MVVM-Pattern, besonders in Verbindung mit WPF. Dazu dann das typische Schichten-Modell (wo ich jedoch die Datenschicht in zwei Schichten splitte, einmal reine Datenschicht und dann Daten-Zugriff) und die Dependency Injection um große zentrale Teile (Datenbank zum Beispiel) ohne große Änderungen, austauschen kann.
Du kannst ja mal testweise im Kopf durch spielen, was passiert, wenn du dein Programm, so wie es jetzt ist, fertig stellst.
Dann möchtest du auf einmal eine ganz große neue Version raus bringen mit einem riesen neuen Feature. Sagen wir mal, grafische Oberfläche und Datenbank.
Kannst du das ohne große Änderungen in dein Programm einbauen, oder musst di bestehende Komponenten komplett umbauen?
Für diesen Beitrag haben gedankt: Machiavelli
|
|
Machiavelli 
Hält's aus hier
Beiträge: 7
|
Verfasst: Di 10.06.14 09:21
Danke für die Antwort, und zugleich eine Entschuldigung für die Verspätung. Ich hatte in den letzten Tagen viel um die Ohren und kam kaum dazu, mich "weiterzubilden".
Ich habe mir jetzt einiges recherchiert und verstehe jetzt den Hintergrund der Benutzung von Design Patterns. Als kleiner Zusatz weiß ich jetzt auch nun, wer Robert Cecil Martin ist.
Bei mir hakt es jetzt wieder daran, welchen Pattern ich einsetzen soll.
Ich würde vorschlagen, dass ich das Programm jetzt versuche selbstständig fertig zu schreiben, den Code dann hier poste, und wir ihn dann zusammen, falls möglich, durchgehen können. Derweil werde ich mir versuchen Patterns beizubringen, die mir bei der Programmierung helfen können.
Habt Ihr bessere Vorschläge, oder sollen wir alles Schritt für Schritt (wie zuvor) angehen? Ich suche seit Wochen die richtige Idee, die richtige Modellierung solchen Projektes, und ich bin mir sicher, sogar sehr sicher, dass ich dieses Projekt schaffen werde (und das mit einer schönen Idee!)
Ich würde mich um Feedback sehr, sehr freuen.
Vielen Dank,
Machiavelli
|
|
|