Zum Inhalt springen
View in the app

A better way to browse. Learn more.

Fachinformatiker.de

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Whiz-zarD

User
  • Registriert

  • Letzter Besuch

Alle Beiträge von Whiz-zarD

  1. Ich verstehe deine Nummerierung nicht. Zuerst gibst du an, dass die CSV-Datei so aufgebaut ist: Datum, Packstück-Nummer, Auftrags-Nummer, Paketnummer, Bezeichnung, Menge, Lagerplatz Dann sagst du, dass du die siebte Nummer braucht und das wäre nach meiner Zählung "Lagerplatz". Wo kommt denn deine "7. Nummer her"? Von was?
  2. Das virtual könnte man auch weglassen. Ich schreibe es nur aus Gewohnheit hin, weil das NHibernate-Framework dies so möchte. Außerdem bietet es mir beim Testen gewisse Vorteile. virtual bedeutet, dass diese Eigenschaft mittels Vererbung überschreibbar ist. Für gewöhnlich sind Eigenschaften und Methoden in C# nicht überschreibbar. Wenn man in einer vererbten Klasse eine Eigenschaft oder Methode aber überschreiben möchte, so muss sie in der Basis-Klasse als virtual definiert werden. Beispiel: public class A { public virtual string Hallo { get { return "Hallo"; } } } public class B : A { public override string Hallo { get { return base.Hallo + " Welt"; } } } Die Eigenschaft Hallo in Klasse A liefert "Hallo" zurück, während die Klasse B "Hallo Welt" zurückliefert. Die Klasse B überschreibt die Eigenschaft von Klasse A und ruft mit base.Hallo erst mal die Eigenschaft in Klasse A auf und modifiziert diesen Wert. Was ist denn die "5. Nummer jeder Zeile"? Welche Spaltenüberschrift besitzt dann diese Zeile?
  3. Ich nehme mal an, dass das aus der Logistik kommt. Ich kenne mich da mit den Begrifflichkeiten nicht aus. Ich nehme aber mal an, dass Daten aus deiner Firma sind. Die Daten stammen ja mit Sicherheit auch aus einer Datenbank, also muss das Kind bei euch ja einen Namen haben. Frage doch deinen Ausbilder. Spontan und ohne den Kontext zu kennen, würde ich aber behaupten, dass es sich hierbei um eine Kanban-Karte für einen Logistiker handelt, der mit diesen Informationen die Pakete packt. Unabhängig von dem Klassennamen kannst du aber dennoch schon den Inhalt der Klasse füllen: public class Xyz { public virtual DateTime Datum { get; set; } public virtual int PackstueckNummer { get; set; } public virtual int Auftragsnummer { get; set; } public virtual int Paketnummer { get; set; } public virtual string Bezeichnung { get; set; } public virtual int Menge { get; set; } public virtual string Lagerplatz { get; set; } } Für Xyz muss dann noch ein passender Name gefunden werden. Bei PackstueckNummer, Auftragsnummer, Paketnummer und Lagerplatz musst du dann mal schauen, ob die Datentypen richtig sind. Ich kenne die Daten nicht. Kann auch sein, dass Buchstaben erlaubt sind, dann müssen es Strings sein.
  4. Einfacher geht es kaum noch. Ich versuche es aber mal. Du hast eine For-Schleife. Im Kopf stehen folgende Informationen: Die Zählervariable heißt i => int i = 0 i wird mit 0 initialisiert => int i = 0 Die Schleife läuft so lange, wie i kleiner 5 ist => i < 5 Pro Schleifendurchlauf wird i um 1 erhöht => i++ Das bedeutet, du hast 5 Schleifendurchläufe bei jedem Schleifendurchgang wird folgende Logik angewendet: if (i == 3) { result += 10; } else { result += i; } D.h. wenn i gleich 3 ist, dann wird auf result 10 addiert. Ansonaten i. Jetzt gehen wir die Durchläufe Schritt-für-Schritt durch: Durchlauf: i = 0, result = 0 i ist nicht 3, also wird auf result 0 addiert. result ist 0; also: result = result + i = 0 + 0 = 0 Durchlauf: i = 1, result = 0 i ist nicht 3, also wird auf result 1 addiert. result ist 0; also: result = result + i = 0 + 1 = 1 Durchlauf: i = 2, result = 1 i ist nicht 3, also wird auf result 2 addiert. result ist 1; also: result = result + i = 1 + 2 = 3 Durchlauf: i = 3, result = 3 i ist 3, also wird auf result 10 addiert. result ist 3; also: result = result + 10 = 3 + 10 = 13 Durchlauf: i = 4, result = 13 i ist nicht 3, also wird auf result 4 addiert. result ist 13; also: result = result + i = 13 +4 = 17 Beim 6. Durchlauf hätte i den Wert 5 und somit entspricht i nicht mehr der Bedingung, dass es kleiner als 5 ist und bricht die Schleife ab. result ist dann 17 und wird in der Konsole ausgegeben.
  5. Und wo genau liegt das Problem? Gehen wir mal die Schleife durch: i result ------------------------------------ 0 result += 0 => result = result + 0 => result = 0 + 0 = 0 1 result += 1 => result = result + 1 => result = 0 + 1 = 1 2 result += 2 => result = result + 2 => result = 1 + 2 = 3 3 result += 10 => result = result + 10 => result = 3 + 10 = 13 4 result += 4 => result = result + 4 => result = 13 + 4 = 17 Auf result wird pro Schleifendurchlauf immer i dazu addiert. Die Ausnahme ist, wenn der Zähler i bei 3 ist. Dann wird 10 draufaddiert. Der += operator ist nur eine Abkürzung result += i ist das selbe wie result = result + i
  6. Der Code ist nicht vollständig. Der Kopf der For-Schleife ist nicht vollständig. Und was machst du gerade? Bis jetzt sehe ich nur, dass wenn der Zähler i gleich 3 ist, auf das Ergebnis 10 addiert wird, ansonsten 1.
  7. Web-Technologie und C# schließen sich nicht aus. Eine moderne Web-Applikation besteht aus Microservices und wie die Microservices entwickelt wurden, ist dem Browser schnuppe. Das kann jetzt C#, F#, Java, Python, C, C++, Go, Fortran, R oder was auch immer sein. Die Web-Applikation kommuniziert mit dem Server über eine Web-API und holt sich dort die Daten ab. Das HTML-Gerüst wird dann im Browser mittels JavaScript-Frameworks wie z.B. AngularJS oder React clientseitig gerendert. Daher trennt man zwischen Frontend- und Backend-Entwickler. Frontend-Entwickler machen den clientseitigen JavaScript-Kram, während der Backend-Entwickler sich auf dem Server austobt. AngularJS benutzt als Sprache auch TypeScript, was Ähnlichkeiten mit C# aufweist und auch von Microsoft ist. Gerade Microservices bieten die Möglichkeit, sehr flexibel mit der Programmiersprache zu sein. Ist es besser, eine Aufgabe mit F# zu entwickeln, dann nimmt man halt F#. Für eine andere Aufgabe ist C# vielleicht besser. Man legt sich hier also nicht auf eine Sprache fest, sondern man wählt die geeignete Sprache. Das selbe gilt auch für die Datenhaltung. Bei sog. Monolithen ist es oft der Fall, dass man sich für eine relationale Datenbank entscheidet aber vielleicht ist so eine Datenbank für bestimmte Aufgaben nicht flexibel genug. Für bestimmte Microservices nimmt man dann vielleicht eine NoSQL-Datenbank oder vielleicht sogar einen ganzen Hadoop-Cluster, während andere wiederum weiterhin mit einer relationalen Datenbank arbeiten. Was Mobile angeht, sollte man sich nicht fragen, wo dort C# steht, denn C# hat seinen Platz eher im Backend, sondern die Frage, ob es noch Sinn macht, native Apps zu entwickeln? Bei Spielen macht es vielleicht noch Sinn aber bei Apps, die auch als Web-Applikation laufen können? Inzwischen können auch Browser auf die Sensoren zugreifen und auswerten. Der Bedarf an nativen Apps sinkt also meiner Meinung nach. Abgesehen davon gibt es auch noch im Frontend-Bereich Xamarin, womit man Android-, und iOS-Apps nativ mit C# entwickeln kann. Für die iOS-Kompilierung ist aber ein Mac von nöten.
  8. Eine CSV-Datei ist ja auch nichts weiteres, als eine Tabelle. Was für Daten stecken denn in der Datei? Ob die Daten doppelt und dreifach sind, ist ja auch erstmal egal. Ein Problem nach dem anderen.
  9. Suche unter Google nach "coding katas" und versuche sie zu bearbeiten. Eine Liste findest du z.B. hier
  10. Mein Beispiel ist bei weitem nicht perfekt. Ich wollte dich auch nicht entmutigen, sondern einfach zeigen, dass auch erfahrene Entwickler erstmal anfangen und dann den Code stückweise verbessern. Niemand schreibt auf Anhieb guten Code. Man muss aber Erfahrungen sammeln, um ein Gefühl für die Sprache zu bekommen. Mit Erfahrungen merkt man auch, wenn etwas vielleicht nicht ganz so elegant ist und man vielleicht noch mal drüber nachdenken sollte. Ich wollte nur aufzeigen, wie ich an solche Dinge rangehe. Du solltest einfach Mut besitzen, mal etwas auszuprobieren. Kaputtmachen kannst du ja nichts. Wenn du sogar eine quellcodeverwaltung, wie z.B. Git, nutzt, dann kannst du sogar ganze Änderungen wieder rückgängig machen. Du kannst auch Fragen stellen und mit anderen Leuten über dein Code reden. Ich denke, niemand reißt dir deswegen den Kopf ab. Das einzige, was ich erwarte, ist, dass man auch die Ratschläge von anderen annimmt und sie mal ausprobiert.
  11. Um Dinge zu lernen, geht es viel weniger um tolle Projekte, sondern um Techniken zu lernen: O/R-Mapper (Entity Framework, NHibernate) IoC/DI container (z.B. StructureMap, AutoFac) Logging (z.B. SeriLog, NLog) Message Queues (z.B. RabbitMQ) Unittests Microservices Web-APIs OData Linq Oder du optimierst dein Programmierstil. Lerne Clean Code und TDD (Test-driven Development) und verinnerliche die SOLID-Prinzipien. Selbst wenn du eine WinForms-Anwendung gebaut hast, könntest du mit Sicherheit diese noch weiter optimieren. z.B. kannst du versuchen, diese mit dem MVP-Pattern (Model-View-Presenter) umzubauen oder du verwendest das Event-Aggregator-Pattern um die Kommunikation zwischen den einzelnen Forms zu realisieren. Eigentlich gibt es immer was zu optimieren und kann dabei neue und nützliche Dinge lernen. Ich finde, man muss diese Techniken nicht alle bis zur Perfektion verinnerlicht haben. Ich finde, man sollte aber mal davon gehört und ggf. auch ein mal ausprobiert haben. Die Vertiefung einzelner Techniken kommt hinterher, wenn man ein Projekt gefunden hat, was man umsetzen möchte.
  12. Ich habe eine Assistenten-Ausbildung gemacht aber auf einer Schule die, zumindest in Raum Hamburg und Schleswig-Holstein, einen sehr guten Ruf genießt und ich denke auch, dass die Fachinformatiker-Ausbildung noch sehr viel Verbesserungspotenzial besitzt aber es kommt immer darauf an, was man später daraus macht. Ein Studium macht dich auch nicht zu einem Super-ITler, der ein Allround-Genie ist. Zuerst solltest du dir mal überlegen, was du eigentlich möchtest? Möchtest du eher Software entwickeln oder lieber die IT-Infrastruktur am Leben erhalten (ich nenne es einfach mal so)? Beides sind doch sehr unterschiedliche Berufsfelder, auch wenn sie einen gewissen Schnittpunkt haben. Ein FISI wird z.B. wenig mit Java arbeiten und ein FIAE wenig Netzwerke- und Server administrieren. Dann kommt es darauf an, was du aus einem Abschluss machst. Selbst mit einem Studium-Abschluss kannst zu einem Entwickler versauern, der nach kurzer Zeit zu nichts zu gebrauchen ist, wenn man nicht bereit ist, sich weiterzuentwickeln. Falls du dich mehr für Softwareentwicklung begeisterst, brauchst du eigentlich auch weniger Zertifikate. Bei einem Entwickler kommt es eigentlich mehr auf die Projekte an, an denen er mitgewirkt hat und ehrlich gesagt, wüsste ich auch nicht, welche Zertifikate man da überhaupt machen könnte. Ob man die 2.000 € Netto erreicht, hängt auch von der Branche ab, wo man arbeiten möchte. Aus Erfahrung weiß ich, dass man in der Spielebranche kaum die 2.000 € erreichen wird. Dort ist die Bezahlung eher mies. In der Finanz- oder Versicherungsbranche sieht die Welt auch schon wieder ganz anders aus. Die Qualifikation ist also nicht immer entscheidend. Ich arbeite in der Finanzbranche und ich habe dein Wunschgehalt schon in wenigen Jahren erreicht. Auch ohne Zertifikate oder Studium.
  13. Wieso sollte man dich töten wollen? Softwareentwicklung ist nun mal ein Reifeprozess. Niemand liest nur ein Buch und kann gleich wunderbar sauberen Code schreiben. Mein Code sah zum Anfang auch mies aus und selbst Robert C. Martin, der das Buch "Clean Code" geschrieben hat, sagt von sich aus, dass er nicht die Weisheit mit Löffeln gefressen hat und es auch bei seinen Code-Beispielen sicherlich noch Verbesserungspotenzial gibt aber nur durch Ausprobieren lernt man. Du hast schon richtig erkannt, dass man fürs Einlesen der Datei eine eigene Klasse benötigt. Allerdings gehört die Logik nicht in den Konstruktor. Der Konstruktor dient zur Initialisierung der Klasse. Der Name der Klasse sollte auch die Aufgabe widerspiegeln, was die Klasse tut. "DateiEinlesen" ist vielleicht gut, aber geht es vielleicht noch konkreter? Ich weiß, dass es eine CSV-Datei ist. Vielleicht eher CsvReader? Wobei dieser Name auch wieder sehr allgemein ist. In der CSV-Datei steckt ja eine Tabelle. Welche Daten besitzt die Tabelle? Vielleicht kann man der Tabelle einen Namen geben. Eine CSV-Datei ist ja eine Art der Serialisierung. Das Verfahrung um so eine Tabelle in ein Objekt zu überführen, nennt man auch Deserialiserung. Das kann man ja erst mal im Hinterkopf behalten. Zuerst würde ich mir aber erst mal eine geeignete Datenstruktur überlegen. In der CSV-Datei stecken ja Daten. Ich nehme jetzt mal als Beispiel, dass die CSV-Datei Daten zu Personen beinhaltet: Name;Vorname;Geschlecht;Alter Doe;John;Maennlich;38 Also würde ich erst mal eine Klasse für diese Daten erstellen: public class Person { public string Name { get; set; } public string Vorname { get; set; } public Geschlecht Geschlecht { get; set; } public int Alter { get; set; } } public enum Geschlecht { Maennlich , Weiblich } Nun könnte ich mich darum kümmern, eine(!) Datei einzulesen. Ich habe eine Datenstruktur und ich weiß, dass ich eine CSV-Datei deserialisieren muss. Also könnte man die Klasse z.B. PersonCsvDeserializer nennen. In dieser Klasse soll es eine Methode geben, die Deserialize() heißt. Ich verzichte hier jetzt erst mal bewusst auf ein Interface, weil ich denke, dass es für dich bis hier hin schon kompliziert genug ist. Das Interface werde ich später noch mal erklären. Erst mal kümmern wir uns darum, was wir alles brauchen, um eine Datei zu deserialisieren. Was muss die Klasse PersonCsvDeserializer alles wissen, um eine CSV-Datei deserialisieren zu können? Man könnte vielleicht im ersten Schritt auf die Idee kommen, dass die Klasse den Pfad und Dateinamen benötigt. Mit den Informationen aus dem letzten Absatz könnte ein erster Entwurf so aussehen: public class PersonCsvDeserializer { public IEnumerable<Person> Deserialize(string fileName) { // ... } } Als Rückgabewert habe ich IEnumerable<Person> gewählt, weil IEnumerable<T> ein sehr allgemeines Interface ist und einen Enumerator (auf deutsch: Aufzählung; in anderen Sprachen auch Iterator genannt) zur Verfügung stellt, mit dem wir über die Daten iterieren können (mit der foreach-Schleife). Sowohl IList<T>, ICollection<T>, IDictionary<T>, Array und weitere Klassen implementieren dieses Interface und mehr als über die Daten iterieren wollen wir nicht. Wenn wir später damit mehr machen wollen, können wir es leicht mit Linq in eine Collection, List, Array oder auch in ein Dictionary umwandeln. Die Deserialize()-Methode soll also eine Aufzählung von Personen zurückliefern. Normalerweise macht man es anders, aber aus einfachheit behaupte ich mal frech, dass die erste Zeile in der CSV-Datei immer ein Header besitzt. In der Implementierung überspringe ich den Header per Linq mit der Skip()-Methode. Die Deserialize()-Methode soll also folgendes machen: Die Datei lesen Durch die Datenzeilen iterieren Pro Datenzeile ein Person-Objekt erstellen Die Person-Objekte als Aufzählung zurückliefern Der erste Entwurf könnte daher folgendermaßen aussehen: public class PersonCsvDeserializer { public IEnumerable<Person> Deserialize(string fileName) { IList<Person> result = new List<Person>(); foreach (string line in File.ReadAllLines(fileName).Skip(1)) { string[] elements = line.Split(';'); result.Add(new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }); } return result; } } Die Methode macht zwar was sie soll, aber ist sie wirklich übersichtlich? Nicht wirklich. Wir haben hier mehrere Ebenen miteinander vermischt. Wir können also mit dem Refactoring anfangen. z.B. das File.ReadAllLines(fileName).Skip(1) Wofür ist das genau gut? Wenn man den gesamten Kontext kennt, weiß man es zwar aber eigentlich liegt der Code-Abschnitt eine Ebene Tiefer. Es hantiert mit Dateien und hat mit der eigentlichen Aufgabe der Deserialiserung wenig zu tun. Also sollte man diesen Teil in eine separate Methode packen: private IEnumerable<string> ReadDataFromFile(string fileName) { return File.ReadAllLines(fileName).Skip(1); } Somit wandert das Skip(1) in eine tiefere Ebene und interessiert uns in der Deserialize()-Methode nicht mehr. Als nächstes fällt aber auf, dass wir ein String mit Split() in ein Array teilen und aus diesem Array dann die einzelnen Personendaten herausfischen. Diesen Vorgang nennt man auch Parsing. Also könnten wir diesen Teil auch in eine Methode auslagern: private Person Parse(string serializedData) { string[] elements = serializedData.Split(';'); return new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }; } Unsere Klasse sieht dann bis jetzt folgendermaßen aus: public class PersonCsvDeserializer { public IEnumerable<Person> Deserialize(string fileName) { IList<Person> result = new List<Person>(); foreach (string serializedData in ReadDataFromFile(fileName)) { Person person = this.Parse(serializedData); result.Add(person); } return result; } private IEnumerable<string> ReadDataFromFile(string fileName) { return File.ReadAllLines(fileName).Skip(1); } private Person Parse(string serializedData) { string[] elements = serializedData.Split(';'); return new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }; } } Nun ist Deserialize() doch recht gut lesbar. Wir lesen die Daten aus der Datei, parsen die Daten und erhalten ein Person-Objekt, welches wir dann in eine Liste packen und zum Schluss geben wir die Liste zurück. Es gäbe hier noch weiteres Verbesserungspotenzial aber ich belasse es erst mal hierbei. Ein paar Hinweise gebe ich aber noch: Fehler-Handling? Was passiert, wenn z.B. die Datei nicht existiert? Ist das erzeugte Objekt List<Person> wirklich eine gute Wahl? Angenommen, wir haben es mit einer riesigen CSV-Datei (mehrere Gigabytes) zu tun, die größer ist, als unser Arbeitsspeicher. Hier schmeiße ich mal das "yield return"-Schlüsselwort in den Raum. Auch ist das indexierte Zugreifen auf das Array in der Methode Parse() nicht wirklich glücklich gelöst. Was passiert nämlich, wenn mal eine Spalte in der Datei hinzukommt? Dann muss man ja auch den Code anpassen. Das will man aber eigentlich gar nicht. Zu diskutieren wäre auch, ob die Variable fileName nicht doch besser eine Instanzvariable sein sollte, die per Konstruktor reingereicht wird. Es fällt ja auf, dass die Methoden Deserialize() und ReadDataFromFile() den Dateinamen benötigen. Also stellt fileName ja eine gewisse Abhängigkeit dar, die die Klasse benötigt, um arbeiten zu können. Als Überlegung kannst du ja selber mal schauen, wie man mit solchen Situation umgehst. Um später im Hauptpgramm alle Personen zu iterieren könntest du nun folgendes schreiben: static void Main(string[] args) { string sourcePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\blabla"; IEnumerable<string> fileNames = Directory.GetFiles(rootPath, "*.csv"); PersonCsvDeserializer deserializer = new PersonCsvDeserializer(); foreach(string fileName in fileNames) { IEnumerable<Person> persons = deserializer.Deserialize(fileName); foreach (Person person in persons) { // ... } } } Nach dem selben Prinzip, wie bei der PersonCsvDeserializer-Klasse kannst du ja mal überlegen, wie man nun diesen Code refactoren an. Ab hier wird es noch etwas technischer und tiefgreifender. Ich möchte dir noch zwei Techniken zeigen, die du aber erst mal nicht umsetzen brauchst. "Inversion of Control" und "Dependeny Injection" In der Klasse PersonCsvDeserializer fällt auf, dass die Klasse von einer Datei abhängig ist aber die Daten können vielleicht aus einer Datenbank kommen oder wir schreiben die CSV-Daten direkt in eine grafische Oberfläche. Möchte man jetzt für jeden Anwendungsfall eine eigene Klasse schreiben? Eigentlich nicht. Die Abhängigkeit zur Datei muss also aufgelöst werden. Das .Net-Framework bietet ja die abstrakte Klasse TextReader, die so ziemlich alles darstellen kann. Ein Reader, der eine Datei liest oder aus einem TCP-Stream oder aus einer Datenbank, etc. Anstatt also den Dateinamen reinzureichen, könnte man auch ein TextReader reinreichen. Hier mal ein Beispiel, wie so eine Klasse aussehen könnte: public class PersonCsvDeserializer { private TextReader reader; private bool isHeaderSkipped; public PersonCsvDeserializer(TextReader reader) { this.reader = reader; } public IEnumerable<Person> Deserialize() { string serializedData; while ((serializedData = this.ReadNextData()) != null) { Person person = this.Parse(serializedData); yield return person; } } private string ReadNextData() { string serializedData = this.reader.ReadLine(); if (!this.isHeaderSkipped) { this.isHeaderSkipped = true; return this.ReadNextData(); } return serializedData; } private Person Parse(string serializedData) { string[] elements = serializedData.Split(';'); return new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }; } } Die Main-Methode sieht dann so aus: static void Main(string[] args) { string sourcePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\blabla"; IEnumerable<string> fileNames = Directory.GetFiles(rootPath, "*.csv"); foreach(string fileName in fileNames) { using (TextReader reader = File.OpenText(fileName)) { PersonCsvDeserializer deserializer = new PersonCsvDeserializer(reader); IEnumerable<Person> persons = deserializer.Deserialize(); foreach (Person person in persons) { // ... } } } } Zugegeben, in diesem Beispiel ist die Klasse PersonCsvDeserializer etwas komplizierter geworden aber es ist jetzt egal, woher die Daten stammen, solange wir ein TextReader in den Konstruktor schieben. Das reinrechen der Abhängigkeit in den Konstruktor nennt sich auch "Dependeny Injection". In diesem Beispiel habe ich auch das yield return verwendet. Da wir jetzt nur noch maximal den Speicher für ein Person-Objekt verbrauchen, könnte die Klasse eigentlich nun unendlich viele Daten deserialisieren. Ein Problem stellt aber immer noch die Indexierung des Arrays dar aber das überlasse ich jetzt dir. Das Interface Das letzte, was ich noch schreiben wollte, wäre ein geeignetes Interface für den Deserializer. Wollen wir jetzt mehrere Deserializer schreiben oder einen Deserializer als Abhängigkeit in eine Klasse reinreichen, ist ein Interface geeignet, damit es später egal ist, um welchen Deserializer es sich handelt. Man könnte sich ja auch vorstellen, dass die Daten nicht in einer CSV-Datei stecken, sondern in einer XML-Datei. Dafür wäre folgendes Interface recht nützlich public interface IDeserializer<T> { IEnumerable<T> Deserialize(); } Mit diesem Interface könnten wir sogar das hässliche using im Hauptprogramm wieder loswerden. Ich finde, das using stört im Lesefluss. Wir haben ja jetzt eine Klasse, die CSV-Daten aus unterschiedlichsten Quellen von Personen deserialisieren kann. Was hindert uns nun daran, einen weiteren Deserializer zu bauen, der aus Dateien deserialisiert? Beispiel: public class PersonCsvFileDeserializer : IDeserializer<Person> { private string fileName; public PersonCsvFileDeserializer(string fileName) { this.fileName = fileName; } public IEnumerable<Person> Deserialize() { using (TextReader reader = File.OpenText(fileName)) { PersonCsvDeserializer deserializer = new PersonCsvDeserializer(reader); return deserializer.Deserialize(); } } } Das using wurde nach PersonCsvFileDeserializer und somit eine ebene tiefer verschoben. Wenn du Dependecy Injection verstanden hast, dann würde dir auffallen, dass die Zeile PersonCsvDeserializer deserializer = new PersonCsvDeserializer(reader); eigentlich böse ist, da es eine Abhängigkeit darstellt, die wiederum in den Konstruktor gehört. Ich habe sie aber erst mal hier drinnengelassen, weil das sonst wieder bedeuten würde, dass das using wieder ins Hauptprogramm rein müsste. Eigentlich müsste man sich eine Fabrik-Methode ausdenken, die den PersonCsvFileDeserializer zusammenbaut. Die habe ich hier aber weggelassen. Die kannst du dir ja ausdenken. Das Hauptprogramm würde dann so aussehen: static void Main(string[] args) { string sourcePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\blabla"; IEnumerable<string> fileNames = Directory.GetFiles(rootPath, "*.csv"); foreach(string fileName in fileNames) { PersonCsvFileDeserializer deserializer = new PersonCsvFileDeserializer(fileName); IEnumerable<Person> persons = deserializer.Deserialize(); foreach (Person person in persons) { // ... } } } Das wäre doch schon wieder ein Schritt übersichtlicher. Wie du also siehst, haben wir allein nur für das Einlesen von den CSV-Dateien drei Klassen: Person PersonCsvDeserializer PersonCsvFileDeserializer und ein Interface: IDeserializer<T> geschrieben. Man braucht also kein mega großes Projekt, um mehrere Klassen zu schreiben. Es reicht auch schon was ganz einfaches. Man sollte sich immer bewusst machen, dass Klassen immer nur eine Aufgabe machen sollten und Methoden Teilaspekte dieser Aufgabe sind und sie sollten auch nicht mehr machen, als eine Sache. Es macht auch nichts, wenn man zum Anfang Spagetticode schreibt und diesen später nach und nach einem Refactoring unterzieht. Niemand ist perfekt und niemand schreibt perfekten Code. Man fängt also immer erst mal an und arbeitet sich Schritt für Schritt an eine geeignete und saubere Lösung. Selbst meine Lösung ist mit Sicherheit nicht perfekt und ich habe auch nicht die Weisheit mit Löffeln gefressen. Wenn du mein Beitrag richtig verfolgt haben solltest, hast du vielleicht auch gemerkt, dass ich erst mal eine Lösung geschrieben habe und sie dann nach und nach verfeinert und verbessert habe. Das Wissen kommt erst mit Erfahrung und Erfahrung sammelt man nur, indem man es ausprobiert und darüber mit anderen diskutiert. Also trau dich. So, das reicht auch fürs erste. Ich denke, das ist erst mal genug Input.
  14. Glaube mir mal, deine Probleme beruhen darauf, dass der Code so schlecht geschrieben wurde. Es ist äußerst schwierig, dort den Überblick zu behalten. Du sagst ja selber, dass du die Kommentare schreibst, um dich daran zu erinnern, was das soll aber so etwas brauchst du bei einem gut strukturierten Code nicht. Es ist ein trugschluss, wenn man meint, man spart Zeit, wenn man den Code hinfrickelt. Ja, mag sein, dass der initiale Aufwand geringer ist aber der Aufwand kommt bei der Fehlersuche und momentan raubt doch die Fehlersuche viel Zeit. Wenn dir das schon bei so einem kleinen Programm nicht der Sinn von Klassen erschließt, dann wohl gar nicht. Ich habe dir schon Beispiele genannt, wie man hier vier Klassen designen kann, wenn du das dann immer noch nicht verstehst, wann dann? Entweder du willst es lernen oder nicht. Wenn du es lernen willst, dann probiere es einfach aus. Versperr dich doch nicht.
  15. Bei einem using wird Dispose() immer am Ende des Blocks aufgerufen. Beispiel: Wir haben folgenden Code: try { using(TextWriter writer = new TextWriter(...)) { // Business-Logik } } catch(Exception e) { // Exception-Logik } Das wäre das selbe, wie das hier: try { TextWriter writer = new TextWriter(...); try { // Business-Logik } finally { if (writer != null) writer.Dispose(); } } catch(Exception e) { // Exception-Logik } Der Finally-Block wird immer aufgerufen. Komme was wolle. Es ist egal, ob innerhalb der Business-Logik eine Exception geworfen wird oder sie fehlerfrei durchläuft und genau so verhält sich auch das using. Es ruft immer am Ende des Blockes Dispose() auf. Du solltest aber wirklich erst mal die Grundlagen der Programmierung bzw. der Objektorientierung lernen. Ich sehe da einfach nur Schwarz. Du siehst ja selber, dass dein Code einfach unwartbar und unstrukturiert ist. Du solltest dir erst mal Gedanken machen, welche Aufgaben dein Programm erfüllen soll. Wenn ich den Code richtig verstehe, sehe ich da vier Aufgaben: CSV-Dateien einlesen Daten aus dem Internet herunterladen Daten parsen CSV-Dateien schreiben Also sehe ich hier schon mindestens vier Klassen. Du versuchst aber gleich mehrere Aufgaben in einer einzigen Methode zu erledigen und das kann einfach nicht gut ausgehen. Kommentare im Code sind auch fast immer ein Anzeichen dafür, dass der Code unsauber ist. Es gibt eigentlich nur sehr wenige Gründe, warum man ein Kommentar schreiben sollte.
  16. Das Close() sollte man noch zusätzlich aufrufen. Das schließt zwar die Datei aber gibt nicht die Ressourcen frei.
  17. Managed Code ist das, was von der Common Language Runtime (CLR) gemanaged wird. Die CLR ist die Laufzeitumgebung, in der dein C#-Code läuft. In der CLR läuft auch der Gargabe Collector, der eben den Speicher reserviert und wieder freigibt. Unmanaged Code ist das, was außerhalb der CLR läuft. Die Klasse TextWriter ruft also Funktionalitäten auf, die außerhalb der CLR liegen. z.B. wenn die Win32-API aufgerufen wird. Die Win32-API ist mit C++ geschrieben und direkt in Maschinencode kompiliert. Sprachen wie z.B. C/C++ besitzen keinen Gargabe Collector. Der Entwickler muss hier selbst für die Speicherreservierung- und Freigebung kümmern. Daher spricht man hier von "unmagaged Code", weil es keine automatische Unterstützung für diese Aufgabe gibt. Die Klasse TextWriter muss also selbst dafür sorgen, dass der Speicher der darunterliegenden Aufrufe bereinigt wird. Dafür wurde das IDisposable-Interface geschaffen, die die Dispose()-Methode besitzt und aufgerufen werden sollte, wenn man es mit solchen Klassen zu tun hat. Das using ist nur eine syntakische Erleichterung. Folgenden Code: using(TextWriter writer = new TextWriter(...)) { ... } könnte man auch folgendermaßen schreiben: TextWriter writer = new TextWriter(...) try { ... } finally { if (writer != null) writer.Dispose(); } Das using hat nämlich auch die Eigenschaft, dass das Dispose() immer aufgerufen wird, auch wenn eine Exception geworfen wird. Ein using musst du also so oder so verwenden. Es sei denn, du verwendest eine Bibliothek, um die CSV-Dateien zu erstellen, die das alles für dich abnimmt. Wie z.B. CsvHelper. Ob eine Klasse das IDisposable-Interface implementiert kann man aus der Dokumentation entnehmen oder man schaut sich die Definition der Klasse in Visual Studio an. Dafür klickst du einfach mit der rechten Maustaste auf den Klassennamen und wählst "Go To Definition" aus oder drückst F12.
  18. Der StreamReader und StreamWriter implementieren auch IDisposable. Bei solchen Klassen gilt besondere Vorsicht, da man sie im einem using benutzen sollte: using(StreamWriter writer = new StreamWriter(...)) { ... } Klassen, die IDisposable implementieren, besitzen sog. unmanaged code. D.h. sie werden vom Gargabe Collector nicht vollständig weggeräumt. Deswegen implementieren solche Klassen einen eigenen Destruktor, um die Ressourcen wieder freizugeben. Per using wird implizit die Dispose()-Methode aufgerufen, die dann die Ressourcen wieder freigibt. Ich vermute mal, dass dort dein Fehler liegt und du auf eine Ressource (sprich auf eine Datei) zugreifen möchtest, die auf der noch ein Handle liegt. Ich persönlich würde aber nicht so mit Dateien rumhantieren. Ich würde eine geeignete Datenstruktur überlegen und erst mal alles im Speicher machen und dann im letzten Schritt alles als eine Textdatei rauspusten.
  19. Die *.pdb-Datei erzeugt keine Datei. Sie dient lediglich dazu, mehr Debug-Informationen zu bekommen. Beispiel: static void Main(string[] args) { try { throw new NullReferenceException(); } catch (Exception e) { Console.WriteLine(e); } finally { Console.ReadKey(); } } Wenn ich jetzt das Programm ausführe, erhalte ich folgende Nachricht in der Konsole: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei ConsoleApplication3.Program.Main(String[] args) in d:\dokumente\visual studio 2015\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs:Zeile 16. Ich kann also sehen, dass die Exception in Zeile 16 in der Datei Program.cs geworfen wurde und die Zeile ist folgende: throw new NullReferenceException(); Auch sollte man die Meldung der Exception verstehen. Die obere Zeile ist die Art der Exception mit einer optionalen Nachricht. Der Teil: bei ConsoleApplication3.Program.Main(String[] args) Sagt, wo die Exception ausgelöst wurde und der Teil: in d:\dokumente\visual studio 2015\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs:Zeile 16. Ist der Stacktrace. Der Stacktrace kann noch deutlich länger werden, je nach dem wie viele Ebenen der Code absteigt und wieder raufklettern muss, um die Exception zu fangen. Wenn ich jetzt aber die *.pdb-Datei lösche und das Programm starte, erhalte ich nur folgende Meldung: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei ConsoleApplication3.Program.Main(String[] args) Ich sehe also nicht mehr, wo die Exception geworfen wurde, sondern lediglich nur noch die Methode, wo sie ausgelöst wird. Für dich ist also erst mal der Stacktrace wichtig, um zu wissen, wo überhaupt die Exception aufgetreten ist. Das spart auch schon vieles an Rätselraten. Wenn man die Stelle dann kennt, kann man sich überlegen, was eigentlich passiert ist. Ohne den Stacktrace zu kennen, wird das Debugging sehr schwer.
  20. Und trotzdem hast du nicht meine Frage beantwortet: Aus deinem Code entnehme ich dann, dass die Dateien nicht im Netzwerk-Ordner gelesen und gespeichert werden, sondern im Desktop-Ordner? Edit: Fürs lesen und Schreiben von CSV-Dateien gibt es auch fertige Bibliotheken. Da würde ich mir auch nicht mehr die Mühe machen und es selbst implementieren. Den CsvHelper finde ich persönlich nicht schlecht.
  21. Also willst du ein Webcrawler schreiben? Warum nimmst du denn nicht einen? z.B. HTTrack? Die IOException wird auch nicht vom WebClient ausgelöst. Laut Dokumentation werden folgende Exceptions geworfen: Exception Condition ArgumentNullException Der address-Parameter ist null. WebException Der URI-Format durch Kombination von BaseAddress und address ist ungültig. - oder - Fehler beim Herunterladen von Daten. NotSupportedException Die Methode wurde in mehreren Threads gleichzeitig aufgerufen. Dein Problem muss also beim Lesen oder Schreiben der Dateien zu tun haben. Da dieser Teil in deinem Code fehlt, kann ich dazu nichts sagen. Was genau sagt denn die Exception? Wie sieht denn der Stacktrace aus? Wenn die Symbol-Dateien (*.pdb) dabei liegen, bekommt man sogar die Zeilennummer mit, wo die Exception geworfen worde.
  22. Ähm, eine Anwendung soll eine Datei lesen und eine Datei schreiben und beide befinden sich dann im selben Ordner, wie die Anwendung. Wozu dann ein WebClient? Was hast du denn überhaupt vor? Wozu die beiden Schleifen?
  23. Ein paar Ansätze hatte ich hier schon mal erwähnt. z.B. das Üben von DDD, indem z.B. die Schule bzw. der Lehrer als Kunde auftritt oder auch das Üben von TDD mit sog. Code-Katas. Selbst ich treffe mich ab und zu mit ein paar anderen Entwicklern und bearbeiten eine Kata (über eine Meetup-Gruppe). Man kann da sehr viel von den anderen Entwicklern lernen, weil man andere Sichtweisen und Entwicklungsstile kennenlernt. Wenn man gewisse Regeln definiert, dann hat auch jeder mal die Tastatur vor der Nase und nicht nur die dominanten, die eh alles viel besser können. Auch habe ich hier gelesen, dass ein Azubi OOP beigebracht bekommt, indem er in einer WinForms-Anwendung einen Button zum Springen bringt. Was ist denn das für eine Lernmethode? Wieso eine WinForms-Anwendung? Wenn überhaupt, dann sollte man mit einer Konsolenanwendung anfangen, da bei einer Anwendung mit einer grafischen Oberfläche schon zu viel Magie passiert, die im verborgenen bleibt. Um OOP zu lernen braucht man auch keine grafische Oberfläche. Dann sollte man den Azubis erklären, was (abstrakte) Klassen, Interfaces, Generics, etc. sind und dann auch mal ein bisschen unter die Haube schauen und zeigen, wie z.B. eine Liste funktioniert anstatt sich nur mit Arrays zu befassen. Im selben Atemzug kann man vielleicht auch noch erklären, was eine Referenz bzw. ein Zeiger ist. Vielleicht auch die Problematik mit Geldbeträgen erklären, da ich das Gefühl habe, dass selbst vielen Firmen diese Problematik nicht bewusst ist. Letztes Jahr gabs dazu auf der Developer Week in Nürnberg einen Vortrag dazu und dieser Vortrag war sehr gut besucht und viele waren doch sehr erstaunt. Auch sollte man was sinnvolles entwickeln und keine hüpfenden Buttons. In meiner Assistenten-Ausbildung hatten wir im zweiten Semester zur Übung u.a. einen Sudoku-Löser entwickelt. Dafür hatten wir eine Woche zeit. Zum Testen gab uns der Dozent einen Satz an Unittests, die der Löser bestehen musste. Darüber hinaus gab es noch einen zweiten Satz an Unittests, der erst bei der Abgabe zum Einsatz kam. Der beinhaltete "komplexere" Testszenarien. Das zielte eben halt darauf ab, dass wir uns selbst noch Gedanken machen sollten und selbst Unittests ausdenken und auch untereinander austauschen sollten. Entwickelt wurde dann im Pair-Programming und bei der Abgabe wurden dann Fragen zu unserem Code gestellt, um herauszufinden, ob beide tatsächlich den Code verstanden haben oder ob einer nur den Vortänzer macht und den anderen hinterher schleift. Auch benutzte die Schule ein Analyse-Tool, um herauszufinden, ob vielleicht nicht zwei oder mehrere Gruppen den selben Code abgeliefert haben. Der Code wurde dann in einer Quellcodeverwaltung gepflegt. Wie wäre es auch mit einer größeren Projektarbeit bzw. eine Art Hackerspace? Das könnte man sogar mit den FISIs verbinden. Wie wäre es auch, wenn man einen Gastredner in die Schule holt, der einen kleinen Vortrag zu einem bestimmten Thema hält? Erst neulich habe ich ein 3 Stunden Vortrag zu Microservice in Verbindung mit RabbitMQ angeschaut. Man muss ja das Thema nicht gleich verstehen und es muss nicht gleich sofort eine Klausur darüber geschrieben werden aber es hilft schon, wenn man schon von bestimmten Themen was gehört hat, damit man im Bilde ist, was es sonst noch so für Technologien gibt. Das Ganze kann auch freiwillig sein. Da gibt es eigentlich so viele Ideen, wie man den Berufsschulunterricht gestalten könnte aber das größte Problem ist wohl, dass die Lehrer sich selbst damit überhaupt nicht auskennen. Was ich hier manches mal lese, habe ich das Gefühl, dass sie selbst nicht einmal wissen, wovon sie eigentlich reden und nur ein altes Lehrbuch stur abarbeiten. Man müsste wohl erst mal die Lehrer stetig fortbilden, damit sie auch im Bilde sind, was in der Wirtschaft wirklich abläuft bzw. Lehrer so ausbilden, sodass sie ihr Fach auch selbst verstehen. Ich habe ja auch nichts dagegen, wenn die Azubis auch etwas BWL lernen aber ich finde, dass hier einfach die Priorität zu hoch liegt und die eigentliche Arbeit als FIAE/FISI zu kurz kommt. Ja, ich weiß, eigentlich soll der praktische Teil in den Firmen stattfinden aber die Firmen haben oft gar nicht die Möglichkeit die Azubis optimal auszubilden, da sie oft nicht das Know-How besitzen, um einen Azubi ausgiebig zu schulen. Ich finde, Berufsschulen könnten hier echt was bewirken, um einen Blick über den Firmen-Tellerrand zu wagen aber dafür muss das Personal - sprich die Lehrer - fachkundig sein. Wenn man OOP erklären möchte, dann sollte man nicht mit hüpfenden Buttons kommen.
  24. Ja, ein Entwickler bestimmt nicht, wann sich etwas amortisiert hat, oder sich irgendwann etwas lohnt. Er sagt nur, wie komplex die Umsetzung ist und wie lange es dauert. Die Entscheidung der Wirtschaftlichkeit passiert an einer anderen Stelle. Der Entwickler kann mehrere Alternativen aufzählen aber entscheiden, was umgesetzt wird, macht ein anderer. Und hier sehe ich das größte Problem vieler Softwarefirmen: Es wird kein Wert auf Weiterbildung gesetzt. Ich habe es inzwischen schon in so manchen Firmen erlebt, dass man veraltete Techniken einsetzt, weil man sie schon immer eingesetzt hat und neumodischer Schnick-Schnack sowieso Teufelszeug ist und die Firmen verlieren dann den Anschluss, weil die Kunden sich nicht mehr mit einer augenkrebserzeugenden WinForms- oder Java-Desktop-Anwendung zufrieden geben, sondern eine Webanwendung haben wollen, die auch auf Tablets läuft. Keiner möchte eine Software neu programmieren aber wenn man die Software nicht pflegt und einem ständigen Refactoring unterzieht, wird das aber irgendwann passieren. Irgendwann ist auch eine Code-Basis von anno 2000 veraltet und nicht mehr wirtschaftlich, wenn man sie nicht laufend verbessert und erneuert. Fürs Refactoring wird aber oft kein Budget freigegeben, weil der Kunde davon nichts hat. Solche Diskussionen führe ich gefühlt jeden Tag und irgendwann häufen sich die Beschwerden der Kunden, dass die Software fehleranfällig oder langsam ist und die Entscheidungen werden häufig von Leuten getroffen, die es eigentlich hätte besser wissen müssen. Microservices sind derzeit auch so ein Thema: Die wurden nicht eingeführt, weil man es für Hip und Cool fand, sondern sie zwingen einem nach dem SOA-Paradigma zu arbeiten. Das klassische SOA hat einfach nicht funktioniert auch wenn die Idee gut war und heute immer noch ist. Auch bieten die Microservices einen Vorteil, den du als Nachteil genannt hast: Man ist von der Programmiersprache unabhängig. Es ist egal, ob Service A mit Java, Service B mit C# und Service C mit Cobol entwickelt wurde. Man hat die Möglichkeit, eine geeignete Sprache für die Aufgaben zu suchen. In der Praxis wird das zwar selten vorkommen aber die Möglichkeit besteht. Ich habe früher Industrie-Anlagen programmiert. Nein, sie werden nicht alle 3 Jahre gewechselt aber es handelt sich hier auch nicht um eine Standard-Software. Die Software wird pro Anlage geschrieben und angepasst. Somit läuft pro Anlage eine komplett individuelle Software, die über mehrere Wochen optimiert wird. Der Umgang ist ein völlig anderer, als bei einer Standard-Software aber auch hier bleibt die Zeit nicht stehen. Es werden immer neue Techniken entwickelt. IoT und Big Data wird auch in der Industrie immer wichtiger. Das war vor 15 Jahren noch Utopia. Ich arbeite derzeit bei einer Firma, die eine Software für Banken entwickelt. Banken galten immer als sehr konservativ und intern arbeiten sie wie eine Behörde. Da gibt es dann pro Jahr zwei oder drei Zeitfenster, von wenigen Wochen, in der neue Softwareversionen ausgerollt werden dürfen und selbst bei solchen Kunden erleben wir derzeit ein Umdenken. Das Wort "Cloud" durfte man vor einigen Jahren noch nicht mal in den Mund nehmen. Heute ziehen viele Kunden es schon in Erwägung, ihre Daten in die Cloud zu schieben. Von einem Kunden weiß ich, dass sie schon ihre Daten in eine Cloud outsourcen. Auch haben wir schon von dem einen oder anderen erfahren, dass sie ihre eigens entwickelte Desktop-Lösung gegen eine Microservice-Lösung austauschen wollen und wozu das Ganze? Weil die Banken immer schneller reagieren müssen. Nicht, weil der Markt so schnelllebig ist, sondern weil immer mehr Anforderungen, seitens der Regierung oder der EZB, an die Banken gestellt werden, die sie dazu zwingen, schneller zu handeln. Dazu gehören auch die o.g. Excel-Makros, die nun gegen eine revisionssichere Software-Lösung ausgetauscht werden müssen. Das selbe Problem sehe ich auch in der Automobilbranche, wenn sie tatsächlich autonome Fahrzeuge auf den Markt bringen wollen. Softwarefehler müssen schnell erkannt und behoben werden. Wollen sie denn bei jedem Fehler eine Rückruf-Aktion starten, die die Fahrer dazu zwingt, in die Werkstatt fahren zu müssen? Da müssen noch geeignete Softwareverteilungsmechanismen gefunden werden. Da ap­pel­lie­re ich einfach an den gesunden Menschenverstand. Ich habe selbst schon von Informatikkaufmännern Fehlentscheidungen gesehen, die echt nicht mehr feierlich waren und Jobs gekostet haben, was eigentlich nicht nötig getan hätte. Die Frage ist ja auch, lag es am Entwickler oder an den schwammig formulierten Anforderungen. Das Thema diskutiere ich auch gefühlt jeden Tag. Wenn einem Entwickler nicht klar ist, was er da eigentlich macht und wieso, dann ist es eher ein Anzeichen dafür, dass die Anforderungen nicht klar sind und das wiederum ist ein Anzeichen, dass nicht genügend kommuniziert wurde. Viele Firmen arbeiten immer noch nach dem Wasserfall-Modell, obwohl man schon seit 15 Jahren predigt, dass das Wasserfall-Modell in der IT-Branche nun mal nicht funktioniert.
  25. Du kannst es so oft schreiben, wie du willst und trotzdem wird es nicht richtiger. Mag sein, dass es Bestandteil der Ausbildung ist (was ich auch durchaus für angemessen halt), es sollte aber kein Kernbestandteil sein und genau das meine ich, dass die Prioritäten falsch gesetzt werden. Die Softwareentwicklung ist schon ein sehr komplexes Thema und ich finde FIAEler sollten dann nicht noch zusätzlich ein halber Kaufmann werden, denn dafür gibt es schon die Informatikkaufmänner. Viel mehr sollte man sich hier auf den technischen Aspekt konzentrieren und genau das passiert auch in der Wirtschaft, wie ich z.B. schon mit SCRUM angesprochen habe. Mag sein, dass es viele kleine Firmen gibt, wo ein Entwickler dies benötigt aber nur aufgrund solcher Firmen den kaufmännischen Teil als ein Kernbestand der Ausbildung zu machen, halte ich schlichtweg für falsch. Ja, Wow ... Und für diese Erkenntnis braucht man drei Seiten? Es ist bekannt, dass eine Automatisierung schneller ist, als eine manuelle Abarbeitung und somit kostengünstiger. Das kann ich auch in einem Satz formulieren und brauche keine drei Seiten. Und wenn man den kaufmännischen Aspekt weglassen würde, wären bei der 100% Abschlussarbeit dafür 3 Seiten platz gewesen. Für mich sind Datenstrukturen ein Teil der Grundlagen und diese werden konsequent ignoriert. Stattdessen versucht man wohl in den drei Jahren auch den letzen Deppen beizubringen, dass es keine if-Schleifen gibt. Den Azubis wird doch keine Grundlagen beigebracht. Vielfach habe ich schon gesehen, dass z.B. Geldbeträge als eine Fließkommazahl dargestellt werden. Das ist eine Todsünde! In der 100% Abschlussarbeit wird zwar richtigerweise decimal als Datentyp genommen aber ob dies dem Azubi klar war, lässt sich aus der Dokumentation nicht ableiten. Auch werden oft Arraylisten verwendet, obwohl fleißig neue Elemente rangehängt werden. Dass so etwas inperformant ist und dafür eine verkettete Listen genommen werden sollte, weiß keiner. Die Grundlagen sind also mehr, als nur die Syntax. Zu den Grundlagen gehört auch der Verständnis der bekannten Datenstrukturen und so ein Verständnis ist Programmiersprachenunabhängig. Eine verkettete Liste funktioniert unter C genauso wie unter C# oder Java. Mit dem Unterschied, dass man sie in C erst mal selbst implementieren muss, während man in C# die List<T> und in Java die LinkedList<T> verwenden kann. DDD ist nicht auf Objektorientierung beschränkt. Genauso gut lässt sich DDD auf prozeduraler oder funktionaler Programmierung anwenden. Und genau das meine ich ja mit "Defizite" und "Ein Blick über den Tellerrand". Nicht jede Firma verwendet DDD, TDD oder klassische Unit-Tests aber eine Berufsschule sollte sich deswegen nicht verschließen, sondern sollte den Azubis so etwas zeigen und auch üben. In meiner Ausbildung als Mechatroniker hatten wir so einen ähnlichen Fall. Ich hatte meiner Klasse zwei Mitschüler, die in ihrer Firma nicht mit SPS-Programmierung, Pneumatik oder Hydraulik in Kontakt kamen. Den Part hat dann die Schule übernommen und genau so sollte es auch bei der Softwareentwicklung sein. Einfach zu sagen: "Gibt es nicht, weil nicht alle Firmen dies einsetzen!" ist falsch. DDD und TDD sind Konzepte, die sich in den letzten Jahren sehr bewährt haben. Warum sollte man den Azubis dies nicht beibringen wollen? Der Weisheit letzter Schluss ist das mit Sicherheit noch lange nicht. Sicherlich sind auch irgendwann diese Konzepte überholt und gegen andere ausgetauscht aber wir leben im Jetzt und Jetzt haben sie eine große Bedeutung. Vor 15 Jahren hatten Pflichten- und Lastenhefte noch eine große Bedeutung. Heute schon lange nicht mehr und dennoch tut die IHK so, als wäre dies DAS Prinzip, was auf Ewigkeit bestand hält. Ich habe ja nichts dagegen, dass den Azubis die Objektorientierung beigebracht wird aber man zäumt das Pferd oft von Hinten auf. Ich höre oder lese oft folgendes: "So, jetzt lernen wir Objektorientierung! Startet dafür jetzt alle Visual Studio und öffnet ein neues WinForms-Projekt" ... Das ist schon ein Zeichen für mich, dass man nichts über Objektorientierung lernt sondern nur mit der IDE rumspielt, wenn man schon so hoch in der Abstraktion anfängt. Dass im Hintergrund eine Endlosschleife läuft, die dafür sorgt, dass die grafische Oberfläche immer neu gezeichnet wird und die Events auslöst, wird nicht mal erwähnt. Gern genommen wird auch die allseits beliebte Entwicklung eines Taschenrechners. Aus meiner Sicht für ein Anfänger viel zu kompliziert, wenn man auch die Rechenregel "Punkt- vor Strichrechnung" und Klammersetzung einhalten möchte, denn da sind Konzepte wie z.B. die Polnische Notation oder Binäre Bäume sehr hilfreich. Auch wird dann hier das Konzept der Vererbung benötigt. Für mich sind also schon die angeblich grundlegenden Konzepte falsch gewählt. "Grundlagen" heißt offenbar, dass man die Hälfte verschweigt und den Rest dann nur noch überfliegt. Man muss bedenken, dass man hier Softwareentwickler ausbilden möchte und nicht Tante Emma mal im groben zeigen möchte, wie man programmiert. Daher sollte man den Azubis auch ein Grundverständnis vermitteln, was eigentlich wirklich passiert und wenn man das alles mal berücksichtigt, dann merkt man, dass da überhaupt kein Platz für einen kaufmännischen Teil ist und wenn man endlich diese Erkenntnis erlangt hat, dann ist man wohl endlich auf dem richtigen Weg. Ja, ich beziehe mich nur auf den FIAEler, da die Softwareentwicklung nun mal mein Hauptberuf ist. Zu den FISIs muss ich aber auch sagen, dass ich es für falsch halte, dass die FIAEler und die FISIs sehr häufig die selben Kurse in der Berufsschule haben. Beide sollten zwar Grundlagen in der Programmierung, der Shellskripte, den Datenbanken und der Netzwerktechnik haben aber dann sollten sich schon die Wege trennen. Ein FISI wird nicht großartig irgendeine Software mit Java und einer grafischen Oberfläche, die Daten in Echtzeit auswertet, schreiben und das habe ich bis jetzt noch in keiner Firma gesehen, dass dies erwartet wird. Genauso wird ein FIAEler kaum ein Windows- oder Linux Server mit Active Directory, Firewall, Proxy, DNS, DMZ und sonstigem Kram aufsetzen. Es ist zwar Nett, wenn ein FISI mehr über Softwareentwicklung und ein FIAEler mehr über die Server-Administration weiß aber das ist kein KO-Kriterium, wenn man es nicht weiß.

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.