1 / 30

Lecture 14

Lecture 14. Inheritance vs Composition. Inheritance vs Interface. Use inheritance when two objects share a structure or code relation Embodies the is_a relation Should reflect the roles of the objects throughout the program Use an interface when they share a common behavior spec.

Download Presentation

Lecture 14

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Lecture 14 Inheritance vs Composition

  2. Inheritance vs Interface • Use inheritance when two objects share a structure or code relation • Embodies the is_a relation • Should reflect the roles of the objects throughout the program • Use an interface when they share a common behavior spec

  3. Abstract Classes vs Interfaces • Abstract for the inheritance situation above, i.e. sharing code or structure • Interfaces for sharing behavior spec, not code • Example: Consider a Framework • It provides code in the form of methods that are inherited without being overridden->code is inherited • The implementation of an action listener cannot be predicted, so no code is inherited here

  4. Inheritance and Composition • Which to use and when? • Alternative 1: Implement a Stack with inheritance from a Vector class Stack extends Vector{ public Object push(Object item){ addElementAt(size()-1;} public Object pop(){return elementAt (size()- 1);} • Stacks can use protected members of Vector • Stacks can be used where Vectors are used as arguments • Reuse Vector methods, e.g. size, isEmpty • What to do with unwanted Vector methods, e.g. removeAt()?

  5. Inheritance and Composition • Now do this with Composition: class Stack{ private Vector theData; public Stack(){theData=new Vector();} public Object push(Object item){theData.addElement( item); return item;} • Advantages • Can change the implementation without any impact to users of stacks • Interface is narrower: we don’t need to know anything about Vectors

  6. Inheritance and Composition • Advantages to Composition (cont.) • There are no substitutability issues • Stacks and Vectors are different types • One cannot be substituted for the other • Meaningless behavior is not exposed • Inheritance couples base and derived class • Changes do not ripple upwards • Promotes encapsulation • Not dependent on private variables • Can change implementation of composed objects at run-time, not so with inheritance

  7. Composition • Prefer it to inheritance • Used in Java AWT • Uses Components and Containers • An item is a Component • A Container can contain Components and Containers • Obtain a tree-like structure by nesting • Embodied in the Composite Pattern

  8. Composite Pattern • Facilitate the same treatment of composite and primitive objects • Composite object: an object that contains other objects • E.g. lines and polygons are primitive objects, a drawing is composite. • Composite methods are implemented by iterating over the composite object, invoking the appropriate method for each subcomponent

  9. Composite Pattern • Use it when • You want to represent part-whole hierarchies of objects • You want your clients to be able to ignore differences between compisitions of objects and objects themselves • Benefits • Easy to add new kinds of components • Makes clients simpler

  10. Composite Pattern • Liabilities • Hard to restrict the types of components • Clients can do meaningless things to primitive objects at run-time

  11. Example • Consider a simple GUI system: public class window{ Button[] buttons; TextArea[] ta; Menu[] menus; WidgetContainer[] containers; public void updateWindow(){ if (buttons != null){ for(k=0;k<buttons.length();k++) buttons[k].draw(); if (ta != null) ... }

  12. Problem • If you want to add on a new kind of resource the update() method needs to be modified • Way around this is to use a uniform interface • Just do Widgets and WidgetContainers • Now you are programming to an interface • All Widgets support the Widget interface

  13. Another Attempt public class window{ Widget[] widgets; WidgetContainer[] containers; public void updateWindow(){ if (widgets != null) for (k=0; k<widgets.length(); k++){ widgets[k].updateWindow(); if (containers != null) ....

  14. Now Use Composite Pattern Component Button Menu widgetContainer

  15. To Obtain public class window{ Component[] components; public void updateWindow(){ if (components != null) for(k=0;k<components.length();k++) components[k].updateWindow(); } } • Bottom Line: Do not distinguish Widgets and WidgetContainers

  16. Delegation in Composition • It is often convenient to allow a receiving object, e.g. a Window, to further delegate certain operations to another object--its delegate--e.g. a Rectangle • This is better than making Window a subclass of Rectangle, the Window class may reuse the behavior of Rectangle and delegate rectangle-specific behavior to it

  17. Window Rectangle Rectangle area() area() width height return Rectangle->area() return width*height

  18. Comments • Here Window has_a Rectangle • It is easy to compare behavior at run-time • You can reuse Rectangle as a black box (not white-box as in inheritance) • You can use polymorphism to achieve dynamic behavior--employ an interface. This is the Strategy Design Pattern • Trade-off: harder to understand, much more flexible than inheritance • Visitor uses delegation

  19. Combining Inheritance and Composition • Main use: simplify an inheritance hierarchy • Not achievable everywhere, but it is very powerful where you can • Main example: Java Stream Wrappers • Add capabilities to a stream by “wrapping” it in another object that provides the desired capabilities • Makes a bigger, better version of a base class

  20. InputStream Hierarchy • InputStream • ByteArrayInputStream • FileInputStream • PipedInputStream • SequenceInputStream • ObjectInputStream • FilterInputStream • BufferedInputStream • DataInputStream  differ in the source of data values

  21. The Way It Works • Start with an InputStream, i.e. try to read a stream of bytes in sequence • Use it polymorphically • Add functionality, depending on your data • This new functionality is called a “wrapper” • Just add a new and better interface to the old one, getting/sending the result from/to the same place, i.e. InputStream, resp. OutputStream • Subclassing provides the new interface

  22. Example class FilterInputStream extends InputStream ... protected InputStream in; FilterInputStream(InputStream in){ this.in = in;} ...} • Filter is a wrapper • Builds on InputStream • First obtain the sequence of bytes from the InputStream and then do the filtering • Use composition on the InputStream

  23. Comments • You really have one object, many interfaces • Thus you avoid an explosion of the inheritance hierarchy • FilterInputStream is really just an InputStream with added functionality • If you wanted a DataInputStream, just wrap the InputStream in a DataInputStream object • Construct a new DataInputStream object, using the InputStream as input to the constructor

  24. Important Note • The primitive data-type operations of DataInputStream cannot be included in InputStream, because that object does not know about integer, float, etc • But you can make it understand these types by wrapping. • The neat thing: You can use a DataInputStream anywhere an InputStream is expected--thi is the advantage of inheritance

  25. Example • If dataSource is of type InputStream: InputStream dataSource; • Then the DataInputStream: DataInputStream typedDataSource = new DataInputStream(dataSource); • Gets the data from exactly the same place as data retrieved from dataSource • We have merely provided a better interface to the same input stream

  26. Another Example: Buffered Readers • Use Readers and Writers for character data • Start with primitive readers that directly manipulate the input data: • CharArrayReader, StringReader, FileReader • Now add functionality to data generated by the above Readers: • BufferedReader, FilterReader, LineNumberReader • Reader has the subclasses • BufferedReader • FileReader • PipedReader

  27. One Way to Buffer the Char Input • Do it from FileReader: BufferedReader in =new BufferedReader( new FileReader(“stuff.in”); • Buffers the char file “stuff.in”

  28. Another Way • Start with Reader (which is also primitive for 16 Bit Unicodes) • Buffer on top of it • Allow two types of buffer--a standard one with 8192 bytes and another of user-specified size • Use Reader’s capabilities and then add the buffering in the constructor

  29. public class BufferedReader extends Reader{ private Reader in; //use composition private char cb[]; //the buffer private static int defaultCharBufferSize=8192; private static int defaultExpectedLineLength=80; public BufferedReader(Reader in, int sz){ super(in); //get Reader functionality if (sz <= 0) throw new IllegalArgumentException( “Illegal Buffer size”); this.in = in; cb = new char[sz]; nextChar = nChars = 0;} public BufferedReader(Reader in){ this(in, defaultCharBufferSize);}

  30. Remarks • BufferedReader has methods read() and readLine() to read a single character, resp. a line of text. • Both of these throw an IOException • Remember to import from java.io

More Related