CS 541 Lecture -*- Outline -*- * OO programming tactics This is a discussion of tactics for a discussion of strategy, see the object-oriented-design unit Q: what ways of programming are enabled by the mechanisms of OO languages like Smalltalk? main mechanisms: - objects, message passing, inheritance, blocks, assignment ** mutation (from assignment) *** mutatator operations ------------------------------------------ MUTATION | l | l := OrderedCollection new. l add: 3 ==> OrderedCollection(3) ------------------------------------------ however, instead of just having operations that get and set values, have the object do something higher level make a higher-level language from your design, not an assembly language *** aliasing ------------------------------------------ ALIASING def: x and y are aliases for an object if Uses of aliasing: ------------------------------------------ ... some sequence of messages sent to x can be observed as a change by sending messages to y. Q: How can you use aliasing in a program? Q: How can you prevent aliasing? make a copy, or design the object to be immutable! A danger with aliasing is ... ------------------------------------------ EXPOSING THE REP class: RepExposureDemo subclass of: Object instance variables: rep "class methods" with: aSet ^ self new initialize: aSet "instance methods" initialize: aSet rep := aSet contains: anElement ^ rep contains: anElement What if a client does the following? | mySet it | mySet := Set new. it := RepExposureDemo with: mySet. mySet add: #isOk. it includes: #isOk ------------------------------------------ Q: What could go wrong? ------------------------------------------ HOARE LOGIC BASEICS Triple {P} S {Q} means if P is true and S terminates then Q must be true Assigment axiom {Q[e/x]} x := e {Q} e.g., {(x = 5)[x+1/x]} x := x+1 {x = 5} ------------------------------------------ What could go wrong? Imagine an IntCell type that is aliased... ** encapsulation (from objects) objects package both data and operations, methods Q: Is there a fundamental difference between data and operations? can we treat operations as data? yes (e.g., strategy pattern) can we treat data as operations? yes (interpreter pattern) *** composition vs. inheritance ------------------------------------------ COMPOSITION vs. INHERITANCE Given: class: Point subclass of: Object instance variables: x y composition |-----------------| |-----------| | Rectangle |------->*| Point | |-----------------| |-----------| class: Rectangle subclass of: Object instance variables: upperLeft, lowerRight inheritance |-----------------| | Point | |-----------------| | | / \ |-----------------| |-----------| | Rectangle |-------->| Point | |-----------------| |-----------| class: Rectangle subclass of: Point instance variables: lowerRight ------------------------------------------ Q: should Rectangle be represented by 2 points or should it be a subclass of Point? Q: What are the maintainence advantages or problems? do you want Rectangle to respond to messages x and y (for one point)? When have object as a component can hide and translate messages to it When inherit can't easily hide messages, or translate. but in Smalltalk-80, Set is a subclass of Bag... ** protocols and polymorphism (from message passing) *** protocols, types, and behavior ------------------------------------------ PROTOCOLS, TYPES, and BEHAVIOR def: the protocol (interface) of an object is def: the behavioral protocol of an object is def: a type is def: S is a subtype of T if def: S is a behavioral subtype of T if ------------------------------------------ ... the set of messages it can respond to (without error, sensibly) ... its protocol together a specification of the effect (meaning) of each message Q: What should a type be? ... a protocol (or perhaps the name of a protocol) or perhaps better: a set of objects that obey a protocol. ... the protocol of T is part of the protocol of S, i.e., if the interface of T contains the interface of S Q: What would it mean for S to not be a subtype of T? Q: What should a behavioral subtype be? ... it is a subtype and also that its objects obey the behavioral protocol of T Q: What would it mean for S to not be a behavioral subtype of T, and still be a subtype of T? *** subtype vs. inheritance (see Gamma et al., pages 16-17) Q: What does subtyping have to do with inheritance? Q: What do we know about these? ------------------------------------------ SUBTYPING VS. INHERITANCE | ===================|====================== class | | subclass | | type | | subtype | | behavioral subtype | | ------------------------------------------ can make analogy that a subtype inherits protocol and a behavioral subtype inherits behavior (or specifications of it) e.g., Set and Interval want Interval (immutable) to be a subtype of Set, but don't want to use the same representation. (in Smalltalk/V, Interval does not inherit from Set) ------------------------------------------ ORTHOGANALITY EXAMPLE IntSetType ^ IntSetClass | subtype | IntervalType IntervalClass ------------------------------------------ Q: How does C++ handle this? Java? Q: What if we required subclass = subtype? We'll see that when we study binary methods (later) that a subclass doesn't necessarily generate a subtype. Q: What can we do to make a subclass not be a behavioral subtype? override methods (e.g., to loop forever or give errors) we'll come back to this issue when we discuss abstract classes *** polymorphism ------------------------------------------ POLYMORPHISM def: code is *polymorphic* if e.g., displayAll: objList objList do: [ :o | o display ] ------------------------------------------ ... it works (in approximately the same way) for 2 or more classes (we could always say it works for just one type, the one with the required protocol, so "classes" is really needed) because of message passing, all code in Smalltalk is polymorphic; it works for any type of object that obeys a certain protocol. (in C++ you have to choose to get message passing and hence don't always have this flexibility) e.g., a window may contain a list of objects to display, its refresh method sends the "displayOn:" message to each, doesn't have to do case TypeOf(o) rectangle: rectangle$display(o) circle: circle$display(o) etc. so it's polymorphic works even for stuff you haven't thought of yet! when add new type of data object don't have to go through the code looking for the case stmts Q: What's the language design tradeoff here? ** code reuse (from inheritance) *** factoring behavior so relevant parts can be inherited (with parts done by sending message to self) ------------------------------------------ FROM INHERITANCE What benefits? 1. Factoring ------------------------------------------ ... reuse ... split methods into part that is to be overridden (printString) and part for subclasses to inherit (printStringElements) draw picture of this split would be nice if didn't have to plan ahead... see printString and printStringElements discussion in previous section *** abstract classes we can also apply this factoring idea to make class hierarchies also be subtype hierarchies e.g., suppose we have a class LinkedList, and want to make a class SortedList from it, which has a binary search method but then if we want to define an "addToEnd" method, LinkedList doesn't support it so need an abstract class "List" to hold common code ------------------------------------------ ABSTRACT CLASSES def: an abstract class is def: a deferred method is ------------------------------------------ ... a class that *cannot* be instantiated, because it defers its the implementation of some of its methods without instances, site of common code (often def. of protocol) e.g. collection design issue: not identified in Smalltalk, so no way to enforce ... a method that is sent to self in a class but not implemented there; it is to be implemented by subclasses Q: What are these called in C++? *** frameworks a framework is a set of OO classes, usually with many abstract classes. e.g., for operating systems, compilers, finance, etc. Draw class diagram *** using message passing instead of tests in code message passing checks a type code already, can use it instead of if or case statements ------------------------------------------ USE MESSAGE PASSING INSTEAD OF TESTS Boolean / \ True False "instance methods for True" ifTrue: b1 ifFalse: b2 ^ b1 value "instance methods for False" ifTrue: b1 ifFalse: b2 ^ b2 value ------------------------------------------ ------------------------------------------ FOR YOU TO DO Program: LISPList / \ Empty Cons "instance methods in LISPList:" length (self isEmpty) ifTrue: [^0] ifFalse: [^ 1 + self tail length] deferred methods: head, tail, isEmpty Notes: (Empty new) head gives an error Cons has instance variables: hd,tl and method head:tail: to initialize ------------------------------------------ could add class Contig for contiguous intervals Q: How would you do that in Haskell? fill in the following... ------------------------------------------ ADTs vs. OO Programming Empty | Cons ====================|===================== head tail isEmpty ------------------------------------------ columns are classes, rows are procedures as in ADTs cf. paper by Cook