Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung des zusammengesetzten Mechanismus in Java

Detaillierte Erläuterung des zusammengesetzten Mechanismus in Java

黄舟
黄舟Original
2017-09-09 13:21:171529Durchsuche

Dieser Artikel stellt hauptsächlich relevante Informationen zu den detaillierten Beispielen des zusammengesetzten Mechanismus in Java vor. Ich hoffe, dass jeder durch diesen Artikel den Unterschied zwischen Vererbung und Zusammensetzung verstehen und den Mechanismus der Zusammensetzung anwenden kann

Detaillierte Erläuterung von Beispielen für zusammengesetzte Mechanismen in Java

Die Mängel der Vererbung

Die Mängel der Vererbung sind verursacht durch seine übermäßig leistungsstarke Funktion. Die Vererbung macht die Unterklasse von der Implementierung der Oberklasse abhängig. Unter diesem Gesichtspunkt entspricht sie nicht dem Prinzip der Kapselung.
Sobald sich die Oberklasse mit einer Veröffentlichung ändert, kann die Unterklasse kaputt gehen, auch wenn sich ihr Code überhaupt nicht geändert hat.

Um es genauer zu erklären, müssen wir unter der Annahme, dass wir HashSet jetzt in unserem Programm verwenden, eine Funktion hinzufügen, um zu zählen, wie viele Elemente diesem HashSet seit seiner Erstellung hinzugefügt wurden.

Ohne die Mängel der Vererbung zu kennen, haben wir eine Klasse entworfen, die HashSet geerbt, ein Attribut addCount für Statistiken hinzugefügt und die Methoden add und addAll überschrieben. In der Methode Ändern Sie den Wert von addCount,

Der Code lautet wie folgt:


public class InstrumentedHashSet<E> extends HashSet<E> { 
  // The number of attempted element insertions 
  private int addCount = 0; 
 
  public InstrumentedHashSet() { 
  } 
 
  public InstrumentedHashSet(int initCap, float loadFactor) { 
    super(initCap, loadFactor); 
  } 
 
  @Override public boolean add(E e) { 
    addCount++; 
    return super.add(e); 
  } 
 
  @Override public boolean addAll(Collection<? extends E> c) { 
    addCount += c.size(); 
    return super.addAll(c); 
  } 
 
  public int getAddCount() { 
    return addCount; 
  } 
 
}

Diese Klasse sieht vernünftig aus, funktioniert aber nicht ordnungsgemäß. Führen Sie diesen Code aus:


public static void main(String[] args) { 
  InstrumentedHashSet<String> s = 
    new InstrumentedHashSet<String>(); 
  s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));   
  System.out.println(s.getAddCount()); // expect 3 but 6 
}

Da nur drei Elemente eingefügt wurden, erwarteten wir, dass die getAddCount-Methode 3 zurückgeben sollte, aber tatsächlich gab sie 6 zurück. Was ist schief gelaufen?

Tatsächlich wird in HashSet die addAll-Methode basierend auf der add-Methode implementiert. Wenn Sie also addAll zum Hinzufügen von drei Elementen verwenden, wird addAll einmal aufgerufen und dreimal hinzugefügt.
Wenn wir uns unsere überschreibende Methode ansehen, werden wir verstehen, warum getAddCount 6 zurückgibt.

Natürlich werden Sie sagen, dass wir die addAll-Methode nicht überschreiben müssen, da HashSet so implementiert ist. Ja, das stimmt.

Obwohl dies normal funktionieren kann, hängt seine Richtigkeit davon ab, dass die addll-Methode von HashSet in der add-Methode implementiert ist.

Sobald die Superklasse die Implementierungsdetails ändert, kann dies Auswirkungen auf unsere Funktionalität haben.

Im Allgemeinen weist die Vererbung drei natürliche Mängel auf, die die Software sehr anfällig machen:

1) Wenn die Unterklasse die Methode der übergeordneten Klasse aufruft, wird sie gebildet eine Abhängigkeit von der übergeordneten Klasse. Sobald die übergeordnete Klasse geändert wird, funktioniert die Unterklasse möglicherweise nicht ordnungsgemäß.
2) Wenn die übergeordnete Klasse eine neue Methode hinzufügt und die Unterklasse bereits eine Methode mit derselben Signatur, aber einem anderen Rückgabewert bereitstellt, kann die Unterklasse nicht kompiliert werden.
3) Durch die Verwendung der Vererbung, wenn sie nicht durchgeführt werden sollte, werden unnötige APIs für Unterklassen verfügbar gemacht. In dieser Hinsicht hat die Java-Plattform einen Fehler gemacht: Eigenschaften erben HashTable, was unzumutbar ist, aber die Eigenschaften im Java-Code erben Erstellt eine Properties-Instanz, es gibt zwei Methoden: setProperties und getProperties. Die Put- und Get-Methoden sollten Benutzern nicht zugänglich gemacht werden.
Denn in den Eigenschaften sollten sowohl Schlüssel als auch Wert String sein, während HashMap andere Typen oder sogar Objekte sein kann.


public class TestProperty { 
  public static void main(String[] args) { 
    Properties properties = new Properties(); 
    properties.setProperty("aaa", "aaa"); 
    properties.put("aaa", new TestPropertyObj()); 
    System.out.println(properties.getProperty("aaa")); // null 
    System.out.println(properties.get("aaa")); // com.hzy.effjava.chp3.item16.TestProperty$TestPropertyObj@5f4fcc96 
  } 
  static class TestPropertyObj { 
     
  } 
}

Zusammengesetzte Alternativen zur Vererbung

Im vorherigen Abschnitt ging es um die Mängel der Vererbung. In diesem Abschnitt werfen wir einen Blick auf die Lösung dieses Problems – die Aufzinsung.

Zuerst benötigen wir eine Klasse, die ein Set-Objekt enthält. Die Implementierungsmethode ruft die entsprechende Methode des Set-Objekts auf, daher nennen wir sie auch Weiterleitung 🎜>


public class ForwardingSet<E> implements Set<E> { 
  private final Set<E> s; 
  public ForwardingSet(Set<E> s) { this.s = s; } 
 
  public void clear()        { s.clear();      } 
  public boolean contains(Object o) { return s.contains(o); } 
  public boolean isEmpty()     { return s.isEmpty();  } 
  public int size()         { return s.size();   } 
  public Iterator<E> iterator()   { return s.iterator(); } 
  public boolean add(E e)      { return s.add(e);   } 
  public boolean remove(Object o)  { return s.remove(o);  } 
  public boolean containsAll(Collection<?> c) 
                  { return s.containsAll(c); } 
  public boolean addAll(Collection<? extends E> c) 
                  { return s.addAll(c);   } 
  public boolean removeAll(Collection<?> c) 
                  { return s.removeAll(c);  } 
  public boolean retainAll(Collection<?> c) 
                  { return s.retainAll(c);  } 
  public Object[] toArray()     { return s.toArray(); } 
  public <T> T[] toArray(T[] a)   { return s.toArray(a); } 
  @Override public boolean equals(Object o) 
                    { return s.equals(o); } 
  @Override public int hashCode()  { return s.hashCode(); } 
  @Override public String toString() { return s.toString(); } 
}
Dann können wir eine Klasse mit statistischen Funktionen entwerfen. Wir müssen nur die Weiterleitungsklasse erben, die wir gerade erstellt haben, und dann zählen. Schreiben Sie einfach die Logik Code:



public class InstrumentedSet<E> extends ForwardingSet<E> { 
  private int addCount = 0; 
 
  public InstrumentedSet(Set<E> s) { 
    super(s); 
  } 
 
  @Override public boolean add(E e) { 
    addCount++; 
    return super.add(e); 
  } 
  @Override public boolean addAll(Collection<? extends E> c) { 
    addCount += c.size(); 
    return super.addAll(c); 
  } 
  public int getAddCount() { 
    return addCount; 
  } 
 
  public static void main(String[] args) { 
    InstrumentedSet<String> s = 
      new InstrumentedSet<String>(new HashSet<String>()); 
    s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));   
    System.out.println(s.getAddCount()); 
  } 
}
Diese Implementierungsmethode vermeidet alle im vorherigen Abschnitt erwähnten Probleme. Da die Vererbung nicht verwendet wird, ist dies nicht der Fall Da wir in der Superklasse sind, besteht kein Grund zur Sorge über die Auswirkungen neuer Methoden, die von der Superklasse auf uns hinzugefügt werden.


Und es gibt einen weiteren Vorteil dieser Schreibweise. Diese Klasse kann allen Klassen, die die Set-Schnittstelle implementieren, statistische Funktionen hinzufügen, nicht nur HashSet, sondern auch anderen Sets wie TreeSet.


Eigentlich ist dies der Dekorationsmodus, bei dem InstrumentedSet Set ändert und ihm ein Zählattribut hinzufügt.

Zusammenfassung

Vererbung zerstört die Kapselung der Klasse, wodurch die Unterklasse sehr zerbrechlich wird und leicht beschädigt werden kann.


Verwenden Sie die zusammengesetzte Methode, um die Superklasse zu ändern und die Unterklasse robuster und leistungsfähiger zu machen.

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des zusammengesetzten Mechanismus in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn