C# OOP Designfrage

haze4real

Lt. Junior Grade
Registriert
Juni 2009
Beiträge
266
hab mal ne kurze Designfrage bezüglich OOP, ich will Packete aus einem Stream lesen und frage mich gerade welcher von beiden Ansätzen OOP-technisch der bessere wäre:

Variante 1:
Code:
public class Packet
{
  private Packet() { }

  public static Packet ReadFromStream(Stream stream)
  {
    // Packet aus Stream lesen
  }
}

//Aufruf
Packet packet = Packet.ReadFromStream(stream);

Variante 2:
Code:
public  class Packet
{
  public Packet() { }

  public void ReadFromStream(Stream stream)
  {
     // Packet aus Stream lesen
  }
}

//Aufruf
Packet packet = new Packet();
packet.ReadFromStream(stream);

würde mich eigentlich für Variante 1 entscheiden da es ein Packet nicht geben kann ohne dies aus dem Stream gelesen zu haben, oder denke ich da falsch?
 
Variante 1 ist mir spontan sympathischer, weil der Programmierer (z.B. du selbst) der die Klasse verwendet, sich nicht um Initialisierung und dergleichen kümmern muss, sondern stattdessen die entsprechende Methode aufruft und fertig.

Bei Variante 2 sieht es etwas anders aus, weil da zuerst der Konstruktor aufgerufen wird und danach die eigentliche Initialisierung durch die Methode ReadFromStream() erfolgt. Diese 2 Schritte sind notwendig, damit der Rest deines Objektes auch ordnungsgemäß funktionieren kann, oder denkt man automatisch daran alle "150 Properties" und "250 Initialize Methoden" eines Objektes in der richtigen Reihenfolge zu initialisieren bzw. aufzurufen? Und das obwohl man bereits den Konstruktor aufgerufen hat!

Aus OOP Sicht sollte wohl besser der Konstruktor für die Initialisierung des Objektes verwendet werden, sodaß keine "Initialize()" oder ähnliche Methoden danach aufgerufen werden müssen. Bei Variante 2 würde ich demzufolge auf die ReadFromStream() Methode verzichten und stattdessen den Konstruktor um den Stream Parameter ergänzen und darin das Lesen des Packets implementieren. Sollte der Stream kein gültiges Packet enthalten, dann kannst du immer noch entsprechend mit einer Exception die Erzeugung eines "fehlerhaften" Packet Objekts verhindern.

Code:
public  class Packet
{
  public Packet(Stream stream)
  {

     // Packet aus Stream lesen
     // Flags initialisieren
     // Abhängigkeiten prüfen

     if(!bEverythingOk)
       throw new Exception("Paket ungültig");
  }
}
//Aufruf
Packet packet = new Packet(stream);


Hoffe dir hilfts weiter...

Happy coding!
 
Zuletzt bearbeitet:
Also meiner Meinung nach ist Variante 1 besser.

Darf ich fragen, was die Klasse ReadFromStream eigentlich genau macht. Ruft die nur einmal die Funktion Stream.Read mit einem Buffer auf und initialisiert damit das Packet?

Wenn das Paket oft gebraucht wird, dann würde ich die Funktionalität eher in der Klasse Stream implementieren. Ich habe mir da eine abgeleitete Klasse gebaut, die z.B. Funktionen wie WriteString, ReadBytes(Length as Integer) usw. zur Verfügung stellt.
 
Hi haze4real,

verwendet man statische Methode, so sind diese von jeglichen Objekten einer Klasse, die man mit new Klassenname() initialisiert, unabhängig. Deswegen kann man auch z.B. keine nicht-statischen globalen Variablen in solchen Methoden nutzen. Damit kannst Du Dir z.B. eine Util-Klasse erstellen, die dem Nutzer objektunabhängige Funktionalität zur Verfügung stellt. Diese kann man dann bequem und schnell mit der Klassenname.methode()-Notation aufrufen.
Führt die Methode jedoch Operationen(nicht unbedingt da Aufrufen anderer Methoden) auf dem erstellten Objekt aus (etwas unglücklich formuliert, glaube ich), wie z.B. irgendwelche Variablen setzen, so würde sich hier eine "normale" Methode anbieten.
Mal angenommen, Du liest den Stream ein und möchtest, dass das Gelesene auch anderen Methoden deiner Klasse Packet zur Verfügung steht, d.h. Du speicherst es in einer globalen Variable bzw. Member Variable. In diesem Fall arbeitest du auf dem Objekt, zu dem diese aufgerufene Methode gehört.
Klar, man kann alles auf die statische Art und Weise lösen, aber das ist nicht der Sinn von OOP.
Man hat Objekte mit Attributen und Funktionen und möchte mit diesen den Zustand des Objekts ändern.
Also, wenn Du z.B. Werte/Konstanten anbieten willst, die man an vielen Stellen brauchen würde, also auch in anderen Klassen, dann bietet sich static an. Dasselbe gilt für Funktionalitäten wie z.B. einen String von jeglichen Leerzeichen säubern, oder in ein besonderes Format bringen, dass Du in deiner Applikation besser verarbeiten kannst und somit die Methode an vielen Stellen gebrauchen könntest. Statt jetzt überall zuerst neue Objekte mit new Klasse() zu initialisieren und dann die Methode aufzurufen, kann man sofort Klassenname.Methodenname() aufrufen. Ein ganzes Objekt ist in diesem Fall unnötig.
Leider kann ich es vorerst nicht besser erklären (.. und später wahrscheinlich auch nicht :D), hoffer aber, dass es verständlich war.
Naja, falls Fragen auftauchen sollten, dann frag einfach und jemand aus dem Forum oder meine Wenigkeit werden versuchen Dir weiterzuhelfen.

PS Ich implementiere zwar in JAVA, aber ich vermute, dass es auch für C# gilt.
 
PS Ich implementiere zwar in JAVA, aber ich vermute, dass es auch für C# gilt.

@munchkin: da vermutest du richtig. Java und C# nehmen sich da nicht viel.

Ergänzung meinerseits:
In C# werden die Konstanten einer Klasse nicht explizit mit "static" markiert, dennoch verwendet man dann die Notation Klasse.Konstante.

Statische Methoden werden auch verwendet um z.B. sicherzustellen, das von einer Klasse nur ein einziges Objekt erzeugt werden kann (Stichwort Singleton). Alle weiteren Versuche werden dann entweder mit einer Exception abgelehnt oder man gibt stattdessen eine Referenz auf dieses bereits erzeugte Objekt zurück.

Code:
public class EventLog
{
  private static EventLog writer;
  public static GetWriter()
  {
     if(writer==null)
       writer = new EventLog();
     return writer;
  }
  public void Write(string message)
  {
     try
     {
       System.Threading.Monitor.Enter(writer);
       // ... schreibe die Message in einen Stream...
     }
     finally
     {
       System.Threading.Monitor.Exit(writer);
     }
  }
}

// Anwendung in Methode 1 (Thread 1)
EventLog writerLogFile = EventLog.GetWriter();
writerLogFile.Write("Hello World.");

// Anwendung in Methode 2 (Thread 2)
EventLog writerLogFile = EventLog.GetWriter();
writerLogFile.Write("Hello World 2.");

// Es wird stets das gleiche Objekt verwendet, somit gibt es für das Schreiben der Log-Datei nur ein Objekt, 
// das dank Monitor Klasse auch noch threadsicher in die Log-Datei schreibt.


Ebenso können Statische Methoden verwendet werden um z.B. immer Objekte mit definierten Inhalten zu erzeugen, siehe z.B. die Klasse System.Drawing.Color. Diese Klasse hat statische "readonly" Eigenschaften (Sonderform einer statischen Get() Methode) die vordefinierte Farben - wie Red, Green, Blue, Yellow - erzeugen. Einen öffentlich sichtbaren Konstruktor von Color gibt es nicht. Alternativ kann man aber auch über die ARGB Werte ein Color Objekt erzeugen, dafür verwendet man die FromArgb Methode. So kann sichergestellt werden, das der Programmierer nicht irgendwelchen Nonsens zur Erzeugung des Objektes betreibt, z.B. fehlerhafte Werte an den Konstruktor übergibt, die dann in der Konsequenz ein ungültiges Objekt erzeugen. Die Prüfung ob die Initialisierungswerte für ein Objekt gültig sind, kann somit vor der Erzeugung eines Objektes stattfinden, d.h. es wurden noch keine Ressourcen wie z.B. Speicherplatz im RAM reserviert um dann die belegten Ressourcen des gerade im Initialisierungprozess befindlichen Objektes bei Fehlern mühselig vom Garbage-Collector bereinigt werden zu müssen. Ebenso lässt sich ein "statischer" Konstruktor (als Sonderform einer statischen Methode) dazu missbrauchen, Einträge in ein Logfile schreiben zu lassen, bevor die erste Zeile deines eigentlichen Programms ausgeführt wird.
 
Zuletzt bearbeitet:
Zurück
Oben