Class Design III Advanced Inheritance You should be
Class Design III: Advanced Inheritance You should be able to: • describe the open-closed principle, why it matters, and how it applies to object-oriented code. • use overloading correctly and recognize inappropriate uses • Additional References • “Object-Oriented Software Development Using Java”, Xiaoping Jia, Addison Wesley, 2002 • “Core Java 2”, Cay Hortsmann, Gary Cornell, Sun Microsystems Press, 2003 • describe the Liskov Substitution Principle (LSP) • explain whether or not a given design adheres to the LSP • incorporate inheritance into the design of software systems so that the LSP is respected • compare and contrast the use of inheritance and delegation • use delegation and interfaces to realize multiple inheritance in design (e. g. , to support the implementation of multiple types) • identify elements of a given design that violate the basic design principles of low coupling and high cohesion
Background: class member visibility public: • Other classes can access these classes, fields, or methods private: • Only the declaring class can access these classes, fields, or methods protected: • The declaring class and all sub-classes can access these classes fields, or methods package protected: • Other classes in the same package can access these classes, fields or methods • This is the default visibility, when no keyword is used
To Overload or Not to Overload • Overloading: Same name is used for more than one method in the same class • Mainly used for convenience • Misuse may reduce program readability • Should use overloading only in two situations: • There is a general description that fits all overloaded methods • All overloaded methods have the same functionality (some may provide default arguments) Class Design III: Good/Bad Practices 3
To Overload or Not to Overload • Overloading: Same name is used for more than one method in the same class • Mainly used for convenience • Misuse may reduce program readability • Should use overloading only in two situations: • There is a general description that fits all overloaded methods • All overloaded methods have the same functionality (some may provide default arguments) Class Design III: Good/Bad Practices 4
Overloading Examples Good: class String. Buffer { Bad: class Employee { String. Buffer append(char c) { … } //sets employee’s name void name(String s) { … } String. Buffer append(int i) { … } // returns employee’s name String name() { … } String. Buffer append(float f) { … } … Class Design III: Good/Bad Practices … Do both fit under a common description? 5
Method Dispatch: Find the right method declaration for a given method call. METHOD CALL object. method. Call. Name(arg 1, arg 2, . . , arg. N) METHOD DECLARATION return. Type declared. Method. Name(parm 1, parm 2, . . , parm. N) {. . } • Overall strategy: 1. Find candidate method signatures (Find. Signature) 2. Use the most specific candidate signature found (Find. Most. Specific) 3. Find a method matching that signature in the most specific Class Design III: Good/Bad Practices 6 class (Find. Method)
Method Dispatch: Which method signatures are candidates? • Find. Signature 1. In the reference type (i. e. static type) of the target object, select the set of method signatures where. . . • Declared name matches called method name • Number of declared parameters matches number of actual arguments passed to method call 2. From this set, restrict the set to those where. . . • The reference type (i. e. static type) of each passed argument, matches or is a subtype of the corresponding parameter in a declared method signature Class Design III: Good/Bad Practices 7
Method Dispatch: Which exact method signature will the target method have? • Find. Most. Specific 1. From the candidates, select the one signature whose declared parameter types are furthest from Object in the inheritance hierarchy • These types are said to be the most specific 2. In the case that some types are more specific for some parameters in a signature, but less specific for some parameters • Compile time error, i. e. Java will not allow this Class Design III: Good/Bad Practices 8
Method Dispatch: Which method with that signature is executed? • Find. Method 1. Take the signature identified in Find. Most. Specific • Don’t worry about which class it came from or which method implementation was associated with the signature 2. Now, select the class for the actual (runtime) type of the object the method is being called on (i. e. the target object). 3. If that class has a method exactly matching the right method signature, execute that method, else repeat for super-class, etc. . Class Design III: Good/Bad Practices 9
Overloading Example public class Employee { public void name(String s) { … } public void name(Object o) { … } } In Main: String string. As. String = new String(“a. String”); Object string. As. Object = string. As. String; Employee e = new Employee(); e. name(string. As. Object); // what gets called? e. name(string. As. String); // what gets called? Class Design III: Good/Bad Practices 10
Open-Closed Principle • Classes should be open for extension but closed for modification • Want to extend the behaviour of our system by adding subclasses • without having to modify the superclasses • The principle suggests you should consider possible future subclasses when defining a class Class Design III: Good/Bad Practices 11
is-a Style Inheritance : The right way • In addition to the required Java inheritance declaration (extends), subclasses must logically be a subtype of supertype based on class contract • Liskov Substitution Principle or LSP • A subclass can weaken the preconditions • “A subclass can require less restrictions” • “A subclass can accept a larger range of values” • A subclass can strengthen the postconditions • “A subclass can provide more guarantees” • “A subclass can return a smaller range of values” Class Design III: Good/Bad Practices 12
is-a Style Inheritance: The right way… Car … turn. Left( ): void turn. Right( ): void … Hummer Hybrid. Car • Program should be able to use cars without having to know exactly which kind of car it is: void navigate. To. Destination(Car c) { c. turn. Left(); . . . } Class Design III: Good/Bad Practices 13
Weakening the precondition • A subclass method can weaken the precondition (but it cannot strengthen it) when overriding a method from its superclass. The subclass can accept a wider range of values as input. class Payment { /** * @pre amt >= 0 */ void set. Payment. Amount(int amt) {…} } class Credit. Card. Payment extends Payment { /** * @pre true */ void set. Payment. Amount(int amt) {…} } class Cash. Payment extends Payment { … } Class Design III: Good/Bad Practices 14
Weakening the precondition • Why does it not make sense to strengthen the precondition? • Suppose we set the precondition on the set. Payment. Amount of Credit. Card. Payment to be: @pre amt >= 25 • Client should be able to do: Payment p; // substitute Cash. Payment for Payment p = new Cash. Payment(); p. set. Payment. Amount( 5 ); . . . // substitute Credit. Card. Payment for Payment p = new Credit. Card. Payment(); p. set. Payment. Amount( 5 ); // oops! Class Design III: Good/Bad Practices 15
Strengthening the postcondition • A subclass’s method can strengthen the postcondition (but it cannot weaken it): a subclass‘s method can return a subset of the values returned by the method it overrides. class Payment { /** * @post returns >= 0 */ double get. Interest() {…} } class Credit. Card. Payment extends Payment { /** * @post return 4. 25 */ double get. Interest() {…} } class Cash. Payment extends Payment { … } } Class Design III: Good/Bad Practices 16
Strengthening the postcondition • Why does it not make sense to weaken the postcondition? • Suppose the client writes code based on the postcondition of the superclass. • That client code could break if we substitute a superclass object with an instance of one of its subclasses if the subclass' method has a weaker postcondition. • Example: • client writes code assuming that a method returns a value that is positive • subclass overrides method to return *any* value (so postcondition is weakened) • client code is going to break if a negative value is returned. Class Design III: Good/Bad Practices 17
Limitation Inheritance : The wrong way • Subclass restricts rather than extends the behavior inherited from the superclass • Violates is-a relationship • Violates the Liskov Substitution Principle • Usually used for implementation convenience (obviously in the wrong way) • Example • Square defined as a subclass of Rectangle (next slide) • Methods set. Height and set. Width are not applicable to a square Class Design III: Good/Bad Practices 18
Example: Rectangle Class class Rectangle { private double height; private double width; // class invariant height>0 // class invariant width>0 Rectangle(double h, double w) { height = h; width = w; } void set. Height(double h) { height = h; } void set. Width(double w){ width = w; } Class Design III: Good/Bad Practices 19
Example: Rectangle Class (continued). . . double area() { return height * width; } } Class Design III: Good/Bad Practices 20
Example : Square Class (the wrong way) class Square extends Rectangle { Square() { super(); } Square( double s) { super(s, s); }. . . What is wrong with this? Class Design III: Good/Bad Practices 21
Example : Square Class (the wrong way, cont. . . ). . . // Override set. Height and set. Width void set. Height(double l) { ? ? ? } void set. Width(double l) { ? ? ? ? } void set. Side(double s){ super. set. Height(s); super. set. Width(s); } } Class Design III: Good/Bad Practices 22
Example: Rectangle Class (revised) class New. Rectangle { protected double height; protected double width; // class invariant height>0 // class invariant width>0 Rectangle(double h, double w) { height = h; width = w; } double get. Area() { return height*width; } } Class Design III: Good/Bad Practices 23
Example: Square Class (a correct way) class New. Square extends New. Rectangle { New. Square(double s) { super(s, s); } void set. Sides(double s){ height = s; width = s; } } Class Design III: Good/Bad Practices 24
Delegation – another form of re-use • A method delegates the execution of a task to another object of a different type • Think of the “other object” as a servant used to carry out the task • In OO languages delegation can be: - class-based (or static) servant is a component of the class – method-based (or dynamic) method creates a servant and delegates the service • Example next slide: – Square defined using class based delegation Class Design III: Good/Bad Practices 25
Square Class (a right way) public class Square { private Rectangle rectangle; public Square() { rectangle = new Rectangle(); } public Square(double s) { rectangle = new Rectangle(s, s); } } Class Design III: Good/Bad Practices 26
Square Class (a right way) public void set. Side(double s){ rectangle. grow. To. Width(s); } public double area() { return rectangle. area(); } } Class Design III: Good/Bad Practices 27
Multiple Inheritance • Multiple inheritance occurs when a class has more than one super-class. • Multiple inheritance is supported by some programming languages (e. g. , C++) but not others (e. g. , Java). • Multiple inheritance can lead to problems, for example, the classic diamond problem: Person Student Employee Suppose Person has a method my. Method() that's overridden in a different way in Student and Employee and that's not overridden in Teaching. Assistant. Which version of the method should the following code call: Teaching. Assistant ta = new Teaching. Assistant(); Teaching. Assistant Class Design III: Good/Bad Practices ta. my. Method(); 28
Handling Multiple Inheritance in Java • We can use delegation to implement multiple class inheritance if necessary • For instance: instead of this: Student Employee Teaching. Assistant you can do this: Student. Interface Employee. Interface Student Employee Teaching. Assistant Class Design III: Good/Bad Practices 29
Multiple Inheritance Example interface Student. Interface { public float get. GPA(); } interface Employee. Interface { public float get. Salary(); } public class Student implements Student. Interface { protected float GPA; public float get. GPA() { // code for GPA } } Class Design III: Good/Bad Practices 30
Multiple Inheritance Example (continued) public class Employee implements Employee. Interface { protected float salary; public float get. Salary() { // code for Salary } } public class Teaching. Assistant implements Student. Interface, Employee. Interface { private Student student; private Employee employee; Class Design III: Good/Bad Practices 31
Multiple Inheritance Example (continued) public Teaching. Assistant() { student = new Student(); employee = new Employee(); } public float get. GPA() { return student. get. GPA(); } public float get. Salary() { return employee. get. Salary(); } } Class Design III: Good/Bad Practices 32
Name Collisions Among Interfaces • A Java class may extend another class and implement one or more interfaces • Inherited method from one interface may have same name as a method in another class or interface • Name Collision procedure: • if methods have different signatures, they are considered overloaded • if they have same signature and return type, they are one method • if they have same signature, different return types, produce compilation error • if they have same signature and return type, but throw different exceptions, they are one method that throws the union of the exceptions thrown by each of them Class Design III: Good/Bad Practices 33
General Design Guidelines for Inheritance • Place common attributes and methods in the superclasses • Use inheritance to model only is-a type relationships • Use abstract classes and interfaces to design extensible families of objects with common properties • e. g. , employees of different types • e. g. , different types of objects to be drawn in a CAD application Class Design III: Good/Bad Practices 34
Exercise Which is the right way to define ellipse and circle? Ellipse +set. Major. Axis(double): void +set. Minor. Axis(double): void Circle +set. Radius(double): void Class Design III: Good/Bad Practices Circle +set. Radius(double): void 35
Key Concepts In This Lecture • There a lot of related concepts we covered in this lecture • When you design a superclass, think about whether it might be extended in the future (i. e. , which methods should be protected instead of private, etc. ). This is the open-closed principle in action. • In Java, a subclass is considered a subtype as is an implementation of an interface. To ensure an instance of a subclass (or a class that extends an interface) is substitutable for its superclass (or its interface) we need to follow the Liskov Substitutability Principle (LSP). i. e. , watch out that pre-conditions and post-conditions of overridden methods do the right thing. • If we want to reuse code but can’t do it via a subclass because we’d violate the LSP, we can use delegation where we keep an object of the type from which we want the code and we call the object’s methods to do the work we want done. • If we want one class to include behavior from different types, use interfaces (and sometimes delegation too!) Class Design III: Good/Bad Practices 36
- Slides: 36