Download presentation
Presentation is loading. Please wait.
1
Assertions Prasun Dewan Comp 114
2
Assertions Declare some property of the program Used for – formal correctness – testing – documentation – specification
3
Compile time vs. runtime properties Some assertions language supported –Compile time String s = nextElement() –Runtime ((String) nextElement()) –Asserting type properties of object. –Assertions describe runtime properties
4
Application-independent vs. dependent Language can provide us with fixed number of application-independent assertions. Cannot handle –First character of String is a letter. –Letter concept not burnt into language. Class Character defines it –Innumerable assertions about letters possible Second elements of string is letter. Third element of string is letter. Need mechanism to express arbitrary assertions
5
Assertions vs. Exceptions Wrong assertion results in exception. Wrong class cast leads to class cast exception.
6
Reasons for exceptions User error –Programmer cannot prevent it Internal error –Programmer can prevent Assertions catch internal errors.
7
Reasons for assertions Why catch internal errors via assertions? Alternative: Make test runs and look at output.
8
Finite test runs Some errors not exhibited in test runs. –Inconsistent string not printed because of user option.
9
Late detection Output produced much after the cause –Storing an inconsistent string causes erroneous output when the string is matched not when it is stored.
10
Complex output May not know what the output is, but know the relationship between input and output –(123.345 * 789.123 ) 123.345 == 789.123 –Number is divisible by a prime factor
11
No manifestation in output Some errors have to do with efficiency. –Storing duplicate items in a set. Not exhibited in output
12
Example Assertion { y = 10/x; z = 5} assert (y == 10/x) & (z == 5)
13
Definition Example { y = 10/x; z = 5} assert (y == 10/x) & (z == 5) Statement regarding –State of program (variable values) –Program counter (currently executing statement) PC implicit by putting assertion next to a statement –Specifies Boolean expression involving selected program variables –Assertion fails if boolean expression false –Not all program variables named X unnamed above Does not care about unnamed variables
14
Role of Assertions Debugging –Exception thrown if assertion fails Documentation // detailed prime factor computation code …. assert (number % prime) == 0 Specification –Assertion defined desired result of the code to be implemented Formal correctness –Can prove if assertion met by code. –Need assertions regarding language constructs
15
Recording Variables Cannot say: {X = X + 1} assert X == X + 1; Introduce recording variable {oldX = X; X = X + 1} assert X = oldX + 1 Special variables needed to make assertions sometimes
16
Preconditions and Postconditions assert (x !=0) { y = 10/x; z = 5} assert (y == 10/x) & (z == 5) Precondition –What is expected before statement(block) execution Postcondition –What is guaranteed after statement(block) if precondition met Together define a contract
17
Alternative syntax pre (x !=0) post (y == 10/x) & (z == 5) { y = 10/x; z = 5} Both conditions placed before statement block.
18
Incorrect precondition? x = 0; pre (x !=0) post (y == 10/x) & (z == 5) { y = 10/x; z = 5} Precondition may not be satisfied by previous incorrect statement. Yet it is a correct precondition
19
Alternative precondition pre (x > 0) post (y == 10/x) & (z == 5) { y = 10/x; z = 5} Some assertions imply others –x > 0 x !=0 Implied assertion is considered weaker. Can always replace precondition with a stronger one. Prefer weakest precondition –Requires fewest assumptions. –Statement can be used in more contexts –Better documentation
20
Alternative postcondition pre (x != 0) post (z == 5) { y = 10/x; z = 5} Some assertions imply others –y == 10/x & (z == 5) => z == 5 Can always replace postcondition with a weaker one Prefer strongest postcondition –More detailed documentation –Can be used in more contexts Can be followed by statements with stronger preconditions.
21
Course Analogy Preconditions –Declared course prerequisite Post conditions –Advertised objectives Stronger precondition –Does not allow some students to enroll Weaker post conditions –Does not allow enrollment in future courses
22
Strongest Postcondition? pre (x !=0) post (y == 10/x) & (z == 5) { y = 10/x; z = 5} Says nothing about x.
23
Strongest Postcondition pre (x !=0) post (y == 10/x) & (z == 5) & (x != 0) { y = 10/x; z = 5} Strongest post condition given the precondition The stronger the pre condition, stronger the post condition.
24
Alternative weakest/strongest pair pre (x > 0) post (y == 10/x) & (z == 5) & (x > 0) & (y > 0) { y = 10/x; z = 5} Stronger precondition Stronger postcondition
25
Multiple valid pairs Weakest precondition relative to post condition. Strongest post condition relative to pre condition. A statement not associated with a unique pair that is best. Strengthening the precondition strengthens the post condition What is the “right pair?”
26
Two approaches Start with a precondition –Find the strongest postcondition we can prove. Start with a post condition –Find the weakest precondtion for it.
27
Theoretical specification point of view Specify the result needed as a post condition. Identify the weakest precondition needed to obtain the result. Write a program that satisfies the pair.
28
Reality specification point of view Based on what you can assume you change your expectations. Iterative process.
29
Debugging/specification/ documentation point of view Precondition of a statement should be weakest one necessary for “correct” operation of program. –Correct means no exception. A statement that is not equivalent should not have the same pre and post condition
30
Debugging/ specification/ documentation point of view pre true post true { y = 10/x; z = 5} pre true post true { y = 10/x;} Statements are not equivalent Not the best pre and post conditions
31
Debugging/ specification/ documentation point of view pre false post false { y = 10/x; z = 5} pre false post false { y = 10/x;} Statements are not equivalent Not the best pre and post conditions
32
Debugging/specification/ documentation point of view pre (x >0) post (y == 10/x) & (z == 5) & (x > 0) { y = 10/x; z = 5} pre (x >0) post (y == 10/x) & (z == 5) & (x > 0) { y = 10/x; z = 5; x = abs(x) + 1} Statements are not equivalent
33
Debugging/ specification/ documentation point of view pre (x !=0) post (y == 10/x) & (z == 5) & (x != 0) { y = 10/x; z = 5} Best pre and post conditions
34
Proving programs correct point of view Precondition can be stronger than one that is the best one for the statement from debugging/specification/documentation point of view Allows us to derive pre and post conditions of containing statement/program
35
Program correctness proof pre true post (x == 1) {x = 1} pre (x == 1) post (y == 10/x) & (z == 5) & (x == 1) { y = 10/x; z = 5}
36
Program correctness proof pre true post (y == 10/x) & (z == 5) & (x == 1) {x = 1; y = 10/x; x= 5}
37
Changeable assertions What we assert about a statement (to prove program correct) may depend on what is executed before it. Need facilities to change assertions easily. Proxies explained later address this.
38
Weakest possible condition Implied by anything true p true If p is true then true is true. But true is always true. So p can be any boolean expression.
39
Strongest possible condition Implies anything false false p If false is true then p is true But false is never true. So any p is implied. “I will do this when hell freezes over”
40
Important equivalence P Q !P | Q P true !P | true true false Q !false | Q true
41
Asserting false switch c { case ‘a’: … case ‘b’: … default: assert false } Unreachable statement
42
Invariant pre (x !=0) post (y == 10/x) & (z == 5) & (x != 0) { y = 10/x; z = 5} True before and after statement
43
Separately declared inv (x !=0) post (y == 10/x) & (z == 5) { y = 10/x; z = 5} List invariant separately
44
Invariants for debugging purposes Should never be false –False strongest invariant of all statements. –But not best for a reachable statement as it does not convey useful information –Never assert it for reachable statements if assertion not done for program proofs. Should never involve recording variables –recording variables describe how program variables change –invariants describe how these variables do not change
45
Loop Invariant sum = 0; j = 0; while (j < n) { j++; sum = sum + j; } Holds true before and after each loop iteration, that is, before and after each execution of loop body.
46
Loop Invariant (edited) sum = 0; j = 0; while (j < n) { j++; sum = sum + j; } Holds true before and after each loop iteration, that is, before and after each execution of loop body.
47
Loop Invariant inv (sum = 0 j k) & (j = 0) sum = 0; j = 0; while (j < n) { j++; sum = sum + j; } Holds true before and after each loop iteration, that is, before and after each execution of loop body.
48
Method assertions invariant x != 0 post (y == 10/x) & (z == 5) & (x != 0) void m () { y = 10/x; z = 5; } Preconditions, postconditions, invariants associated with method body
49
Class assertions (edit) public class C { int x =0; int y = 1; public void incrementXAndY () { incrementX(); incrementY(); } public int getX() { return x;} public int getY() { return y;} incrementX() { x++;} incrementY() { y++;} } Preconditions, postconditions, invariants shared by all public methods of class
50
Class invariant (edited) public class C { int x =0; int y = 1; public void incrementXAndY () { incrementX(); incrementY(); } public int getX() { return x;} public int getY() { return y;} incrementX() { x++;} incrementY() { y++;} } Preconditions, postconditions, invariants shared by all public methods of class
51
Class assertions invariant y == x + 1 public class C { int x =0; int y = 1; public void incrementXAndY () { incrementX(); incrementY(); } public int getX() { return x;} public int getY() { return y;} incrementX() { x++;} incrementY() { y++;} } Preconditions, postconditions, invariants shared by all public methods of class
52
Expressing Assertions Natural language –All array elements are not odd. –All array elements are either odd or positive. –Easy to read but ambiguous. Programming language –Library or language constructs –Executable, unambiguous but language-dependent and awkward Useful for debugging Specification cannot be done before language decided. Mathematical language –Uambiguous, time tested, convenient but not executable
53
Propositional Calculus Logic operators not, and, or We will use Java syntax. Quantifiers –Universal ( ) –Existential ( ) Propositional variables Program Others: Recording, Quantifier Propositions –Boolean expressions involving operators, variables, and quantifiers Simple/quantified propositions –Do not use/use quantifiers
54
Propositional Algebra Calculus based on algebra Algebra defines –Arithmetic operations –Relations operations –We will use Java syntax
55
Example Propositions Simple propositions –True –False –X > 6 –(X > 6 ) & (Y < 2) Quantified –All elements of B are not null j: 0 <= j < b.size() : b.elementAt(j) != null –At least one element of B is not null. j: 0 <= j < b.size(): b.elementAt(j) != null
56
Quantified Propositions Sub-proposition –Simple or quantified proposition in terms of quantifier Domain –A collection of values used in sub-proposition evaluation –b.elementAt(0), … b.elementAt(B.size() – 1 Domain description –Describes domain using quantified variable Quantified – j: 0 <= j < b.size(): b.elementAt(j) != null – j: 0 <= j < b.size(): b.elementAt(j) != null General form: –Qx:D(x):P(x) –Q is either or quantifier –X is quantified variable –D(x) is domain description –P(x) is sub-proposition
57
Expressing Assertions in Java Write your own code. Libraries Language support –Does not support quantifiers –Works for 1.4 –You have 1.3
58
Assertion Failed Exception Thrown when assertion fails Unchecked because internal error Can subclass it for specific assertions Will use message in examples package assertions; public class AnAssertionFailedException extends RuntimeException { public AnAssertionFailedException () {}; public AnAssertionFailedException (String initValue) { super (initValue); }
59
Assert methods Used for simple assertions package assertions; import java.util.Enumeration; public class AnAsserter { public static void assert (boolean proposition, String message) throws AnAssertionFailedException { if (!proposition) throw new AnAssertionFailedException (message); } public static void assert (boolean proposition) throws AnAssertionFailedException { assert (proposition, “”); }
60
Using assert Must write our boolean expression that is passed to assert –public static int sum (int from, int to) { … } Must code pre/post conditions and invariants as assert calls //inv (sum = 0 j k) & (j = 0) sum = 0; j = 0; while (j < n) { j++; sum = sum + j; }
61
Using assert Must write our boolean expression that is passed to assert –public static int sum (int from, int to) { … } Must code pre/post conditions and invariants as assert calls sum = 0; j = 0; //inv (sum = 0 j k) & (j = 0) while (j < n) { AnAsserter.assert (j = 0 && sum == sum(0, j)) j++; sum = sum + j; AnAsserter.assert (j =0 && sum == sum(0, j)) }
62
Class and Method Invariants Class invariant –Encode them as invariants of all public methods. Method invariant –Put assert at start and end of method //inv y = x + 1 public class C { int x =0; int y = 1; public void incrementXAndY () { incrementX(); incrementY(); } public int getX() { return x;} public int getY() { return y;} incrementX() { x++;} incrementY() { y++;} }
63
Class and Method Invariants Class invariant –Encode them as invariants of all public methods. Method invariant –Put assert at start and end of method –Can define another proxy method that asserts and calls the real method Keeps asserting code separate Useful when multiple exit points //inv y = x + 1 public class C { int x =0; int y = 1; void assertClassInvariant{ AnAsserter.assert (y == x + 1); } void internalIncrementXAndY () { incrementX(); incrementY(); } public void incrementXAndY () { assertClassInvariant(); internalIncrementXAndY(); assertClassInvariant(); } int internalGetX() { return x; } public int getX() { assertClassInvariant(); int retVal = internalGetX(); assertClassInvariant(); return retVal; } … } Asserting proxy method
64
Proxy Methods Can define another proxy method that asserts and calls the real method –Keeps asserting code separate –Useful when multiple exit points char toGrade (int score) { if (score >= 50) return ‘P’; else return ‘F’; } char assertingToGrade (int score) { char grade = toGrade(score); assert (grade == ‘F’ || grade == ‘P’); return grade; }
65
Quantified Assertions Syntax –Qx:D(x):P(x) – j: 0 <= j < b.size(): b.elementAt(j) != null – j: 0 <= j < b.size(): b.elementAt(j) != null Goal: –Write general boolean functions that take as arguments encoding of the elements of proposition and return true iff proposition is true –Will write separate functions for universal and existential quantifer
66
Goal Need to fill the … package assertions; import java.util.Enumeration; public class AQuantifier { public static boolean forAll (…) { … } public static boolean thereExists (…) { … }
67
Problem with inaccessible variables Syntax –Qx:D(x):P(x) – j: 0 <= j < b.size(): b.elementAt(j) != null – j: 0 <= j < b.size(): b.elementAt(j) != null How to describe D(x) and P(x)? –Cannot pass the expressions as variables involved not visible to library –Will pass one argument describing the domain Collection of elements –Another argument describing the subproposition to be evaluated for each domain element.
68
How to describe domain? Syntax –Qx:D(x):P(x) – j: 0 <= j < b.size(): b.elementAt(j) != null – j: 0 <= j < b.size(): b.elementAt(j) != null Domain can be –Array, Vector, StringHistory, … Need a common interface to describe elements –java.util.Enumeration
69
Describing the domain AnAsserter.assert(AQuantifier.forAll(B.elements(), …, "Some element of B is null"); AnAsserter.assert(AQuantifier.thereExists(b.elements(), …, "All elements of B are null"); Need to fill … package assertions; import java.util.Enumeration; public class AQuantifier { public static boolean forAll ((Enumeration domain, …) { while (domain.hasMoreElements()) … } public static boolean thereExists ((Enumeration domain, …) { while (domain.hasMoreElements()) … }
70
How to describe Subproposition Syntax –Qx:D(x):P(x) – j: 0 <= j < b.size(): b.elementAt(j) != null – j: 0 <= j < b.size(): b.elementAt(j) != null Cannot pass expression string. But can pass function that evaluates it. –boolean isNotNull(Object element) { return element != null; } Function will be evaluated for each domain element by our libraries
71
Subproposition as a function AnAsserter.assert(AQuantifier.forAll(B.elements(), isNotNull), "Some element of B is null"); AnAsserter.assert(AQuantifier.thereExists(b.elements(), isNotNull), "All elements of B are null"); package assertions; import java.util.Enumeration; public class AQuantifier { public static boolean forAll (Enumeration domain, (object boolean) subProposition) { while (domain.hasMoreElements()) if (!subProposition (domain.nextElement()) return false; return true; } public static boolean thereExists ((Enumeration domain, (object boolean) subProposition) { while (domain.hasMoreElements()) if (!subProposition (domain.nextElement()) return true; return false; }
72
How to describe Subproposition Cannot pass expression string. But can pass function that evaluates it. –boolean isNotNull(Object element) { return element != null; } Java does not support function parameters But allows object parameters –Object = data + functions All subproposition objects implement same interface A subproposition object visits each element package visitors; import assertions.ElementChecker; public class ANonNullChecker implements ElementChecker { public boolean visit(Object element) { return (element != null); } package assertions; public interface ElementChecker { public boolean visit (Object element); }
73
Describing the subproposition AnAsserter.assert(AQuantifier.forAll(b.elements(), new ANonNullChecker()), "Some element of B is null"); AnAsserter.assert(AQuantifier.thereExists(b.elements(), new ANonNullChecker()), "All elements of B are null"); package assertions; import java.util.Enumeration; public class AQuantifier { public static boolean forAll (Enumeration domain, ElementChecker subProposition) { while (domain.hasMoreElements()) if (!subProposition.visit(domain.nextElement())) return false; return true; } public static boolean thereExists (Enumeration domain, ElementChecker subProposition) { while (domain.hasMoreElements()) if (subProposition.visit(domain.nextElement())) return true; return false; }
74
Subproposition accessing vars other than domain elements j: 0 <= j < b.size(): b.elementAt(j) != a.elementAt(0) j: 0 <= j < b.size(): b.elementAt(j) != a.elementAt(0) package visitors; import assertions.ElementChecker; public class AnInequalityChecker implements ElementChecker { Object testObject; public AnInequalityChecker(Object theTestObject) { testObject = theTestObject; } public boolean visit(Object element) { return !element.equals(testObject); } AnAsserter.assert(AQuantifier.forAll(b.elements(), new AnInequalityChecker(a.elementAt(0))), "Some element of b is equal to a.elementAt(0)"); Each external var becomes constructor parameter
75
Visitor Pattern Some collection C of elements of type T Visitor interface –public interface V {public T2 m (T p);} One or more traverser methods that use collection and visitor interface to pass one or more collection elements to the method. traverser1 (C c, V v) { …v.m(element of C)…} Implementation of interface whose constructors take as arguments external variables that need to be accessed by the visitor method public class AV1 implements V { public AV1 (T1 p1, … Tn pN) { …} public T2 m (T p) { … } } Client passes traverser visitor implementation and collection traverser1(c, new AV1(a1,.. aN))
76
Visitor Pattern Collection Interface C with elements of type T element1: T component Visitor Interface Visitor Class 1 implements Visitor Class 2 uses Traverser 2 Traverser 1
77
Example of Visitor Pattern Enumeration Object component ElementChecker ANonNull Checker implements AnInequality Checker uses thereExists() forAll()
78
Example of Pattern in Everyday Applications Program tree A visitor for printing all nodes. Another for type checking. Yet another for generating code Do not want to put all of this code in tree class. In any case, printing should not be in tree.
79
Java 1.4 Assertion Support AssertionError exception = AnAssertionFailedException assert = AnAsserter.assert(, “”); assert : = AnAsserter.assert(, string representation of Assertions can be dynamically turned on or off. No support for quantifiers. In 1.3 assert is a keyword but has no implementation
80
Modularizing assertion code How to not clutter regular with assertion code. How to turn off library-based assertion code?
81
Proxy classes Put assertion code in special classes that are proxies for real classes. Factory, factory selectors and factory methods can be used to choose between proxy and regular classes.
82
Counter package models; public class ACounter implements Counter { int counter = 0; public void add (int amount) { counter += amount; } public int getValue() { return counter; }
83
Asserting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter extends ACounter { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); }
84
Example of Proxy Pattern ACounter Counter implements AChecked Counter IS-A implements
85
Proxy Pattern Real Subject Class Subject Interface implements Proxy Class IS-A implements
86
Proxies in everyday apps Proxy is a stand-in for real subject. Adds to one or more method implementations Web proxies –Cache data –Redirect to nearest server Proxy may –Log –Cache (remote proxy) –Provide access control –Assert
87
Asserting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter extends ACounter { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); } Alternative way of doing proxy?
88
Delegating Proxy Proxy HAS-A reference to the Subject and not IS-A Subject. Proxy is called delegator and subject is called delegate
89
Delegating Proxy-based Counter package models; import assertions.AnAsserter; public class ADelegatingCheckedCounter implements Counter { Counter counter; public ADelegatingCheckedCounter (Counter theCounter) { counter = theCounter; } public void add (int amount) { int oldVal = counter.getValue(); counter.add(amount); int newVal = counter.getValue(); AnAsserter.assert(newVal == oldVal + amount, "New counter:" + newVal + " != old counter:" + oldVal + " + amount:" + amount); } public int getValue() { return counter.getValue()b; }
90
Example of Delegating Proxy ACounter Counter implements AChecked Counter HAS_A implements
91
Delegating Proxy Real Subject Class Subject Interface implements Proxy Class Has-A implements
92
Inheritance vs. Delegation Real Subject Class Subject Interface implements Proxy Class HAS-A implements Real Subject ClassSubject Interface implements Proxy Class IS-A
93
OMT (Object Modeling Technique) Real Subject Class Subject Interface implements Proxy Class HAS-A implements Real Subject ClassSubject Interface implements Proxy Class IS-A
94
Added notation of implements Real Subject Class Subject Interface implements Proxy Class HAS-A implements Real Subject ClassSubject Interface implements Proxy Class IS-A
95
Inheritance vs. Delegation Reused ClassReused Interface Reusing ClassReusing Interface Reused ClassReused Interface Reusing ClassReusing Interface
96
Inheritance vs. Delegation: Instances Reused Class Reusing Class Reused Class Reusing Class instance Physical component
97
Inheritance vs. Delegation: Init methods/Constructor Reused Class Reusing Class Reused Class Reusing Class declares Constructor/init (delegate, …) declares Constructor/init (…)
98
Inheritance vs. Delegation: Init methods/Constructor Reused Class Reusing Class Reused Class Reusing Class declares Constructor/init (delegate, …) declares Constructor/init (…)
99
Asserting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter extends ACounter { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); }
100
Delegating Proxy-based Counter package models; import assertions.AnAsserter; public class ADelegatingCheckedCounter implements Counter { Counter counter; public ADelegatingCheckedCounter (Counter theCounter) { counter = theCounter; } public void add (int amount) { int oldVal = counter.getValue(); counter.add(amount); int newVal = counter.getValue(); AnAsserter.assert(newVal == oldVal + amount, "New counter:" + newVal + " != old counter:" + oldVal + " + amount:" + amount); } public int getValue() { return counter.getValue()b; }
101
Inheritance vs. Delegation: Reused Methods Reused Class Reusing Class Reused Class Reusing Class declares Reused method m declares m stub delegate.m() declares Delegation is more work!
102
Asserting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter extends ACounter { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); }
103
Delegating Proxy-based Counter package models; import assertions.AnAsserter; public class ADelegatingCheckedCounter implements Counter { Counter counter; public ADelegatingCheckedCounter (Counter theCounter) { init(theCounter); } public void add (int amount) { int oldVal = counter.getValue(); counter.add(amount); int newVal = counter.getValue(); AnAsserter.assert(newVal == oldVal + amount, "New counter:" + newVal + " != old counter:" + oldVal + " + amount:" + amount); } public int getValue() { return counter.getValue(); }
104
Inheritance vs. Delegation: Overridden Methods Reused Class Reusing Class Reused Class Reusing Class declares Reused method m declares Overriding method m declares
105
Asserting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter extends ACounter { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); }
106
Delegating Proxy-based Counter package models; import assertions.AnAsserter; public class ADelegatingCheckedCounter implements Counter { Counter counter; public ADelegatingCheckedCounter (Counter theCounter) { init(theCounter); } public void add (int amount) { int oldVal = counter.getValue(); counter.add(amount); int newVal = counter.getValue(); AnAsserter.assert(newVal == oldVal + amount, "New counter:" + newVal + " != old counter:" + oldVal + " + amount:" + amount); } public int getValue() { return counter.getValue(); }
107
Inheritance vs. Delegation: Super calls Reused Class Reusing Class Reused Class Reusing Class declares Reused method m declares Method n super.m() Method n delegate.m() declares
108
Asserting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter extends ACounter { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); }
109
Delegating Proxy-based Counter package models; import assertions.AnAsserter; public class ADelegatingCheckedCounter implements Counter { Counter counter; public ADelegatingCheckedCounter (Counter theCounter) { init(theCounter); } public void add (int amount) { int oldVal = counter.getValue(); counter.add(amount); int newVal = counter.getValue(); AnAsserter.assert(newVal == oldVal + amount, "New counter:" + newVal + " != old counter:" + oldVal + " + amount:" + amount); } public int getValue() { return counter.getValue(); }
110
Inheritance vs. Delegation: Callbacks Reused Class Reusing Class Reused Class Reusing Class declares Reused method m declares Method n this.n() declares Method n with possibly more access delegator.n() Abstract or Overridden Method n
111
Calls vs. Callbacks Calls –calls from reusing class to reused class Callbacks –calls from reused class to reusing class. –not to implement a symbiotic relationship –done to service calls –not useful if no calls are made
112
Inheritance vs. Delegation: Constructors/Init methods for callbacks Reused Class Reusing Class Reused Class Reusing Class declares Constructor/init (delegator, …) Constructor/init (…)
113
public abstract class AnAbstractTitleToCourseMapper implements TitleToCourseMapper { public AnAbstractTitleToCourseMapper() { fillCourses(); } abstract RegularCourse getRegularCourse(); abstract FreshmanSeminar getFreshmanSeminar(); void fillCourses() { RegularCourse introProg = getRegularCourse (); FreshmanSeminar legoRobots = getFreshmanSeminar();... } public String getTitle() { return titleToCourseMapper.getTitle(); }... } Abstract class
114
Concrete course user public class ATitleToCourseMapper extends AnAbstractTitleToCourseMapper { RegularCourse getRegularCourse() { return new ARegularCourse(); } FreshmanSeminar getFreshmanSeminar() { return new AFreshmanSeminar(); }
115
Delegate class public class ATitleToCourseMapperDelegate implements TitleToCourseMapper { CourseFactory delegator; public ATitleToCourseMapperDelegate(CourseFactory theDelegator) { delegator = theDelegator; fillCourses(); } void fillCourses() { RegularCourse introProg = delegator.getRegularCourse (); FreshmanSeminar legoRobots = delegator. getFreshmanSeminar();... } public String getTitle() { return titleToCourseMapper.getTitle(); }... }
116
Concrete course user public class ATitleToCourseMapperDelegator implements TitleToCourseMapper, CourseFactory{ public RegularCourse getRegularCourse() { return new ARegularCourse(); } public FreshmanSeminar getFreshmanSeminar() { return new AFreshmanSeminar(); } public String getTitle() { return titleToCourseMapper.getTitle(); }... }
117
Access problems Occur with callbacks. And if reusing class accesses variables of reused class
118
Inheriting String Database public class AStringDatabase extends AStringHistory implements StringDatabase { public void deleteElement (String element) { shiftUp(indexOf(element)); } public int indexOf (String element) { int index = 0; while ((index < size) && !element.equals(contents[index])) index++; return index; } void shiftUp (int startIndex) { int index = startIndex ; while (index + 1 < size) { contents[index] = contents[index + 1]; index++; } size--; } public boolean member(String element) { return indexOf (element) < size; } public void clear() { size = 0; }
119
Delegating String Set package collections; import enums.StringEnumeration; public class ADelegatingStringDatabase implements StringDatabase { AStringHistory stringHistory = new AStringHistory(); public void deleteElement (String element) { shiftUp(indexOf(element)); } public int indexOf (String element) { int index = 0; while ((index < stringHistory.size) && !element.equals(stringHistory.contents[index])) index++; return index; } void shiftUp (int startIndex) { int index = startIndex ; while (index + 1 < stringHistory.size) { stringHistory.contents[index] = stringHistory.contents[index + 1]; index++; } stringHistory.size--; } Class as type and accessing non public variables in same package Instantiating delegate rather than getting reference from constructor.init method
120
Delegating String Set public boolean member(String element) { return indexOf (element) < stringHistory.size; } public void clear() { stringHistory.size = 0; } public int size() { return stringHistory.size(); } public String elementAt (int index) { return stringHistory.elementAt(index); } public StringEnumeration elements() { return stringHistory.elements(); } public void addElement(String element) { stringHistory.addElement(element); } Large number of forwarding stubs
121
Access issues Callbacks methods –must be given public access if interface types the delegator. –can be given protected or default access – but requires class types, delegator and delegator class in same package May need to give access to reused class variables also –through public methods –or putting delegate and delegator in same package, giving them protected/default access and using class as type
122
Cons of Delegation Need to instantiate multiple classes. Need to compose instances. Need to define stub methods Access problems
123
Pros of Delegation Reused ClassReused Interface Reusing ClassReusing Interface Reused ClassReused Interface Reusing ClassReusing Interface
124
Substituting Reused Class Reused Class 2Reused Interface Reusing Class 2Reusing Interface Reused Class 2Reused Interface Reusing ClassReusing Interface
125
Substituting Reused Class Another implementation of reused interface –in inheritance requires another implementation of reusing class that duplicates methods of original reusing class. –In delegation simply requires a different object to be passed to constructor
126
New Inheriting Proxy Class package models; import assertions.AnAsserter; public class ACheckedCounter2 extends ACounter2 { public void add (int amount) { int oldCounter = counter; super.add(amount); AnAsserter.assert(counter == oldCounter + amount, "New counter:" + counter + " != old counter:" + oldCounter + " + amount:" + amount); }
127
Orginal Delegating Proxy package models; import assertions.AnAsserter; public class ADelegatingCheckedCounter implements Counter { Counter counter; public ADelegatingCheckedCounter (Counter theCounter) { counter = theCounter; } public void add (int amount) { int oldVal = counter.getValue(); counter.add(amount); int newVal = counter.getValue(); AnAsserter.assert(newVal == oldVal + amount, "New counter:" + newVal + " != old counter:" + oldVal + " + amount:" + amount); } public int getValue() { return counter.getValue(); }
128
Multiple Reusing Classes Reused Class Reusing Class 1 Reused Class Reusing Class 1
129
Reused Class Reusing Class 1 Reused Class Reusing Class 2 Reusing Class 1 Multiple Reusing Classes Inheritance –each possible combination of reusing classes bound at compile time –Duplicates code Delegation –can be changed dynamically
130
AConsoleControllerAndViewAndJOptionView AConsoleControllerAndView ACounterJOption View Delegation-based MVC ACheckedCounter ACounterController ACounterConsole View
131
Inheritance-based MVC ACounter ACounterWithConsoleAndJOptionViewAndController ACounterWithConsoleView ACounterWithConsoleAndJOptionView No facades or notification needed
132
Controller with Console View package views; import models.ACounter; public class ACounterWithConsoleView extends ACounter { void appendToConsole(int counterValue) { System.out.println("Counter: " + counterValue); } public void add (int amount) { super.add(amount); appendToConsole(getValue()); }
133
Controller with Console and JOption View package views; import models.ACounter; import javax.swing.JOptionPane; public class ACounterWithConsoleAndJOptionView extends ACounterWithConsoleView { void displayMessage(int counterValue) { JOptionPane.showMessageDialog(null, "Counter: " + counterValue); } public void add (int amount) { super.add(amount); displayMessage(getValue()); }
134
Controller with Console and JOption View and Controller package controllers; import views.ACounterWithConsoleAndJOptionView; import java.io.IOException; public class ACounterWithConsoleAndJOptionViewAndController extends ACounterWithConsoleAndJOptionView implements CounterWithConsoleViewAndController { public void processInput() { while (true) { int nextInput = Console.readInt(); if (nextInput == 0) return; add(nextInput); }
135
AConsoleControllerAndView Alternative: Delegation-based Configuration AnObservable Counter ACounterController ACounterConsole View ACounterJOption View
136
Alternative: Inheritance-based MVC ACounter ACounterWithConsoleViewAndController ACounterWithConsoleView New class
137
Controller with Console and View and Controller package controllers; import views.ACounterWithConsoleAndJOptionView; import java.io.IOException; public class ACounterWithConsoleViewAndController extends ACounterWithConsoleView implements CounterWithConsoleViewAndController { public void processInput() { while (true) { int nextInput = Console.readInt(); if (nextInput == 0) return; add(nextInput); } Only difference between it and previous inheriting controller
138
Inheritance vs. Delegation: Distribution Reused Class Reusing Class Reused Class Reusing Class instance Data and methods of both classes at one location Can be in two different locations (client/server)
139
Reused Class 1 Reusing Class Reused Class 1 Reusing Class Reused Class 2 Reused Class 1 Reusing multiple classes In Java reusing multiple classes through inheritance not an option. Can always have multiple instance variables
140
Pros and Cons of Delegation Can change reused class without changing reusing class. Multiple reusing classes can share reused class variables simultaneously. Variables and methods of reusing and reused class can be on separate computers. Works in single inheritance languages Need to instantiate multiple classes. Need to compose instances. Need to define stub methods Access problems Need to weigh pros and cons When benefits of delegation don’t apply use inheritance That is why inheritance exists.
141
Inheritance is a bad idea If reusing class has “with” in its name: –ACounterWithConsoleView If more than one way to do inheritance –Controller can be subclass of view –Or vice versa If some of the inherited methods are not used: –AStringHistory extends Vector Adds methods to add and access string elements rather than object elements addString(), stringAt() –does not use removeElement() etc If reusing class should not be used wherever shared class is used –even if all methods are reused.
142
Inheritance is a bad idea public ACartesianPoint implements Point { int x, y; public APoint(int theX, int theY) { x = theX; y = theY;} public int getX() { return x } public int getY() { return y } public double getRadius() { return Math.sqrt(x*x + y*y); }.public double getAngle() { return Math.atan(y/x);} } public ASquare extends APoint implements Square { int sideLength; public ASquare (int theX, int theY, int theSideLength) { x = theX; y = theY; sideLength = theSideLength} public int getSideLength() {return sideLength}; } public ASquare extends APoint implements Square { Point center; int sideLength; public ASquare( Point theCenter, int theSideLength) { center = theCenter;sideLength = theSideLength; } public int getX() {return center.getX();} public int getY() {return center.getY();} public int getRadius() {return center.getRadius();} public int getAngle() {return center.getAngle();} public int getSideLength() {return sideLength;} } ARectangle IS- NOT-A ACartesianPoint!
143
Inheritance vs. Delegation in Popular Software: Toolkits Widget Class Widget User Widget Class Widget User declares super call delegate call declares Action Performed Java 1.0 Later versions
144
Inheritance vs. Delegation in Popular Software Main problem with inheritance based widgets –Widget users could not be subclass of any other class because of multiple inheritance Multiple objects can receive events from same widget. Widget implementation can be switched Observer supports both approaches –Inherited Observer class vs. Delegated PropertyChangeSupport Threads support both approaches –Inheritable Thread class vs. Implemented Runnable Interface
Similar presentations
© 2024 SlidePlayer.com Inc.
All rights reserved.