+ All Categories
Home > Documents > Object-oriented Programming and Design 1 Polymorphism Poly => many Morph => shape Variables take on...

Object-oriented Programming and Design 1 Polymorphism Poly => many Morph => shape Variables take on...

Date post: 03-Jan-2016
Category:
Upload: sabina-bradley
View: 226 times
Download: 0 times
Share this document with a friend
Popular Tags:
53
Object-oriented Programming and Design 1 Polymorphism Poly => many Morph => shape Variables take on many shapes, or many classes of objects.
Transcript

Object-oriented Programming and Design

1

Polymorphism

Poly => many

Morph => shape

Variables take on many shapes, or many classes of objects.

Object-oriented Programming and Design

2

Polymorphism

Polymorphism in OOP is caused by late-binding of procedure calls (message lookup).

Program can work with any object that has the right set of methods.

Object-oriented Programming and Design

3

Object Model for Payroll

EmployeeEmployeeTransaction

PayrollSystem

post:

date

Paycheck

Timecard

SalaryChangesalarypay, taxes

hoursWorkedvacation

postTo::

abstract classabstract class

abstract methodabstract method

** **

Object-oriented Programming and Design

4

Examples

post: aTransaction

aTransaction postTo: self.

transactations add: aTransaction

numbers inject: 0 into: [:sum :i | sum + i]

Object-oriented Programming and Design

5

Many classes with same interface

Examples

Collections

Numbers

Magnitude

Easier to remember method names

Polymorphic clients can use any class

Object-oriented Programming and Design

6

Polymorphic client

tasks := OrderedCollection new.

tasks add: Task new.

[tasks isEmpty]

whileFalse: [

task := tasks removeLast.

tasks addAll: task computeNewTasks]

Object-oriented Programming and Design

7

Polymorphic client

tasks := SortedCollection sortBlock: [:first :second | first priority > second priority].

tasks add: Task new.

[tasks isEmpty]

whileFalse: [

task := tasks removeLast.

tasks addAll: task computeNewTasks]

Object-oriented Programming and Design

8

Polymorphism instead of Case Statements

Smalltalk has no case statement.

OO programmers tend to replace case statements with message sending.

Instead of making a new case, add a subclass.

Object-oriented Programming and Design

9

Eliminating Cases

exp

case: 1 do: [...];

case: 15 do: [...].

Make classes for 1 and 15, with a method called msg.

exp msg or (dictionary at: exp) msg

Object-oriented Programming and Design

10

Class UndefinedObject

Class UndefinedObject has one instance -- nil.

All variables are initialized to nil.

Also used to indicate illegal value.

Object-oriented Programming and Design

11

Don’t Test Classes

UndefinedObject Object

isNil isNil

^ true ^ false

notNil notNil

^ false ^ true

Don’t test classes: use message sending and inheritance.

Object-oriented Programming and Design

12

Choices

bad

x class = UndefinedObject ifTrue: [...]

x = nil ifTrue: [...]

x isNil ifTrue: [...]

x ifNil: […]

best

Object-oriented Programming and Design

13

x ifNil: [ … ]

Object

ifNil: aBlock

^self

UndefinedObject

ifNil: aBlock

^aBlock value

Object-oriented Programming and Design

14

MagnitudeMagnitude ()

Number ()

....

Character ()

Date ('day' 'year')

Time ('hours' 'minutes' 'seconds')

Object-oriented Programming and Design

15

Number Hierarchy

NumberFraction ('numerator' 'denominator')

Integer ()

LargeNegativeInteger ()

LargePositiveInteger ()

SmallInteger ()

...

Object-oriented Programming and Design

16

Numbers

Numbers are part of the class hierarchy, not built into compiler.

Numbers understand +, -, *, /, <, <=, etc.

3 / 6 => 1 / 2

2 sqrt => 1.41421

3 + 4 * 2 =>

Object-oriented Programming and Design

17

Numbers are polymorphic

(3/4) * 0.45 + 4.6s2

4.9375

(0.45 + 4.6s2) / 17

0.297059

(0.45 + 4.6s2) truncated / 17

(5/17)

Object-oriented Programming and Design

18

Polymorphism, not conditionals

Don’t explicitly check class of argument.

Use polymorphism to make computation depend on class of argument.

aThing class = A

ifTrue: [self doStuffWith: aThing]

ifFalse: [self doSomethingElseWith: aThing]

Object-oriented Programming and Design

19

Original in Number

raisedTo: aNumber "Answer the receiver raised to aNumber."

aNumber isInteger ifTrue:["Do the special case of integer power"^ self raisedToInteger: aNumber].

self < 0 ifTrue:[ self error: self printString, ' raised to a non-integer power' ].

aNumber = 0 ifTrue: [^ 1]. "Special case of exponent=0"(self = 0) | (aNumber = 1) ifTrue:

[^ self]. "Special case of exponent=1"^ (aNumber * self ln) exp "Otherwise use logarithms"

Object-oriented Programming and Design

20

Original in NumberraisedToInteger: operand

"Answer the receiver raised to the power operand, an Integer."| count result |operand = 0 ifTrue: [^ self class one].operand = 1 ifTrue: [^ self].operand < 0 ifTrue: [^ (self raisedToInteger: operand negated) reciprocal].count := 1.[(count := count + count) < operand] whileTrue.result := self class one.[count > 0]

whileTrue: [result := result * result.(operand bitAnd: count)

= 0 ifFalse: [result := result * self].count := count bitShift: -1].

^ result

Object-oriented Programming and Design

21

Refactoring raisedTo:

raisedTo: power

"Answer the receiver raised to a power."

self = 1 ifTrue: [^ self].

^ power timesMultiply: self

Object-oriented Programming and Design

22

IntegertimesMultiply: aNumber

| count result |

self = 0 ifTrue: [^ aNumber class one].self < 0 ifTrue: [^ (self negated timesMultiply: aNumber) reciprocal].

count := 1.[(count := count + count) < self] whileTrue.

result := 1.[count > 0] whileTrue:

[result := result * result.(self bitAnd: count) = 0 ifFalse: [result := result * aNumber].count := count bitShift: -1].

^ result

Object-oriented Programming and Design

23

NumbertimesMultiply: aNumber

| truncated |

aNumber < 0.0 ifTrue:

[(truncated := self truncated) = self ifTrue:

["self is a whole number."

^ (truncated timesMultiply: aNumber) * self class one].

ArithmeticError signal:

aNumber printString ,

' : negative number raised to a non-integer power'].

^ (self * aNumber ln) exp

Double-dispatching: the Problem

FloatFloat

SmallintegerSmallinteger

FractionFraction

FixedPointFixedPoint

MatrixMatrix

FrFr

ScSc

FiFi

FlFl MM

**

**

**

**

**

FlFl

FlFl

FlFl

FlFlFlFl

FrFr

FrFr

FlFl

FiFi

FiFi

ScSc

ScSc

ScSc ScSc

ScSc

ScScScSc

FiFi

FiFi

Arithmetic

Number * Matrix

Multiply each element by number

Matrix * Number

Multiply each element by number

Matrix * Matrix

Standard matrix multiplication

Arithmetic

Integer * Integer Primitive int operations

Integer * Float Convert int to float

Float * Integer Convert int to float

Float * Float Primitive float operation

Double dispatching: the Solution

Primary method (+, *, etc) sends a second message to argument, encoding the class of the receiver.

Second message knows class of both its argument and its receiver.

Primary operations

Send a second message, encoding the class of the original receiver in the name of the message.

+ anArg

anArg sumFromInteger: self

Double Dispatching Methods

Knows the class of receiver and argument.

sumFromInteger: anInteger

^anInteger asFloat + self

The first message dispatch

37 + 8.9

SmallInteger + anArg

<primitive: 1>

^ anArg sumFromInteger: self

8.9 sumFromInteger: 37

The second message dispatch

8.9 addSmallInteger: 37

Float addSmallIngeter: anArg

^ anArg asFloat + self

8.9 + 37.0

Finishing Up

8.9 + 37.0

Float + anArg

<primitive: 41>

^aNumber sumFromFloat: self

MultiplicationFraction has two instance variables, numerator and

denominator.

* aNumber

"Result is a new Fraction unless the argument is a Float, in which case the result is a Float."

^aNumber productFromFraction: self

Fraction

productFromFraction: aFraction

^(self species

numerator: aFraction numerator * numerator

denominator: aFraction denominator * denominator) reduced

Integer

productFromFraction: aFraction

^(aFraction species

numerator: aFraction numerator * self

denominator: aFraction denominator) reduced

Float

productFromFraction: aFraction

^aFraction asFloat * self

Float

* aNumber

"Answer a Float that is the result of multiplying the receiver by the argument, aNumber. The primitive fails if it cannot coerce the argument to a Float"

<primitive: 49>

^aNumber productFromFloat: self

Fraction

productFromFloat: aFloat

^aFloat * self asFloat

Shared Responsibilities

Sometimes need to select a method based on class of several objects:

Displaying object -- depends on both the kind of object and the windowing system

Arithmetic -- depends on the types of both arguments

Double dispatching

Three kinds of mesages

• primary operations

• double dispatching methods

• forwarding operations

Implement inheriting from superclass of argument

Implement commutativity

Cost of Double Dispatching

Adding a new class requires adding a message to each of the other classes.

Worst case is N*N methods for N classes.

However, total lines of code is not much larger.

Object-oriented Programming and Design

42

openMessageEditString: aString"Create a pluggable version of the views for a Browser that just shows one message."| messageListView browserCodeView topView annotationPane underPane y |

Smalltalk isMorphic ifTrue: [^ self openAsMorphMessageEditing: aString].

topView := (StandardSystemView new) model: self.topView borderWidth: 1.

"label and minSize taken care of by caller"

messageListView := PluggableListView on: selflist: #messageListSingletonselected: #indexIsOne changeSelected: #indexIsOne:menu: #messageListMenu:shifted:.

messageListView window: (0 @ 0 extent: 200 @ 12).topView addSubView: messageListView.

Object-oriented Programming and Design

43

openOnClassWithEditString: aString"Create a pluggable version of all the views for a Browser, including views and controllers."| classListView messageCategoryListView messageListView browserCodeView topView switchView annotationPane underPane y optionalButtonsView |

Smalltalk isMorphic ifTrue: [^ self openAsMorphClassEditing: aString].

topView := (StandardSystemView new) model: self.topView borderWidth: 1.

"label and minSize taken care of by caller"

classListView := PluggableListView on: selflist: #classListSingletonselected: #indexIsOne changeSelected: #indexIsOne:menu: #classListMenu:shifted:keystroke: #classListKey:from:.

classListView window: (0 @ 0 extent: 100 @ 12).topView addSubView: classListView.

Object-oriented Programming and Design

44

Problem

Two user interface frameworks, Morphic and MVC

Application knows its UI frameworks Refactor so app doesn’t know its UI

Object-oriented Programming and Design

45

Solution

Have method delegate to an object that might be either a Morphic expert or a MVC expert

openMessageEditString: aString

^UIManager default

openMessageEditString: aString for: self

Object-oriented Programming and Design

46

MorphicUIManager

openMessageEditString: aString for: aBrowser

^aBrowser openAsMorphMessageEditing: aString

Object-oriented Programming and Design

47

MVCUIManager

openMessageEditString: aString for: aBrowser| topView messageListView … |topView := (StandardSystemView new) model: aBrowser.topView borderWidth: 1.

"label and minSize taken care of by caller"

messageListView := PluggableListView on: aBrowserlist: #messageListSingletonselected: #indexIsOne changeSelected: #indexIsOne:menu: #messageListMenu:shifted:.

messageListView window: (0 @ 0 extent: 200 @ 12).topView addSubView: messageListView.

Object-oriented Programming and Design

48

First attempt

To get rid of isMorphic

Change method to delegate to UIManager default

Copy old method into MVCUIManager and MorphicUIManager

Replace “isMorphic” with “true” or “false”

Simplify

Object-oriented Programming and Design

49

Problem

UIManager subclasses will accumulate application-specific code.

Methods related to application should be in application package.

Object-oriented Programming and Design

50

Uses of Polymorphism

Methods often depend radically on class of receiver.

isNil

ifTrue:ifFalse:

double dispatching

Object-oriented Programming and Design

51

Double dispatch

Sometimes method needs to depend on class of receiver AND class of argument

Solution: First method sends message to argument,

encoding type of original receiver in the message name

Second method knows type of both receiver and argument

Object-oriented Programming and Design

52

Double dispatch in browser

Browser>>displayWith: aUIManager

aUIManager displayBrowser: self

MorphicUIManager>>displayBrowser: aBrowser

Object-oriented Programming and Design

53

Polymorphism

More significant than inheritance.

Requires standard interfaces.

Many different uses:

Template methods

standard interfaces

eliminating conditionals


Recommended