Dieser Artikel wurde maschinell übersetzt.

Programmiererpraxis

Multiparadigmatisches .NET, Teil 4: Objektausrichtung

Ted Neward

Ted NewardIm Vorherigen Artikel Wir Untersucht Gemeinsamkeiten Und der Streuung Über Prozedurale Programmierung Ausgedrückt Und Einige Interessante “ Schieberegler ” Entdeckt, wird Die Streuung in Entwürfe Entstehen Kann. Insbesondere Ergeben Haben Zwei Vorgehensweisen Für Den Entwurf, Außerhalb der Gedanke Prozeduralen Zeile: Geben Namen Sie Und Verhalten Variabilität Und Algorithmus Variabilität.

Mit der Zunehmenden Komplexität der Programme Und Ihre Anforderungen Wurde Gefunden Entwickler Selbst Die Schwierigkeiten, die Verschiedene Subsysteme Gerade Gewährleistet. Prozedurale Abstraktionen Wir Entdeckten Nicht “ Skalieren ” Auch Wir Haben soll, Könnten. Mit der Einführung der Grafischen Benutzeroberfläche, Ein Neuer Stil der Programmierung Eine Beheben Begonnen Hat Viele Leser, die Das “ Einfache As SDK ” Gelernt Stil Erstellen von Windows-Anwendungen aus Dem Windows-3-SDK Und Charles Petzold Klassische “ Programming Windows ” (Microsoft Press, 1998) Werden Sofort Click. Ostensibly Prozeduralen Natur, Folgen Diese Formatvorlage Ein Besonders Interessante Muster. Jede Prozedur in Einem Eng Gruppierten Knoten des Änderung Funktionen Parameter “ Behandeln ”, die Meisten Häufig Dauert es als Einen Ersten (Oder Einzigen) Parameters Oder Einer Create Aufruf Oder Ähnliches Aufgegeben Zentriert Werden Soll: CreateWindow FindWindow, ShowWindow Und Vieles Mehr, Alle z. B. um Ein Fensterhandle (HWND), Zentriert.

Was Entwickler Zur Zeit Bewusst Nicht War, War Dies Tatsächlich Eine Neue Art der Programmierung, Eines Neuen Paradigmas, Das Sich in Ein Paar Jahren Das Gefühl Machen. Hindsight, Macht es must Offensichtlich, Dass Das Objektorientierte Programmierung War, Und Die Meisten Leser Dieses Artikels Mit Seinen Precepts Und Maßnahmen well-versed Bereits. Möchten Auf der Grundlage, Würde Warum Wir Wertvolle Spalteneinheiten Für Antragsteller Ausgeben? Die Antwort ist Keine Erläuterungen multiparadigm Entwurf Abgeschlossen Wäre, Ohne Die Objekte Innerhalb Seiner Purview Einbinden.

Objekt Grundlagen

Objektorientierung ist in Vielerlei Hinsicht Eine Übung in der Vererbung. Implementierungsvererbung hat Ein Großteil der Objekt-Design-Diskussion Mit Fürsprecher Vorschlagen, Dass Die Richtige Abstraktionen Erstellt Werden, Durch Fields der Entitäten Im System Dominiert – Die “ Substantive ” als es Wurden – Und wo Gemeinsamkeit Hervorgeht, Erhöhen Die Gemeinsamkeiten in Einer Basisklasse deshalb Eine “ IS A ” Beziehung Erstellen. Kursteilnehmer ist Eine Person, Ein Kursleiter ist Eine Person, Ein IS A Personenobjekt usw. Vererbung Haben Entwickler deshalb Eine Neue Achse Auf der Gemeinsamkeiten Und der Streuung Analysieren.

In Den Tagen von C++ der Implementierungsvererbung Ansatz Allein Waren, Aber Wie Zeit Und Erfahrung Fortgeschritten, Schnittstellenvererbung als Alternative Entwickelt. Im Wesentlichen die Einführung von Schnittstellenvererbung in der Toolbox des Designers Für Ein Heller Gewicht Vererbungsbeziehung Deklarieren, Dass Ein Typ ist Ein Anderer Typ, Jedoch Ohne Das Verhalten Oder Die Struktur der des Übergeordneten Typ Zulässig. Schnittstellen finden Einen Mechanismus Für Die Typen von “ Gruppierung ” Entlang Einer Achse Vererbung deshalb Ohne Erzwingen Bestimmten Einschränkungen Für Deren Implementierung.

Betrachten Sie z. B. Im Kanonischen Objektorientierten Wird, Die aus Einer Hierarchie von Geometrischen Formen,, die Gezeichnet (Nur Wenn figuratively) Zum Bildschirm:

    class Rectangle
    {
      public int Height { get; set; }
      public int Width { get; set; }
      public void Draw() { Console.WriteLine("Rectangle: {0}x{1}", Height, Width); }
    }
    
    class Circle
    {
      public int Radius { get; set; }
      public void Draw() { Console.WriteLine("Circle: {0}r", Radius); }
    }

Die Gemeinsamkeiten Zwischen Den Klassen hinzu Schlägt Vor, Dass Eine Übergeordnete Klasse in der Reihenfolge, ist Zum nicht angezeigt werden soll Dieser Gemeinsamkeiten in Jeder Zeichenbare Geometrische Form:

abstract class Shape
{
  public abstract void Draw();
}
  
class Rectangle : Shape
{
  public int Height { get; set; }
  public int Width { get; set; }
  public override void Draw() { 
    Console.WriteLine("Rectangle: {0}x{1}", Height, Width); }
}

class Circle : Shape
{
  public int Radius { get; set; }
  public override void Draw() { Console.WriteLine("Circle: {0}r", Radius); }
  }

So Weit, so Gut – die Meisten Entwickler Würde kein weiterer Problem Mit Bisher Abgeschlossen wurde ist. Leider Warten Liegt in Ein Problem der unwary.

Erneut Liskovsche Schlittenfahrten

Der Haken Wird Das Liskovsche Substitutionsprinzip Genannt: Jeder Typ, der von Einem Anderen Erbt, muss Vollständig Für Die Anderen Austauschbar Sein. Oder Verwenden Sie Die Wörter, die Ursprünglich Das Prinzip behandelt “ Automatisch Eine Eigenschaft Über Objekte provable q(x) X Vom Typ t. If Sollte q(y) Für Objekte y Vom Typ S true Sein, Wobei S sind Sich Ein Untertyp von t. ”

Was Bedeutet, Dass in der Praxis Muss Eine Bestimmte Ableitung der Rectangle, z. B. Quadrat-Klasse Sicherstellen, Dass es die Gleichen Verhalten Garantien Beachtet Werden von der Basisklasse Bereitgestellt Werden. Da es Sich Bei Einem Quadrat Im Wesentlichen Ein Rechteck Mit der Sichergestellt ist, Dass die Höhe Und Die Breite Immer Gleich Sind, Scheint es Sinnvoll, Wie Im Beispiel in Abbildung 1 Quadrat Schreiben.

Abbildung 1 Ableiten von Einem quadratisch

class Rectangle : Shape
{
  public virtual int Height { get; set; }
  public virtual int Width { get; set; }
  public override void Draw() { 
    Console.WriteLine("Rectangle: {0}x{1}", Height, Width); }
}

class Square : Rectangle
{
  private int height;
  private int width;
  public override int Height { 
    get { return height; } 
    set { Height = value; Width = Height; } 
  }
  public override int Width {
    get { return width; }
    set { Width = value; Height = Width; }
  }
}

Beachten Sie wie Eigenschaften Height und Width jetzt virtuelle für jede Art von zufälligen shadowing oder Aufteilen in Slices Verhalten, wenn Sie in der Square-Klasse überschreiben zu vermeiden. So Weit, so Gut.

Danach wird Ein Quadrat in einer Eine Methode Übergeben, die von Einem Rechteck and “ Wächst ” a (Welche Grafiken Computerfreaks Aufrufen Manchmal “ Transformation ”):

class Program
{
  static void Grow(Rectangle r)
  {
    r.Width = r.Width + 1;
    r.Height = r.Height + 1;
  }

  static void Main(string[] args)
  {
    Square s = new Square();
    s.Draw();
    Grow(s);
    s.Draw();
  }
}

Abbildung 2 Zeigt Das Ergebnis des Aufrufs von Diesem Code ist Nicht Wie Erwartet.

Figure 2 A Surprising Result with Grow Code

Abbildung Ein Überraschend Ergebnis Mit Code vergrößern

Das Problem Hier Darauf Achten, Dass der Leser Bereits surmised Haben kann, ist, Dass Jede Implementierung-Eigenschaft Wird Davon, Ausgegangen Isoliert Aufgerufen Wird Und aus diesem Grund Unabhängig Voneinander Fungieren, um Sicherzustellen, Dass die Höhe Hut == Breite Einschränkung Umgehen Quadrat Jederzeit.Jedoch Wird Im Code Vergrößern Davon Ausgegangen, Dass Ein Rechteck Übergeben Wird Wird Und Bleibt Die Tatsache, Dass es Ein Quadrat (als Beabsichtigt!) in Stammen, Und Verhält Sich Vollständig in Einer Weise Vollständig Bekannt, Die Für Rechtecke Geeignet.

Der Unterschneiden des Problems?Quadrate Nicht Rechtecke.Sie Haben Viel Ähnlichkeit, Gewährt, Jedoch am Ende des Tages Nicht Die Einschränkungen Eines Quadrats Für Rechtecke (Die auch True, Die Für Ellipsen and Kreise) Halten, Und Versucht, Modell Hinsichtlich der Anderen Basiert Auf Einem Fallacy.Es ist Verlockend, Quadrat Rectangle, Insbesondere, da es Uns Einige Code Wiederverwenden Kann, ist es Ein false Prämisse Vererben.Tatsächlich Werde Ich Sogar so Weit Gehen Sie als Zu Vorschlagen, Dass Eine Nie Vererbung Verwenden Soll, um Wiederverwendung Zu Fördern, Bis Das Liskovsche Substitutionsprinzip Für Diese Beiden Nachweislich Erfüllt Sein.

In Diesem Beispiel ist Nicht Neu, Robert “ Uncle Bob ” Martin (bit.ly/4F2R6t) Help Liskovsche Und Dabei Genau in der Mitte-Neunzigerjahre Beim Sprechen in C++-Entwickler.Teilweise, Indem Sie die Beziehungen Beschreiben Mit Schnittstellen Einige Derartige Probleme Gelöst Werden Können, Aber Nicht Das in Diesem Speziellen Fall Helfen, da Bleiben, Separaten Eigenschaften Height Und Width.

Gibt es Eine Lösung in Diesem Fall?Nicht Wirklich Nicht, Während Die Beziehung Quadrat-als-Abgeleitet-aus-Rechteck Beibehalten.Die Beste Antwort Kommt Vom Einrichten Einer Direkten Viereckiger Untergeordnete Form Und Vollständig Abkehr von des Vererbung Ansatzes:

 

class Square : Shape
{
  public int Edge { get; set; }
    public override void Draw() { Console.WriteLine("Square: {0}x{1}", Edge, Edge); }
}

class Program
{
    static void Main(string[] args)
    {
      Square s = new Square() { Edge = 2 };
      s.Draw();
      Grow(s);
      s.Draw();
    }
}

Jetzt Haben Wir must Das Problem, Das Quadrat Kann Nicht Übergeben Werden, die Überhaupt Vergrößern, Und es Scheint so Wie Eine Mögliche Beziehung Wiederverwendung von Code. Wir kann dies in Einer Hinsicht Lösen Durch Eine Übersicht Über Das Quadrat als Ein Rechteck Ein Konvertierungsvorgang Verwenden, Wie in der Abbildung 3 .

Abbildung 3 Konvertierungs-Vorgang

class Square : Shape
{
  public int Edge { get; set; }
  public Rectangle AsRectangle() { 
    return new Rectangle { Height = this.Edge, Width = this.Edge }; 
  }
  public override void Draw() { Console.WriteLine("Square: {0}x{1}", Edge, Edge); }
}

class Program
{
  static void Grow(Rectangle r)
  {
    r.Width = r.Width + 1;
    r.Height = r.Height + 1;
  }

  static void Main(string[] args)
  {
    Square s = new Square() { Edge = 2 };
    s.Draw();
    Grow(s.AsRectangle());
    s.Draw();
  }
}

Es Funktioniert – Doch Ein Bisschen Umständlich ist. Die c#-Konvertierung Operator Einrichtung Können Wir Auch Konvertieren Quadrate, Rechtecke, Vereinfacht Wie in Abbildung 4 .

Abbildung 4 Die c#-Konvertierung Operator Einrichtung

class Square : Shape
{
  public int Edge { get; set; }
  public static implicit operator Rectangle(Square s) { 
    return new Rectangle { Height = s.Edge, Width = s.Edge }; 
  }
  public override void Draw() { Console.WriteLine("Square: {0}x{1}", Edge, Edge); }
}

class Program
{
  static void Grow(Rectangle r)
  {
    r.Width = r.Width + 1;
    r.Height = r.Height + 1;
  }

  static void Main(string[] args)
  {
    Square s = new Square() { Edge = 2 };
    s.Draw();
    Grow(s);
    s.Draw();
  }
}

Dieser Ansatz provides Zwar Vielleicht Überraschend Unterscheidet wurde Erwartet Wird, als der Gleiche Clientperspektive Vor, Aber Ohne Die Probleme der Früheren Implementierung Wie -Zeigt Abbildung 5.

Figure 5 The Result of Using the C# Conversion Operator Facility

Abbildung 5 Das Ergebnis der Verwendung der c#-Konvertierung Operator Einrichtung

In der Tat Haben Wir Ein Anderes Problem –, Bevor Die Grow-Methode Geändert, Das Rechteck, Übergeben Wird, Nachdem er Angezeigt Wird, Dass es Nichts, tut, Vor Allem, da er Eine Kopie der Im Quadrat Geändert Wird, Nicht Das Original Viereckiger Selbst. Wir Können Dieses Problem Beheben, Dass die Konvertierung Operator Rückgabe Eine Neue Unterklasse des Rechtecks, Einen Geheimen Verweis Wieder Zu Dieser Instanz Quadrat, Enthält, Dass Änderungen an einer Den Eigenschaften Height Und Width Werden Wiederum Zurück Und Ändern Das Quadrat Edge... Aber Wir Sind Wieder in Das Ursprüngliche Problem!

Keine Happy Hier beenden

Im Hollywood Müssen Filme in Einer Zielgruppe Erwartungen Sitestruktur Beenden Oder Konfrontiert die Sie Im Büro Abgelehnt. Ich Bin kein weiterer Moviemaker, sodass Ich Keine Compulsion Präsentieren die Leser Dieses Artikels in Jedem Fall Eine Glücklich Endung Vertrauen. Dies ist Einer Dieser Fälle: Versuch des Ursprünglichen Codes ein Ort Aufbewahrt, Und Machen Sie Alle Arbeiten Gerade Erstellt Tiefere Und Tiefere Hackerangriffen. Solutions u. Verschieben, Vergrößern Oder Die Transform-Methode Direkt Dem Shape-Hierarchie Oder Einfach Machen Die Grow-Methode Das Geänderte Objekt Zurückgeben Und Nicht Ändern Sie Das Übergebene Objekt (Wir Über, in Einer Anderen Spalte Sprechen Müssen wurde ist), Aber Nicht Wir Kurz Gesagt des Ursprünglichen Codes ein Ort Aufbewahren Und Halten auch Richtig Funktioniert.

Alle dies Soll Genau Eine Sache Zu Präsentieren: Objektorientierte Entwickler Sind Mit der Modellierung der Gemeinsamkeiten Und der Streuung Mit Vererbung, Vielleicht Zu Viel so Vertraut. Daran Sie Daran, Wenn Sie Die Vererbung Achse Verwenden Zum Erfassen von Gemeinsamkeiten Sie Sicherzustellen, Dass Diese Gemeinsamkeit Über die Gesamte Hierarchie Enthält, Wenn Die Geringfügige Probleme Wie Folgt Vermieden Werden.

Beachten Sie Auch, Dass die Vererbung Immer Eine positive Variabilität (Zum Hinzufügen Neuer Felder Oder Verhaltensweisen) ist Und Modellierung negative Variabilität Bei der Vererbung (Das ist, wurde Versucht Quadrat) schnell Immer Ein Rezept Für Notfälle Entlang von Linien Liskovian ist. Stellen Sie Sicher, Dass Alle Auf Vererbung Basierenden Beziehungen Eine positive Gemeinsamkeiten Beinhalten Und Dinge Sollten Gut Sein. Viel Spaß Beim Programmieren!

Ted Neward Ein Principal Mit Neward ist & Associates, Spezialisiert .NET Framework Und Java Plattform Unternehmenssysteme für Eine Unabhängige Fest. Er Hat Mehr als 100 Artikel Geschrieben, Wird Ein c#-MVP Und INETA-Sprecher and Wurde Verfasst Und Mitverfasser von Einem Dutzend Bücher, Unter Anderem “ Professional f-2.0 ” (Wrox 2010). Er Wird Auch Konsultiert Und regelmässig mentors. Erreichen Sie Ihn Unter ted@tedneward.com Bei Fragen Oder Beratung Anforderungen and Gelesen Sie Seinen Blog Unter blogs.tedneward.com -.

Thanks to the following technical expert for reviewing this article: Anthony Green