CS 1321 Supplemental Notes

Guide to Object Oriented Programming in Scheme: Part II




CONTINUED FROM PART I:

------------------------------ Topics this week: 4. Inheritance in Scheme: (a) Super as a variable (b) Initializing (c) Inheriting Service Manager (d) Overriding superclass methods ----------------------------- References to give out: - Lectures for Hw 12: Objects & Inheritance - Section 39 (p. 572) "Changing Compound Values" in the text ----------------------------- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 4. INHERITANCE IN SCHEME ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Finally we are ready to tackle hierarchies of objects! Every complex data we have studied this semester has had some way of "nesting". Every time we encounter nesting, the code gets messy... Objects, however, don't really "nest", per se - they "inherit". Some background info on inheritance to help you deal with CS this week: - a general object that abstracts properties of other objects is called a "superclass" - objects that "inherit" properties from a superclass are "subclasses" Inheritance is used for: - reducing the size of OOP code - generalizing & simplifying objects that are related to each other - reusing code for similar objects - passing properties of one general object onto other specific ones - making small variations on objects without recreating them entirely IMPORTANT NOTE: The whole has-a/is-a thing will need some reiteration. Composition: An object *has a* piece of data Inheritance: An object *is a* subclass of another object In Scheme you must do several things to model inheritance: 1. In each subclass, include a "super" class instance as a variable 2. When initializing, initialize the super instance too 3. Let the super handle the "else" in your service manager This means each subclass will have a copy of both its own locals and a "super" object's locals, and can use the superclass' service manager instead of needing to rewrite those super functions. Never mind the theory so much, diving right into an example is better: ;;;;;;;;;;;;;;;;;;;;;;; ;; SOME EXAMPLE CODE ;; ;;;;;;;;;;;;;;;;;;;;;;; (Very limited objects to keep this from being 5 pages of code... ;) Let's write an abstract superclass for... um, tickets. They will include: general/abstract tickets / \ / \ / \ concert speeding tickets tickets - General tickets have an ID number (or a serial number or whatever). - Concert tickets, besides the ID num, also have the name of a concert. - Speeding tickets also have an over-the-limit speed and a $$$ amount. ------------ SUPER -------------------------------------- ;; Contract: make-ticket: number --> (lambda (symbol) ...) ;; Purpose: To make a general ticket with a serial number, and ;; return its service manager ;; (define (make-ticket num) (local [ ;; DATA: ;; (define ID 0) ;; <-- this ticket's serial number ;; CONSTRUCTOR: ;; (define (init number) ;; take an argument from the user, (set! ID number)) ;; set the ID to be that number ;; ACCESSOR: ...but *no modifier* - ticket's number stays ;; (define (get-ID) ID) ;; <-- simply return the local ID ;; SERVICE MANAGER: ;; (define (service-manager request) (cond [(symbol=? request 'get-ID) get-ID] [else ;; invalid request: (error 'ticket "Ticket manager error - no such action")] )) ] ;; end of locals for make-ticket (begin ;; INITIALIZING: ;; (init num) ;; RETURNING SERVICE MANAGER: ;; service-manager) )) -------------------------------------------------- This class will allow for tickets with ID numbers - that's about it. All we can use is the ((some-ticket 'get-ID)) function. To get more specific variations of tickets, we certainly don't want to write all that messy Scheme code again, so we subclass & inherit the ID number stuff - but then we "extend" the properties to do other things: ----------SUBCLASSES -------------------------------- ;; Contract: make-concert-ticket: (void) --> (lambda (symbol) ...) ;; Purpose to construct a concert ticket, extending regular tickets ;; (define (make-concert-ticket) (local [ ;; SUPERCLASS INSTANCE: ;; (define super 0) ;; <-- blank for now... ;; DATA: ;; (define concert 0) ;; concert name, also blank for now ;; ACCESSOR - but no modifier again - why? ;; (define (get-concert) concert) ;; CONSTRUCTOR: ;; (define (init name num) (begin (set! super (make-ticket num)) ;; <-- initializing super! (set! concert name) )) ;; SERVICE MANAGER: ;; (define (service-manager sym) (cond [(symbol=? sym 'get-concert) get-concert] [(symbol=? sym 'init) init] ;; Letting super handle the rest: [else (super sym)] )) ] ;; end of locals for concert ticket ;; NO initialization! amazingly enough... but as always: ;; service-manager )) ;; Contract: make-speeding-ticket: (void) --> (lambda (sym) ...) ;; Purpose: To construct a speeding ticket extending main ticket ;; (define (make-speeding-ticket) (local [ ;; DATA: ;; (define super 0) ;; superclass instance (define spd 0) ;; amount of speeding (define amt 0) ;; amount of fine :( ;; ACCESSORS: ;; (define (check-amt) amt) (define (check-spd) spd) ;; MODIFIER...? ;; (define (but-officer!) (begin (display "That's it! You asked for it...") (set! amt (* 2 amt)) )) ;; CONSTRUCTOR: ;; (define (init speed DL) (begin (set! super (make-ticket DL)) ;; <-- initializing super (set! spd speed) (set! amt ;; <-- calculate $$$ amt (* (square speed) 5)) )) ;; SERVICE MANAGER: ;; (define (police-officer task) (cond [(symbol=? task 'check-spd) check-spd] [(symbol=? task 'check-amt) check-amt] [(symbol=? task 'whine) but-officer!] [(symbol=? task 'init) init] [else (super task)] )) ;; <-- let super handle it ] ;; end of speeding ticked locals ;; RETURNING THE SERVICE MANAGER: ;; police-officer )) -------------------------------------------------- Each of the subclasses can also run superclass methods, because each contains an entire "super" instance, and the service manager passes on any foreign function names to *it*. > (define mymistake (make-speeding-ticket)) > ((mymistake 'init) 20 936052884) > ((mymistake 'get-ID)) ;; <-- a superclass function! 936052884 Therefore you "inherit" - really you keep a copy of - the superclass data and behavior, and use it as a building block. Furthermore, each subclass has methods that are unique to that subclass - for instance try: > ((mymistake 'check-amt)) > ((mymistake 'whine)) > ((mymistake 'check-amt)) Obviously the generic "abstract" plain-old ticket does not do this. Only the speeding ticket subclass provides those functions *in addition to* the superclass functions. To summarize INHERITANCE: 1. Create SUPERCLASSES & SUBCLASSES 2. Put a "super" DATA variable in each subclass 3. INITIALIZE both current and "super" when constructing 4. In your service manager, USE THE SUPER'S SERVICE MANAGER after all the local options are checked out. ;;;;;;;;;;;;;;;;;;;;;;; ;; METHOD OVERRIDING ;; ;;;;;;;;;;;;;;;;;;;;;;; Sometimes the general superclass methods just aren't "good enough" for certain specific functions. You would like to make use of them - but you'd also like to EXTEND their properties. Therefore, you override: Overriding - when a subclass has its own version of a superclass method - often the subclass method is more elaborate/specific - often the subclass method will build on the super's For example: ---------------------SUPERCLASS------------------------------ (define (make-duck name) (local [ ;; each duck has a name: ;; (define name 'blank) ;; each duck can say 'quack!' ;; (define (speak) (begin (display name) (display " says: Quack!") (newline)) )) ;; each duck can get/set its name: ;; (define get-name (lambda () name) (define set-name (lambda (N) (set! name N)) ) ;; each duck has a service manager: ;; (define (service-manager request) (cond [(symbol=? request 'get-name) get-name] [(symbol=? request 'set-name) set-name] [(symbol=? request 'speak) speak] [else (error 'duck "Quack! Stop that!")] )) ] ;; end of duck locals service-manager )) ------------------------------------------------------------ Then if we wanted to build the AFLAC (tm) duck, which "is-a" duck (it is a type of duck, therefore it is a subclass of ducks), and we wanted it to also say "AFLAC" (tm), besides the regular quacking sound, we could make it have its own speak method: -----------------------SUBCLASS---------------------------- (define (make-AFLAC-duck) (local [ ;; instance of the SUPER: ;; (define super (make-duck "The AFLAC Duck")) ;; <-- giving it a name ;; overriding the 'speak' method! ;; (define (speak) (begin ((super 'speak)) ;; <-- using regular quack (display "AFLAC!!!") ;; <-- then adding MORE to (newline) )) ;; extend the super ;; service manager: ;; (define (service-manager request) (cond [(symbol=? request 'speak) speak] ;; overriding! [(symbol=? request 'set-name) void] ;; also changing! [else (super request)] )) ] ;; end of AFLAC locals service-manager )) ----------------------------------------------------------- As you can see, this AFLAC duck borrows a lot of methods from the general duck, but also changes some of them. Notice that the 'speak and 'set-name service manager requests never go to the super! That is because this subclass overrides them. The AFLAC duck has its own version of the 'speak method, and it also does not let anyone change its name. Very feisty: ;; Try to explore the differences ;; between the super and the subclass: ;; > (define Donald (make-duck "Donald")) > (define AFLAC (make-AFLAC-duck)) > ((Donald 'get-name)) ;; <-- method common to both > ((AFLAC 'get-name)) ;; super and subclass > ((Donald 'speak)) ;; <-- regular quack > ((AFLAC 'speak)) ;; <-- OVERRIDDEN by subclass > ((Donald 'set-name) "Donald Duck")) ;; <-- regular change-name > ((AFLAC 'set-name)) ;; <-- OVERRIDDEN by subclass > ((Donald 'blaargh)) ;; error handling still the same? > ((AFLAC 'blaargh)) Try all of those out and see how this works. In short, METHOD OVERRIDING: 1. Have a subclass with a method that is THE SAME NAME as a superclass method. 2. Feel free to CALL the superclass' method as part of your more specific version. 3. Modify the SUBCLASS SERVICE MANAGER to handle the methods you have overridden, so that they are not passed on to the super in the ELSE clause. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 5. BONUS NOTES: BIG-O NOTATION & RULES ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Big-O definitions & rules: - efficiency measure of time or resources - worst case, limiting estimate, based on input size N - function performs "on the order of" O(f(N)) steps - constants are entirely ignored! Some common Big-O values:
BIG O Description Examples
O(1) constant (vector-ref, vector-set!, first, node-data...)
O(logN) logarithmic (binary search, full & balanced BST ops)
O(N) linear (lists, vectors, unbalanced trees, graphs)
O(NlogN) well... NlogN, as in, almost linear (mergesort & quicksort)
O(N^2) quadratic (insertion sort, selection sort, matrix ops)
O(k^N) exponential (augmenting fibonacci, anything ridiculously inefficient)

THAT IS ALL!    (collective sigh of relief)


AFLAC is a registered trademark of AFLAC Inc. Co. Ltd. and is neither endorsed nor approved by the GA Tech College of Computing. Used for educational purposes only, subject to certain restrictions where applicable. State taxes and federal regulations may apply. Void where prohibited. Batteries not included. See store for details.