Home >Java >javaTutorial >Detailed explanation of composite mechanism in Java
This article mainly introduces relevant information about the detailed examples of composite mechanism in Java. I hope that through this article, everyone can understand the difference between inheritance and composite and apply the composite mechanism. Friends in need can refer to it
Detailed explanation of examples of composite mechanisms in java
The defects of inheritance
The defects of inheritance are caused by its overly powerful functions . Inheritance makes the subclass dependent on the implementation of the superclass. From this point of view, it does not comply with the principle of encapsulation.
Once the superclass changes with a version release, the subclass may be broken, even if its code has not changed at all.
In order to explain more specifically, assuming that we are using HashSet in our program now, we need to add a function to count how many elements have been added to this HashSet since it was created.
Without knowing the flaws of inheritance, we designed a class that inherited HashSet, added an attribute addCount for statistics, and overridden the add and addAll methods. In the method Modify the value of addCount,
The code is as follows:
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; } }
This class looks reasonable, but it does not work properly. Execute this paragraph Code:
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 }
Because only three elements were inserted, we expected the getAddCount method to return 3, but in fact it returned 6. What went wrong?
In fact, within HashSet, the addAll method is implemented based on the add method. Therefore, using addAll to add three elements will call addAll once and add three times.
Looking at our overridden method, we will understand why getAddCount returns 6.
Of course, you will say that since HashSet is implemented in this way, then we don’t need to override the addAll method. Yes, that's right.
But although this can work normally, its correctness depends on the fact that the addll method of HashSet is implemented on the add method.
Once the super class modifies the implementation details, our functions may be affected.
In general, inheritance has three natural flaws, which will make the software very fragile:
1) If the subclass calls the method of the parent class, then it will It forms a dependency on the parent class. Once the parent class is changed, the subclass may not work properly.
2) If the parent class adds a new method, and the subclass happens to already provide a method with the same signature but a different return value, then the subclass will not be able to compile.
3) Using inheritance when it should not be done will expose unnecessary APIs to subclasses. In this regard, the Java platform has made mistakes. A typical example is that Properties inherits HashTable, which is unreasonable. The property list is not a hash table, but the Properties in the Java code inherits HashTable. As a result, after the user creates a Properties instance, there is a put There are two methods, setProperties and getProperties. The put and get methods should not be exposed to users.
Because in Properties, both key and value should be String, and HashMap can be other types or even objects.
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 { } }
Composite alternatives to inheritance
The previous section talked about the shortcomings of inheritance, this one Let us take a look at the solution to this problem - compounding.
First we need a class that holds a Set object. This class implements the Set interface. The implementation method calls the corresponding method of the Set object held, so we also call it forwarding. Class:
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(); } }
Then, we can design a class with statistical functions. We only need to inherit the forwarding class we just created, and then the statistical logic code Just write:
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()); } }
This implementation method avoids all the problems mentioned in the previous section. Since inheritance is not used, it does not depend on the super class. Implementation logic, there is no need to worry about the impact of new methods added to the super class on us.
And there is another advantage of writing this way. This class can add statistical functions to all classes that implement the Set interface, not just HashSet, but also other Sets such as TreeSet.
In fact, this is the decoration mode. InstrumentedSet modifies Set and adds a counting attribute to it.
Summary
Inheritance will destroy the encapsulation of the class, making the subclass very fragile and easily damaged.
Use the composite method to modify the super class to make the subclass more robust and more powerful.
The above is the detailed content of Detailed explanation of composite mechanism in Java. For more information, please follow other related articles on the PHP Chinese website!