[OOP] Guter Stil?

Venyo

Lieutenant
Registriert
Dez. 2009
Beiträge
547
Ich habe mehrere Fragen zu gutem Stil bei (objektorientierter) Programmierung anhand des u.g. Beispielprogramms in C#.

  1. Wie überprüft man Variablen vernünftig auf Korrektheit?
  2. Wie benutzt man den Rückgabewert einer Funktion?
  3. Wie überprüft man bei einem Array am elegantesten, ob ein bestimmter Wert enthalten ist?
  4. Was ist der richtige Ort für Variablendeklarationen und Methoden?
  5. Kann man die Lesbarkeit und Wartbarkeit des u.g. Programms verbessern?
  6. Hat jemand einen Link zu einem Kompendium zu gutem Programmierstil o.ä.?

Ich habe mir ein Konsolenprogramm (ich bin noch relativ unerfahren) in C# geschrieben, bei dem diese Fragen aufgetaucht sind. Die beiden Klassen Program.cs und Farzeug.cs stehen ganz unten im Post. Dieses Programm erzeugt ein neues Objekt "MeinAuto" der Klasse Fahrzeug und man kann dann noch die Farbe angeben, in der MeinAuto lackiert werden soll.

Konkretes zu meinen Fragen:


1) Bevor das Fahrzeug lackiert werden kann, soll überprüft werden, ob die vom User angegebene Farbe überhaupt verfügbar ist. Wenn sie nicht verfügbar ist, soll der User darüber informiert werden und erneut aufgefordert werden, eine Farbe anzugeben. Ich habe dafür die Methode "FarbeÜberprüfen" geschrieben. Ist das vernünftig oder sollte die Überprüfung innerhalb der Methode "Lackieren" gehandhabt werden?
Ist das vernünftig, zur Überprüfung die boolean-Variable "farbeverfügbar" zu benutzen oder sollte ich möglichst eine eigene Exception schreiben? Wenn ja, wo im Programm soll ich try-catch benutzen? Ich hatte mal in einem anderen Programm gedacht, es sei das Beste, eine Exception im Setter der int-Variable "Anzahl" abzufangen, weil die Exception dann abgefangen wird, egal an welcher Stelle im Programm die Variable gesetzt wird. Allerdings wurde die Exception schon geworfen, als ich im Programm die Variable "Anzahl" = Convert.ToInt32(Console.ReadLine()) setzen wollte - was ich übrigens nicht ganz verstehe, weil ich dachte, dass Variablen, die einen setter haben, immer erst im setter gesetzt werden. Daher bin ich davon ausgegangen, dass wenn ich den setter mit einem try-catch-Block umschließe, jede entsprechende Exception, die beim setzen dieser Variable geworfen wird, gefangen wird. Ist allerdings anscheinend nicht der Fall. Kann mir das jemand erklären bitte? :baby_alt:

2) Tjo, eigentlich ziemlich grundlegend, aber ich finde ums verrecken nix dazu. In der Methode FarbeÜberprüfen wäre es doch gut, wenn ich true zurückgeben könnte und diese Ausgabe von der Funktion Lackieren() entgegengenommen würde, wenn die Farbe verfügbar ist anstatt die Hilfsvariable farbeverfügbar und break; zu verwenden, oder? Wie geht das?

3) Bei einer Liste kann man ja mit Liste.TrueForAll oder so ähnlich prüfen, ob ein Element dazugehört, oder? Gibt es so eine Funktion nicht für Arrays? Weil an Arrays mag ich in C#, dass man die zugehörigen Elemente direkt beim deklarieren der Array-Variable setzen kann. Will ja mein Programm nicht mit unzähligen Liste.Add(bla) vollschreiben müssen.

4) Ich habe die Variable "angegebeneFarbe" in der Methode deklariert, in der sie verwendet wird. Sollte man das tun oder sollte man sämtliche Variablen immer am Anfang einer Klasse deklarieren?


Program.cs
Code:
using System;

namespace Fahrzeuge
{
    class Program
    {
        static void Main(string[] args)
        {
            Fahrzeug MeinAuto = new Fahrzeug("Toyota Auris", 4);
            Console.WriteLine("Mein Auto ist ein {0} und hat {1} Räder.", MeinAuto.Modell, MeinAuto.RäderAnzahl);

            MeinAuto.Lackieren();
            Console.WriteLine("{0} ist jetzt {1} lackiert", MeinAuto.Modell, MeinAuto.Farbe);


            Console.WriteLine("Zum Beenden Taste drücken.");
            Console.ReadKey();
        }
    }
}

Fahrzeug.cs

Code:
using System;

namespace Fahrzeuge
{
    class Fahrzeug
    {
        public string Farbe;
        private bool farbeverfügbar = false;
        public string Modell { get; protected set; }
        public int RäderAnzahl { get; protected set; }


        public Fahrzeug(string modell, int räderAnzahl)
        {
            Modell = modell;
            RäderAnzahl = räderAnzahl;
        }


        public void Lackieren()
        {
            string angegebeneFarbe;
            Console.WriteLine("In welcher Farbe soll der {0} lackiert werden?", this.Modell);
            angegebeneFarbe = Console.ReadLine();
            FarbeÜberprüfen(angegebeneFarbe);
            if (farbeverfügbar == true)
            {
                this.Farbe = angegebeneFarbe;
            }
            else
            {
                Console.WriteLine("Farbe nicht verfügbar. Bitte eine andere Farbe wählen");
                Lackieren();
            }
        }


        private void FarbeÜberprüfen(string angegebeneFarbe)
        {
            string[] möglicheFarben = /* optional: new string[5] */ { "blau", "gelb", "grün", "rot", "schwarz" };

            for (int i = 0; i <= 4; i++)
            {
                if (angegebeneFarbe == möglicheFarben[i])
                {
                    Farbe = möglicheFarben[i];
                    farbeverfügbar = true;
                    break;
                    //return true;
                }
                else
                {
                    farbeverfügbar = false;
                    //return false;
                }
            }
        }
    }
}


Edit: Syntax Hightlighting ist hier im Forum nicht möglich?
 
Die Felder in Fahrzeug sind noch public. Grundsätzlich ist für die Kapselung hier private empfehlenswert und für alles, was nötig ist mit der Außenwelt zu kommunizieren getter und setter zu definieren, die dann public sind. Manchmal ist auch ein private setter sinnvoll.

Mit c# kenne ich mich leider nicht aus. Aber vielleicht gibt es noch feingranularere Zugriffsteuerungsmechanismen, wie protected etc. in java.

Im Constructor von Fahrzeug auf Gültigkeit von Variablen checken (not null) etc.

Ebenfalls könnte man Validierung an die Setter bauen, ehe die interne Variable modifiziert wird.
 
Vorweg C# ist nicht mein Gebiet

1. Im Prinzip ist es egal. Allerdings ist es in der Funktion Lackieren selber wohl am Klügsten es zu prüfen, also über eine 2. Funktion. Die Funktion FarbeÜberprüfen würde ich so belassen.

2. Einen Rückgabewert gibst du in der Definition der Funkion an
z.B. "private void FarbeÜberprüfen(string angegebeneFarbe)" gibt void zurück was nichts entspricht. Dort einfach entsprechendes hinschreiben. In deinem Fall ist "bool" wohl das was du suchst

3. So wie du es gemacht hast ist es wohl am gängigsten, allerdings weiß ich nicht ob C# dafür noch speziellere Funktionen bereit stellt. Ich würde es so belassen.

4. Nichts, gut und leserlich aus :)

5. Codingstyles sind etwas individuelles, da würd ich mir andere freie quellcodes angucken. Aber viel dürfte da nicht anders sein
 
Moin,

wenn du falsche eingaben vermeiden willst würde ich den user einfach eine Auswahl anzeigen und er kann halt eine nummer eingeben, denn bei manueller eingabe musst ja auch beachten das "blau" != "bLaU" etc., oder eben erst alle zeichen in kleinbuchstaben umwandeln (oder groß, jedenfalls einheitlich).

Habe mit C# nur am rand erfahrung, bin ehr nen Java Mensch (und kann es jedem nur empfehlen ;)), allerdings denke ich kann ich dir trotzdem helfen.

Zu 1.)
Eigene Exceptions.... Rate ich von ab, i.d.r. gibt es für alles schon vorgefertigte und ist bei dir auch nicht sehr sinnvoll da du ja eine Art Menü realisieren willst, eine Fehlermeldung reicht da so wie du sie dir gedacht hast meiner meinung nach vollkommen aus.
Für die Überprüfung der Farbe eine eigene Methode zu implementieren ist keine schlechte Sache da du so in anderen Funktionen oder SubKlassen wieder darauf zurückgreifen kannst.
Ein Setter ist an sich auch kein eigenes Sprach Element, sondern eine funktion.
Ich kenne zwar dieses
Code:
 public int RäderAnzahl { get; protected set; }
nicht, aber in Ruby gibt es etwas ähnliches.
Der Compiler baut da aus diesen anweisungen etwas das in etwa so aussieht:

Code:
//die get Funktion
public int getRäderAnzahl() {
        return this.RäderAnzahl; }

//die set methode
protected void setRäderAnzahl(int i) {
        this.RäderAnzahl = i; }

Einen wert bekommt eine Variable erst wenn irgendwo ein = steht.
public int RäderAnzahl; teielt dem Computer mit das du Speicherplatz für eine Variable es typs String brauchst und reserviert diesen für dich.
Einen wert hat sie dann noch nicht. In Java würde die Variable auf NULL zeigen, also nichts.

Über einen Array zu Iterieren um zu prüfen ob ein Wert enthalten ist, ist die gänige Methode, natürlich könnte man ihn noch indizieren aber das is bei so wenigen Elementen nicht notwendig. Alternativ kannst du dich ja mal mit Collections und Listen vertraut machen ;)

Zu 2.

Rückgabewerte von Funktionen.....
Ich möchte dir nicht zu nahe treten aber evtl. solltest du dich ein wenig grüdnlicher mit der Materie und Programmieren allgemein beschäftigen.

Code:
public void funktion(int param1....)

da ist void der Rückgabewert, sprich die funktion liefert nichts zurück. (engl. void = leer).

Soll eine Funktion nun eine boolschen Wert zurück gegeben muss es wie folgt aussehen:

Code:
public boolean funktion(...)  {
     ..... code.....

     return boolVariable; //Return beendet IMMER eine funktion, was danach kommt wird nicht mehr ausgeführt
(boolean heißts in java, c# k.a. aber bestimmt bool oder so......)

also kannst du die Klassenvariable farbeverfügbar weglassen und die Kommentare deiner returns entfernen.

und die if-Abfrage in Lackieren kannst du dann wie folgt machen

Code:
if (FarbeVerfügbar(angegebeneFarbe))
            {
                this.Farbe = angegebeneFarbe;
            }
            else
            {
                Console.WriteLine("Farbe nicht verfügbar. Bitte eine andere Farbe wählen");
                Lackieren();
            }
dazu sei noch gesagt das ich dieses problem ehr in einer schleife statt mit rekursion lösen würde da sonst x-mal (x = anzahl falscher eingaben) der code nach der if-abfrage ausgeführt werden würde.

die abfrage funktioniert deshalb weil du vorher das void in der funktion deklaration durch ein boolean ersetzt hast, der Computer also weiß das die funktion in jedemfall true oder false zurückgeben wird.

Und zur Variablen deklaration allgemein: WO du variablen erzeugst hängt davon ab wo und wann du sie brauchst (Stichwort Scope). jede Variable ist nur in dem Block gültig in dem sie deklariert wird.

Variablen in einer Funktion sind nur dort gültig und leben auch nur solange die funktion ausgeführt wird, beim verlassen wird der Speicher wieder freigegeben.

Hoffe konnte bissl helfen sonst frag weiter :)

MFG

edit:
nur java jünger hier ? XD
 
Zuletzt bearbeitet:
CoolHandLuke schrieb:
Die Felder in Fahrzeug sind noch public. Grundsätzlich ist für die Kapselung hier private empfehlenswert und für alles, was nötig ist mit der Außenwelt zu kommunizieren getter und setter zu definieren, die dann public sind. Manchmal ist auch ein private setter sinnvoll.
Was sind Felder? Ich kenne nur Namespaces, Klassen, Methoden und Variablen. Falls damit z.B. die public-Variable "Modell" gemeint ist: wenn ich die private mache, kann ich ja nicht mehr von Program.cs drauf zugreifen. Das Setzen der Variable ist ja durch den protected setter ausreichend geschützt, oder?

Im Constructor von Fahrzeug auf Gültigkeit von Variablen checken (not null) etc.
Da habe ich leider keine Ahnung, wie das geht. Könnte mir das noch jemand erklären plz?

Ebenfalls könnte man Validierung an die Setter bauen, ehe die interne Variable modifiziert wird.
Ebenfalls keine Ahnung, was damit gemeint ist, sorry :watt:



gigajum schrieb:
2. Einen Rückgabewert gibst du in der Definition der Funkion an
z.B. "private void FarbeÜberprüfen(string angegebeneFarbe)" gibt void zurück was nichts entspricht. Dort einfach entsprechendes hinschreiben. In deinem Fall ist "bool" wohl das was du suchst
Jop, wie man einen Rückgabewert definiert, weiß ich; allerdings weiß ich nicht, wie ich diesen Rückgabewert dann in der Methode Lackieren() verwende, also wie ich dann anstatt die Hilfsvariable "farbeverfügbar" zu prüfen, stattdessen den Rückgabewert von FarbeÜberprüfen() prüfe. Edit: ok, Mercsen hats erklärt, jetzt weiß ichs^^
Ansonsten bin ich schonmal beruhigt, nicht völligen Murks geschrieben zu haben, danke gigajum :)
Ergänzung ()

Mercsen schrieb:
die if-Abfrage in Lackieren kannst du dann wie folgt machen

Code:
if (FarbeVerfügbar(angegebeneFarbe))
            {
                this.Farbe = angegebeneFarbe;
            }
            else
            {
                Console.WriteLine("Farbe nicht verfügbar. Bitte eine andere Farbe wählen");
                Lackieren();
            }
Vielen Dank!

dazu sei noch gesagt das ich dieses problem ehr in einer schleife statt mit rekursion lösen würde da sonst x-mal (x = anzahl falscher eingaben) der code nach der if-abfrage ausgeführt werden würde.

Mh, könntest du das noch etwas genauer erklären bitte? Ich kann mir gerade nicht genau vorstellen, wo ich die Methode nochmal aufrufen soll.

Ich möchte dir nicht zu nahe treten aber evtl. solltest du dich ein wenig grüdnlicher mit der Materie und Programmieren allgemein beschäftigen.
Das stimmt wohl^^ Wie gesagt stehe ich noch relativ am Anfang. Ich hab allerdings auch einige openBooks über c# gefunden und a) war in keinem ein eigenes Kapitel über Methoden und b) war zwar in jedem erklärt, was ein Rückgabewert ist und wie man return benutzt, aber nicht, wie man dann diesen Rückgabewert weiterbenutzt...

Danke auch für die nicht zitierten Tipps, ich werde mal versuchen, sie umzusetzen.


edit: mir raucht gerade der Kopf etwas, bin schon den ganzen ...wollte gerade Vormittag schreiben, aber jetzt ists ja schon 3 oO... dran, zu überlegen, was ich wo wie machen sollte. Ich mach glaub ich erstmal ne Pause und fang morgen wieder an^^
 
Zuletzt bearbeitet:
Validierung ist allerdings ein deutscher begriff ;)
Einfach auf korrektheit prüfen.

in OOP sagt man zu variablen von Klassen auch gerne Felder.
Der Sinn von OOP ist der das du nicht mehr auf die Fleder einen Objekts zugreifen kannst (Datenkapselung), nur wenn das Objekt dieses expliziot mit einer get und set methode erlaubt.
protected hat auch eine andere eigenschaft als wie du meinst.
Protected ist für Klassenvererbung gedacht, d.h. Wenn du nun eine Klasse MeinSubAuto von MeinAuto ableitest kann MeinSubAuto direkt auf das Feld zugreifen ohne den umweg über eine get/set methode !

Ich würd mir am besten mal nen buch besorgen, dann lernst es gleich richtig statt dir hier und dort ein wenig wissen zusammen zu suchen. ^^

Edit:
Logo kann ich das.

In deinem Falltritt so ein Verhalten nicht auf aber nehmen wir folgendes beispiel:

Code:
public void test(int i) {
     if(i == 10) {
           print("Ende");
           break; //beenden der Funktion 
     } else {
         print("Neuer Aufruf");
         test(i++); }
     print("ende der funktion");

rufst du nun test(0) auf bekommst du folgende ausgabe:

Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
Neuer Aufruf
ENDE
ende der funktion
ende der funktion
ende der funktion
ende der funktion
ende der funktion
ende der funktion
ende der funktion
ende der funktion
ende der funktion
ende der funktion

Versuch am besten mal das selber nach zu vollziehen, ansosnsten gebe ich gerne weitere Hilfe

edit2:
vergessen xD
als scheliefe würde das so aussehen

Code:
bool laufen = true;
while(laufen) {
if (FarbeVerfügbar(angegebeneFarbe))
            {
                this.Farbe = angegebeneFarbe;
                laufen = false;
            }
            else
            {
                Console.WriteLine("Farbe nicht verfügbar. Bitte eine andere Farbe wählen");
            }
}

das die einfachste methode
 
Zuletzt bearbeitet:
Danke :)

Für mein Programm musste ich das etwas anders machen, aber das Prinzip hab ich anwenden können (ICH BIN SO STOLZ AUF MICH!! :volllol:):

Code:
     do
            {
                Console.WriteLine("In welcher Farbe soll der {0} lackiert werden?", this.Modell);
                angegebeneFarbe = Console.ReadLine();

                FarbeÜberprüfen(angegebeneFarbe);

                if (FarbeÜberprüfen(angegebeneFarbe) == true)
                {
                    Console.WriteLine("Farbe wurde akzeptiert.");
                }
                else
                {
                    Console.WriteLine("Farbe nicht verfügbar. Bitte eine andere Farbe wählen");
                }
            } while (FarbeÜberprüfen(angegebeneFarbe) == false);
        }



Allerdings frag ich mich, ob es wirklich sinnvoll ist, den Rückgabewert zu benutzen anstatt einer Hilfsvariablen (bool farbeverfügbar). Jetzt ists nämlich so, dass zwei mal die Farbe überprüft wird, was ja eigentlich Quatsch ist (das 2. Mal am Ende bei "while (FarbeÜberprüfen(angegebeneFarbe) == false"). Kann man den Rückgabewert an der Stelle evtl. benutzen ohne nochmalige Überprüfung?

Als nächstes versuch ich mich anhand des Programms wohl noch kurz an Vererbung^^
 
naja, 3 mal "FarbeÜberprüfen(angegebeneFarbe)" sollte man schon durch eine Booleanvariable ersetzen - schon allein, damit die Funktion nicht 3 mal aufgerufen wird mit immer den gleichen Parametern.

Und nochwas: meiner Meinung nach sind Umlaute schlechter Stil. Sowas gehört verboten ;-) machs lieber alles in englisch.
 
Code:
         Console.WriteLine("In welcher Farbe soll der {0} lackiert werden?", this.Modell);
          angegebeneFarbe = Console.ReadLine();
          while (!FarbeÜberprüfen(angegebeneFarbe)
            { 
                    Console.WriteLine("Farbe nicht verfügbar. Bitte eine andere Farbe wählen");;
                    angegebeneFarbe = Console.ReadLine();  
            }
           Console.WriteLine("Farbe Ok :)");

Eine überprüfung ;)
Einziger "nachteil" ist das man die abfrage 2 mal einprogrammiert hat

und bei bedinungen brauchst du etwas wie == true oder == false nicht schreiben.
Bedinungen werden immer auf true evaluiert, bzw. ein ausrufezeichen vor den ausdruck dann auf false ;)
 
Was mir irgendwie auffällt ist folgendes.
Du hast die Klasse "Fahrzeug" genannt, soll diese Klasse nun eine Basisklasse sein für darauf folgende Klassen? Wenn ja dann ist die Property RäderAnzahl da falsch, denn nicht jedes Fahrzeug hat Räder.
Wenn sie nicht als Basisklasse fungieren soll, warum die Setter der Properties dann protected?
 
Ich denke du solltest ein wenig nachsicht haben, offensichtlich ist er noch recht neu und mit den prinzipien der OOP noch nicht vertraut. Für mich sieht es aus als würde er ersteinmal versuchen die basis konzepte der OOP zu verstehen und programmieren allgemein, sprich schleifen, rückgabewerte etc.
 
Ja.
Vielleicht kam das grober rüber als es sollte.
Die Fragen sollten dem TE ja zum Denken anregen und überlegen ob seine Lösung schon die beste ist. Denn nur aus Fehlern lernt man ;)

Hoffe jetzt war es "freundlicher" :)
Das er Anfänger ist hab schon mitbekommen;)
 
erm ja, sorry ich meints auch nich böse xD
und sry fürs OT i bin nu ruhig !
 
Noch ein Hinweis zu Getter, Setter Methoden... Wenn es später an die GUI Programmierug geht, sind die ein überholtes in .Net.

Stattdessen setzt man auf Properties - dafür gibt es (später relevant) unter anderem PropertyChanged etc. Events.

Neben dem sind die getter/setter Methoden, wenn sie nichts besonderes können müssen einfach durch:

<Hautzugriffsvariante> <Typ> <Name des Property> {<einschränkungA> get; <einschränkungB> set;}
zu setzen.

Bspw.:
public string FahrzeugName {get; private set;}
 

Ähnliche Themen

Zurück
Oben