CS 342 Lecture -*- Outline -*- ** Object-oriented design *** Motivation What is object-oriented programming? a paradigm compare "structured programming" Better question: what is object-oriented design? **** The problem: coping with change in software **** partial solution: information hiding and data abstraction information hiding (Parnas 71-2), design with data abstractions (Liskov 72) (Hoare 72) These build on ideas already in Simula I (1962-4) and Simula 67 (class concept). **** other problems: reuse of designs regularity, user extensions to system users want to extend design themselves (new functions, new data types) Simula 67 addressed some of these problems too These addressed problems are addressed by object-oriented design try to reuse existing abstractions define types by extension of existing ones (inheritance) use dynamic binding (table-driven design) allow users to add types later *** Objects = data + operations Smalltalk (as in Simula 67) view: operations are part of each object ______________________ | top = 3 | | | | _________ push: | | | | | |_______| | | | 3 | pop | |_______| | | | 2 | | | |_______| top | | 1 | | | |_______| | | | |______________________| state = instance variables (= CLU rep) operations = code that responds to messages (methods) having operations as part of objects allows one to use message passing *** Classes operations not really stored in each object (too much space) class = description of objects (module that defines rep, ops) class object = run-time storage for operations also object where can send messages to create instances each object is an instance of some class ---------------- % Objects in CLU OOstack = cluster is create, get_dispatcher rep = record[elems: array[int], dispatcher: dispatcher_type] dispatcher_type = proctype(string, sequence[any]) returns(any) create = proc() returns(cvt) return(rep${elems: array[int]$new(), dispatcher: send}) end create get_dispatcher = proc(s: cvt) returns(dispatcher_type) return(s.dispatcher) end get_dispatcher send = proc(messageName: string, args: sequence[any]) returns(any) if messageName = "push:" then return(push(force[OOstack](args[1]), force[int](args[2]))) elseif messageName = "pop" then return(pop(force[OOstack](args[1]))) elseif messageName = "top" then return(top(force[OOstack](args[1]))) else signal failure("message " || messageName || " not understood") end end send push = proc(s: cvt, e: int) returns(any) array[int]$addh(s.elems, e) return(up(s)) end push pop = proc(s: cvt) returns(any) array[int]$remh(s.elems, e) return(up(s)) end pop top = proc(s: cvt) returns(any) return(array[int]$top(s.elems)) except when bounds: signal failure("empty") end end top end OOstack ---------------- *** Message Passing (generic invocation, dynamic binding) select the operation from the object, call it => like procedure call using dynamic overloading **** message = operation name + arguments ______________________ ____________ | | | \ / | __ | | | \ / | | push: | \/ | __ | | | push: 1 | | | |__________| | pop \ \ \ | | | | | top | | |______________________| object ^ message ^ --------------------- ;; message passing (sort of) in CLU x: OOstack := OOstack$create() x.dispatcher("push:", sequence[any]$[x,1]) --------------------- But in CLU can't really send messages to objects of any type, since x.dispatcher is only sugar. Want to write a procedure so can say send("push:", sequence[any]$[x,1]) for an object x of an *arbitrary* type! Syntax in ST is "myStack push: 1." **** implementation (in Smalltalk) *draw picture showing class link, class object, dispatch table Steps in message sending: receiver = object that is sent message 1. let c be class of receiver 2. find method stored in class c (if not found give error message, dynamic type check) 3. bind self to receiver and actuals to formals, alloc temps 4. execute the code of the method **** Message Passing and binding times ***** early (static) binding call bound to a module (DU) at compile time specific implementation of that module selected at link time e.g., in CLU stack$push(myStack, 1) ***** delayed (late, dynamic) binding call bound to implementation later than link time e.g., in Smalltalk myStack push: 1 dispatch needed at run-time in general, e.g. can depend on input --------------- (smalltalk inquire: 'use Stack? ') ifTrue: [myStack <- Stack new] ifFalse: [myStack <- LStack new] myStack push: 1 --------------- *** When to use message passing (dynamic binding) in design? Office Automation example: (Cox) various containers: desktops, envelopes, mailboxes, filefolders each may contain many diff types of objects (memos, files..) mailbox displaySummary -- displays a summary menu of contents let each object in mailbox contain a "typeid" (tells type) need map from typeid to method for getting summary info if local to displaySummary (e.g., case stmt), then implementor must change it for new types ==> map should be global to displaySummary ways to represent global map: centralized (table-driven) distributed among the types (message passing) so message passing is way to allow addition of types without causing other changes inheritance allows broadcast of changes (and regularity)