it-swarm.com.de

Wie wird Polymorphismus in der realen Welt eingesetzt?

Ich versuche zu verstehen, wie Polymorphismus in einem realen Projekt verwendet wird, aber ich kann nur das klassische Beispiel (oder etwas Ähnliches) dafür finden, eine Animal Elternklasse mit einer Methodespeak() zu haben und viele untergeordnete Klassen, die diese Methode überschreiben, und jetzt können Sie die Methode speak() für jedes der untergeordneten Objekte aufrufen, zum Beispiel:

Animal animal;

animal = dog;
animal.speak();

animal = cat;
animal.speak();
17
Christopher

Stream ist ein großartiges Beispiel für Polymorphismus.

Stream repräsentiert eine "Folge von Bytes, die gelesen oder geschrieben werden können". Diese Sequenz kann jedoch aus Dateien, Speicher oder vielen Arten von Netzwerkverbindungen stammen. Oder es kann als Dekorator dienen, der vorhandene Streams umschließt und die Bytes auf irgendeine Weise transformiert, beispielsweise durch Verschlüsselung oder Komprimierung.

Auf diese Weise muss sich der Client, der Stream verwendet, nicht darum kümmern, woher die Bytes stammen. Nur dass sie nacheinander gelesen werden können.

Einige würden sagen, dass Stream ein falsches Beispiel für Polymorphismus ist, da es viele "Funktionen" definiert, die von den Implementierern nicht unterstützt werden, z. B. Netzwerk-Streams, die nur Lesen oder Schreiben zulassen, aber nicht beide gleichzeitig. Oder mangelnde Suche. Dies ist jedoch nur eine Frage der Komplexität, da Stream in viele Teile unterteilt werden kann, die unabhängig voneinander implementiert werden können.

35
Euphoric

Ein typisches spielbezogenes Beispiel wäre eine Basisklasse Entity, die gemeinsame Mitglieder wie draw() oder update() bereitstellt.

Für ein reineres datenorientiertes Beispiel könnte es eine Basisklasse Serializable geben, die eine gemeinsame saveToStream() und loadFromStream() bereitstellt.

7
Mario

In den meisten UI-Toolkits sehen Sie viel Vererbung und Polymorphismus.

Beispielsweise erbt im JavaFX-UI-Toolkit Button von ButtonBase, das von Labeled erbt, das von Control erbt, das erbt von Region, das von Parent erbt, das von Node erbt, das von Object erbt. Viele Ebenen überschreiben einige Methoden der vorherigen.

Wenn diese Schaltfläche auf dem Bildschirm angezeigt werden soll, fügen Sie sie einem Pane hinzu, das alles akzeptieren kann, was als Kind von Node erbt. Aber woher weiß ein Bereich, was mit einem Button zu tun ist, wenn er nur als generisches Node -Objekt angesehen wird? Dieses Objekt kann alles sein. Das Fenster kann dies tun, da der Button die Methoden von neu definiert Node mit einer beliebigen Schaltflächenspezifischen Logik. Der Bereich ruft nur die in Node) definierten Methoden auf und überlässt den Rest dem Objekt selbst. Dies ist ein perfektes Beispiel für angewandter Polymorphismus.

UI-Toolkits haben eine sehr hohe Bedeutung für die reale Welt, sodass sie sowohl aus akademischen als auch aus praktischen Gründen nützlich sind.

UI-Toolkits haben jedoch auch einen erheblichen Nachteil: Sie sind in der Regel sehr groß . Wenn ein Neuling Software-Ingenieur versucht, die internen Abläufe eines gemeinsamen UI-Frameworks zu verstehen, stößt er häufig auf über hundert Klassen , von denen die meisten sehr esoterisch sind Zwecke. "Was zum Teufel ist ein ReadOnlyJavaBeanLongPropertyBuilder ? Ist es wichtig? Muss ich verstehen, was es gut ist zum?" Anfänger können sich leicht in diesem Kaninchenbau verirren. Sie könnten entweder vor Angst fliehen oder an der Oberfläche bleiben, wo sie nur die Syntax lernen und versuchen, nicht zu genau darüber nachzudenken, was tatsächlich unter der Haube passiert.

6
Philipp

Es gibt verschiedene Arten von Polymorphismus, der interessierende ist normalerweise Laufzeitpolymorphismus/dynamischer Versand.

Eine sehr allgemeine Beschreibung des Laufzeitpolymorphismus besteht darin, dass ein Methodenaufruf je nach Laufzeittyp seiner Argumente unterschiedliche Aktionen ausführt: Das Objekt selbst ist für die Auflösung eines Methodenaufrufs verantwortlich. Dies ermöglicht ein hohes Maß an Flexibilität.

Eine der gebräuchlichsten Möglichkeiten, diese Flexibilität zu nutzen, ist Abhängigkeitsinjektion, z. damit ich zwischen verschiedenen Implementierungen wechseln oder Scheinobjekte zum Testen einfügen kann. Wenn ich im Voraus weiß, dass es nur eine begrenzte Anzahl möglicher Auswahlmöglichkeiten gibt, könnte ich versuchen, sie mit Bedingungen fest zu codieren, z.

void foo() {
  if (isTesting) {
    ... // do mock stuff
  } else {
    ... // do normal stuff
  }
}

Dies macht es schwierig, dem Code zu folgen. Die Alternative besteht darin, eine Schnittstelle für diese foo-Operation einzuführen und eine normale Implementierung und eine Scheinimplementierung dieser Schnittstelle zu schreiben und zur Laufzeit in die gewünschte Implementierung zu „injizieren“. "Abhängigkeitsinjektion" ist ein komplizierter Begriff für "Übergeben des richtigen Objekts als Argument".

Als Beispiel aus der Praxis arbeite ich derzeit an einem Problem des maschinellen Lernens. Ich habe einen Algorithmus, der ein Vorhersagemodell erfordert. Aber ich möchte verschiedene Algorithmen für maschinelles Lernen ausprobieren. Also habe ich eine Schnittstelle definiert. Was brauche ich von meinem Vorhersagemodell? Bei einigen Eingabestichproben, der Vorhersage und ihren Fehlern:

interface Model {
  def predict(sample) -> (prediction: float, std: float);
}

Mein Algorithmus übernimmt eine Factory-Funktion, die ein Modell trainiert:

def my_algorithm(..., train_model: (observations) -> Model, ...) {
  ...
  Model model = train_model(observations);
  ...
  y, std = model.predict(x)
  ...
}

Ich habe jetzt verschiedene Implementierungen der Modellschnittstelle und kann sie miteinander vergleichen. Eine dieser Implementierungen verwendet tatsächlich zwei andere Modelle und kombiniert sie zu einem verstärkten Modell. Also dank dieser Schnittstelle:

  • mein Algorithmus muss nicht im Voraus über bestimmte Modelle Bescheid wissen.
  • Ich kann leicht Modelle austauschen, und
  • Ich habe viel Flexibilität bei der Implementierung meiner Modelle.

Ein klassischer Anwendungsfall des Polymorphismus sind GUIs. In einem GUI-Framework wie Java AWT/Swing /… gibt es verschiedene Komponenten . Die Komponentenschnittstelle/Basisklasse beschreibt Aktionen wie das Malen auf den Bildschirm oder das Reagieren auf Mausklicks. Viele Komponenten sind Container, die Unterkomponenten verwalten. Wie kann sich ein solcher Container selbst zeichnen?

void Paint(Graphics g) {
  super.Paint(g);
  for (Component child : this.subComponents)
    child.Paint(g);
}

Hier muss der Container die genauen Typen der Unterkomponenten nicht im Voraus kennen - solange sie der Schnittstelle Component entsprechen, kann der Container einfach die polymorphe Methode Paint() aufrufen. Dies gibt mir die Freiheit, die AWT-Klassenhierarchie mit beliebigen neuen Komponenten zu erweitern.

Während der gesamten Softwareentwicklung gibt es viele wiederkehrende Probleme, die durch Anwendung des Polymorphismus als Technik gelöst werden können. Diese wiederkehrenden Problem-Lösungs-Paare werden als Entwurfsmuster bezeichnet, und einige von ihnen werden im gleichnamigen Buch gesammelt. In diesem Buch wäre mein injiziertes Modell für maschinelles Lernen eine Strategie , mit der ich „eine Familie von Algorithmen definiere, jeden einzelnen einkapsele und mach sie austauschbar “. Das Java-AWT-Beispiel, in dem eine Komponente Unterkomponenten enthalten kann, ist ein Beispiel für einen Verbund .

Aber nicht jedes Design muss Polymorphismus verwenden (abgesehen von der Aktivierung der Abhängigkeitsinjektion für Unit-Tests, was ein wirklich guter Anwendungsfall ist). Die meisten Probleme sind ansonsten sehr statisch. Infolgedessen werden Klassen und Methoden häufig nicht für den Polymorphismus verwendet, sondern lediglich als bequeme Namespaces und für die hübsche Methodenaufrufsyntax. Z.B. Viele Entwickler bevorzugen Methodenaufrufe wie account.getBalance() gegenüber einem weitgehend äquivalenten Funktionsaufruf Account_getBalance(account). Das ist ein perfekter Ansatz, es ist nur so, dass viele "Methoden" -Aufrufe nichts mit Polymorphismus zu tun haben.

6
amon

Obwohl es hier bereits schöne Beispiele gibt, besteht ein anderes darin, Tiere durch Geräte zu ersetzen:

  • Device kann powerOn(), powerOff(), setSleep() und getSerialNumber() sein.
  • SensorDevice kann all dies tun und polymorphe Funktionen wie getMeasuredDimension(), getMeasure(), alertAt(threashhold) und autoTest() bereitstellen.
  • natürlich wird getMeasure() für einen Temperatursensor, einen Lichtdetektor, einen Schalldetektor oder einen Volumensensor nicht auf die gleiche Weise implementiert. Und natürlich kann jeder dieser spezialisierteren Sensoren einige zusätzliche Funktionen zur Verfügung haben.
3
Christophe

Präsentation ist eine sehr häufige Anwendung, vielleicht die häufigste ist ToString (). Welches ist im Grunde Animal.Speak (): Sie sagen einem Objekt, sich zu manifestieren.

Im Allgemeinen sagen Sie einem Objekt, dass es "sein Ding machen" soll. Denken Sie Speichern, Laden, Initialisieren, Entsorgen, ProcessData, GetStatus.

2
Martin Maat

Meine erste praktische Anwendung des Polymorphismus war eine Implementierung von Heap in Java.

Ich hatte eine Basisklasse mit Implementierung von Methoden insert, removeTop, wobei der Unterschied zwischen max und min Heap nur darin besteht, wie der Methodenvergleich funktioniert.

abstract class Heap {  

 abstract boolean compare ( int x , int y );

 boolean insert(int x ) { ... }

 int removeTop() { ... }
}

Wenn ich also MaxHeap und MinHeap haben wollte, konnte ich einfach die Vererbung verwenden.

class MaxHeap extends Heap {

   MaxHeap(int maxSize) {super(maxSize);}

   @Override
   boolean compare(int x, int y) {
       return x>y; // x<y for minHeap
   }
}
2
Bazil

Hier ist ein reales Szenario für Web-App/Datenbanktabellen-Polymorphismus :

Ich verwende Ruby on Rails, um Web-Apps zu entwickeln, und eine Sache, die viele meiner Projekte gemeinsam haben, ist die Möglichkeit, Dateien (Fotos, PDFs) hochzuladen usw.) So kann beispielsweise ein User mehrere Profilbilder haben, und ein Product kann auch viele Produktbilder enthalten. Beide haben das Verhalten, Bilder hochzuladen und zu speichern, sowie Ändern der Größe, Generieren von Miniaturansichten usw. Um DRY] zu bleiben und das Verhalten für Picture zu teilen, möchten wir Picture polymorph machen, damit es dazu gehören kann sowohl User als auch Product.

In Rails würde ich meine Modelle als solche entwerfen:

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class User < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

und eine Datenbankmigration zum Erstellen der Tabelle pictures:

class CreatePictures < ActiveRecord::Migration[5.0]
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end

    add_index :pictures, [:imageable_type, :imageable_id]
  end
end

Die Spalten imageable_id und imageable_type werden von Rails intern verwendet. Grundsätzlich imageable_type enthält den Namen der Klasse ("User", "Product" usw.) und imageable_id ist die ID des zugeordneten Datensatzes. Damit imageable_type = "User" und imageable_id = 1 wäre der Datensatz in der Tabelle users mit id = 1.

Dies ermöglicht es uns, Dinge wie user.pictures, um auf die Bilder des Benutzers zuzugreifen, sowie product.pictures um die Bilder eines Produkts zu erhalten. Dann wird das gesamte bildbezogene Verhalten in der Klasse Photo gekapselt (und nicht in einer separaten Klasse für jedes Modell, das Fotos benötigt), sodass die Dinge trocken bleiben.

Lesen Sie weiter: Rails polymorphe Assoziationen .

1
Chris Cirefice

Es gibt viele Sortieralgorithmen wie Blasensortierung, Einfügungssortierung, schnelle Sortierung, Heap-Sortierung usw. und sie haben unterschiedliche Komplexität und welche optimal zu verwenden ist, hängt von verschiedenen Faktoren ab (z. B. Größe des Arrays).

Der mit einer Sortierschnittstelle bereitgestellte Client befasst sich nur mit der Bereitstellung eines Arrays als Eingabe und dem Empfang eines sortierten Arrays. Während der Laufzeit kann in Abhängigkeit von bestimmten Faktoren eine geeignete Sortierimplementierung verwendet werden. Dies ist ein Beispiel aus der Praxis, wo Polymorphismus verwendet wird.

Was ich oben beschrieben habe, ist ein Beispiel für Laufzeitpolymorphismus, wohingegen das Überladen von Methoden ein Beispiel für Polymorphsim zur Kompilierungszeit ist, bei dem die Konformität in Abhängigkeit von den I/P- und O/P-Parametertypen und der Anzahl der Parameter den Aufrufer zur richtigen Zeit an die richtige Methode bindet.

Hoffe das klärt sich.

0
rahulaga_dev