Object-Oriented Design Lecture 20 CS 3500 Spring 2011 (Pucella) Tuesday, Mar 29, 2011

Similar documents
Object-Oriented Design Lecture 16 CS 3500 Fall 2010 (Pucella) Friday, Nov 12, 2010

A Model of Mutation in Java

7 Code Reuse: Subtyping

4 Object-Oriented ADT Implementations

17 From Delegation to Inheritance

13 Subtyping Multiple Types

20 Subclassing and Mutation

21 Subtyping and Parameterized Types

3 ADT Implementation in Java

8 Understanding Subtyping

PREPARING FOR THE FINAL EXAM

12 Implementing Subtyping

16 Multiple Inheritance and Extending ADTs

Lecture 21: The Many Hats of Scala: OOP 10:00 AM, Mar 14, 2018

Object-Oriented Design Lecture 3 CSU 370 Fall 2007 (Pucella) Friday, Sep 14, 2007

Object-Oriented Design Lecture 23 CS 3500 Fall 2009 (Pucella) Tuesday, Dec 8, 2009

Object-Oriented Design Lecture 11 CS 3500 Spring 2010 (Pucella) Tuesday, Feb 16, 2010

CS 1110 Prelim 2 November 6th, 2012

Subclassing for ADTs Implementation

Lecture 23: Priority Queues, Part 2 10:00 AM, Mar 19, 2018

CS 2340 Objects and Design - Scala

Logic and Computation Lecture 20 CSU 290 Spring 2009 (Pucella) Thursday, Mar 12, 2009

Data abstractions: ADTs Invariants, Abstraction function. Lecture 4: OOP, autumn 2003

INHERITANCE. Spring 2019

An Introduction to C++

Functional Objects. Christopher Simpkins CS 3693, Fall Chris Simpkins (Georgia Tech) CS 3693 Scala / 1

Data Structures (list, dictionary, tuples, sets, strings)

PREPARING FOR PRELIM 2

The fringe of a binary tree are the values in left-to-right order. For example, the fringe of the following tree:

Classes Classes 2 / 36

Overview of OOP. Dr. Zhang COSC 1436 Summer, /18/2017

CS Prelim 2 Review Fall 2018

17 Multiple Inheritance and ADT Extensions

CS162 Week 1. Kyle Dewey. Friday, January 10, 14

public Candy() { ingredients = new ArrayList<String>(); ingredients.add("sugar");

Classes Classes 2 / 35

CS 1110 Final, December 8th, Question Points Score Total: 100

Code Reuse: Inheritance

Homework notes. Homework 2 grades posted on canvas. Homework 3 due tomorrow. Homework 4 posted on canvas. Due Tuesday, Oct. 3

PREPARING FOR THE FINAL EXAM

Equality for Abstract Data Types

1.2 Adding Integers. Contents: Numbers on the Number Lines Adding Signed Numbers on the Number Line

PREPARING FOR PRELIM 1

CMSC131. Creating a Datatype Class Continued Exploration of Memory Model. Reminders

Lecture 5: Implementing Lists, Version 1

CS205: Scalable Software Systems

ITI Introduction to Computing II

Lab 8: Introduction to Scala 12:00 PM, Mar 14, 2018

CS 1110 Final, December 8th, Question Points Score Total: 100

Lecture 24: Implementing Heaps 10:00 AM, Mar 21, 2018

For this chapter, switch languages in DrRacket to Advanced Student Language.

CS 1110 Prelim 2 November 10th, 2016

Object-Oriented Design Lecture 21 CSU 370 Fall 2008 (Pucella) Tuesday, Dec 9, 2007

Programming and Data Structure

Lecture 10 Notes Linked Lists

Lesson 13: The Graph of a Linear Equation in Two Variables

Recall. Key terms. Review. Encapsulation (by getters, setters, properties) OOP Features. CSC148 Intro. to Computer Science

8 Hiding Implementation Details

Immutables. At first glance, you d think that immutables were useless, however, they provide many advantages.

15 Multiple Inheritance and Traits

6.170 Lecture 7 Abstract Data Types MIT EECS

Lecture 19. Using Classes Effectively

ITI Introduction to Computing II

Inheritance. Lecture 11 COP 3252 Summer May 25, 2017

Carleton University COMP1406/1006, Summer 2013 Tutorial 2

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Chapter01.fm Page 1 Monday, August 23, :52 PM. Part I of Change. The Mechanics. of Change

Chapter 13: Reference. Why reference Typing Evaluation Store Typings Safety Notes

Lecture 10 Linked Lists

Working with classes and objects COSC346

6.001 Notes: Section 8.1

CS-202 Introduction to Object Oriented Programming

Favoring Isolated Mutability The Actor Model of Concurrency. CSCI 5828: Foundations of Software Engineering Lecture 24 04/11/2012

Lab 9: More Sorting Algorithms 12:00 PM, Mar 21, 2018

Before We Begin. Class Implementation II. Introduction to Computer Science II. Overview (1): Administrative Details (1):

Overview. Elements of Programming Languages. Objects. Self-Reference

Object Oriented Features. Inheritance. Inheritance. CS257 Computer Science I Kevin Sahr, PhD. Lecture 10: Inheritance

(Refer Slide Time 04:53)

Creating a new data type

Announcements for this Lecture

Lecture 23: Priority Queues 10:00 AM, Mar 19, 2018

Project 3: Implementing a List Map

(Refer Slide Time: 4:00)

The Dynamic Typing Interlude

Tiny Compiler: Back End


(Refer Slide Time: 05:25)

CS Prelim 2 Review Fall 2014

Java Fundamentals (II)

Cloning Enums. Cloning and Enums BIU OOP

Al al-bayt University Prince Hussein Bin Abdullah College for Information Technology Computer Science Department

CS 1302 Chapter 9 (Review) Object & Classes

Principles of Programming Languages

Advanced concurrent programming in Java Shared objects

MITOCW watch?v=flgjisf3l78

spindle Documentation

Lecture 10 Notes Linked Lists

Introducing Wybe a language for everyone

CS112 Lecture: Working with Numbers

Lecture Notes on Queues

Transcription:

Object-Oriented Design Lecture 20 CS 3500 Spring 2011 (Pucella) Tuesday, Mar 29, 2011 20 More on Mutation Deciding whether to use mutable structures is all about trade-off. On the plus side, mutable structures let you implement some things much more simply than a corresponding implementation with immutable structures the moveable anchors from last time being a perfect example of something that is straightforward to implement using mutation but somewhat more painful to implement with immutable structures. On the minus side, mutable structures force you to think about sharing, and require you to keep in mind explicit diagrams like we saw last time, in order to predict what happens when you actually end up mutating a field of a mutable structure. Let s suppose that you have understood the trade-offs, and that you accept the added complexity of mutable structures. In other words, you decided to have some classes with mutable state, which generally means having mutable fields. Even if you are okay with having mutable fields, I strongly suggest you make your fields private, and provide getters and setters for each field that can mutate that is, methods to get the value of those fields, and methods to set the value of those fields. Why? Because this lets us enforce invariants. Suppose we only want to work with anchors in the positive quadrant, that is, points whose coordinates are nonnegative. Consider the following implementation of anchors (which for simplicity does not define creators or an abstract class): class Anchor (p:point) { // var indicates that the field can be reassigned a new value var pos = p def position ():Point = pos def move (dx:double, dy:double):unit = pos = pos.move(dx,dy) // move point and reassign // pos to be that point override def tostring ():String = "anchor(" + pos + ")" Enforcing that only positive points can be created is easy by changing the constructor of the class, as well as the move method: class Anchor (p:point) { 254

if (p.xcoord() < 0 p.ycoord() < 0) throw new IllegalArgumentException("Point not positive") // var indicates that the field can be reassigned a new value var pos = p def position ():Point = pos def move (dx:double, dy:double):unit = { newpos = pos.move(dx,dy) // move point and reassign // pos to be that point if (newpos.xcoord() < 0 newpos.ycoord() < 0) throw new RuntimeException("Point moved to negative position") pos = newpos override def tostring ():String = "anchor(" + pos + ")" (Note that the code to check the parameters is free floating in the class any code that does not belong to a method in a class is executed when an instance of a class is created. If we had used a creator instead, that check would probably have gone into the creator.) If we allow unrestricted field access, however, then anyone can just change the position and break the invariant. Which, in this case, can lead to points having negative coordinates, invalidating the invariant we want to preserve. If you really want to give access to the fields to users (as opposed to forcing them to use an operation like move), it is better to define setters, where we can check that the invariant is maintained whenever state is changed.: class Anchor (p:point) { if (p.xcoord() < 0 p.ycoord() < 0) throw new IllegalArgumentException("Point not positive") // var indicates that the field can be reassigned a new value var pos = p def position ():Point = pos def setposition (newpos:point):unit = if (newpos.xcoord() < 0 newpos.ycoord() < 0) 255

throw new RuntimeException("Point moved to negative position") pos = newpos def move (dx:double, dy:double):unit = setposition(pos.move(dx,dy)) override def tostring ():String = "anchor(" + pos + ")" Therefore, I will expect you all to keep fields private and use explicit setters, instead of making fields public when you want a class to be mutable. (Exercise: I did not bother making my field pos private in the implementation of Anchor in last lecture. Still, I claim one cannot change the value of the field pos there from outside anyways. Why?) 20.1 Shallow and Deep Copies As we saw last time, if we pass an instance to a method, we are really passing the address of the instance to the method. And if the method just takes those values and store them somewhere, then we get sharing, which may or may not be what we want. Recall the code we had last time, slightly modified to make Line an explicitly mutable structure. (We saw that Line is already mutable by virtue of Anchor being mutable, we just make Line explicitly mutable by making the start and end points mutable fields, and adding setters.) object Anchor { def create (p:point):anchor = new AnchorRepr(p) private class AnchorRepr (p:point) extends Anchor { private var pos = p def position ():Point = pos def move (dx:double, dy:double):unit = pos = pos.move(dx,dy) // move point and reassign // pos to be that point override def tostring ():String = "anchor(" + pos + ")" 256

abstract class Anchor { def position ():Point def move (dx:double, dy:double):unit object Line { def create (s:anchor, e:anchor):line = new LineRepr(s,e) private class LineRepr (inits:anchor, inite:anchor) extends Line { private var s:anchor = inits private var e:anchor = inite def start ():Anchor = s def end ():Anchor = e def setstart (a:anchor):unit = { s = a def setend (a:anchor):unit = { e = a override def tostring ():String = s + " <-> " + e abstract class Line { def start ():Anchor def end ():Anchor def setstart (a:anchor):unit def setend (a:anchor):unit An example of sharing was the Line.create(a3,a3) example from last time: val a3 = Point.create(Point.cartesian(0,10)) val l2 = Line.create(a3,a3) 257

+-------------+ <-----+ a3 *----------> Anchor <--+ ------------- +------------+ pos = *---------------> Point +-------------+ ------------ xpos = 0 ypos = 10 +-------------+ +------------+ l2 *----------> Line ------------- s = *------------+ e = *---------------+ +-------------+ The two fields s and e of the newly created Line instance end up pointing to the same instance of Anchor, so that modifying that Anchor instance is reflected in both the start and end position of the line. To make our examples more interesting, consider an additional class to represent triangles using a base line and another anchor:: object Triangle { def create (b:line, a:anchor):triangle = new TriangleRepr(b,a) private class TriangleRepr (b:line, a:anchor) extends Triangle { private var base:line = b private var tip:anchor = a def side1 ():Line = base def side2 ():Line = Line.create(base.start(),tip) def side3 ():Line = Line.create(base.end(),tip) def setbase (l:line):unit = { base=l def settip (a:anchor):unit = { tip=a override def tostring ():String = side1() + " " + side2() + " " + side3() 258

abstract class Triangle { def side1 ():Line def side2 ():Line def side3 ():Line def setbase (l:line):unit def settip (a:anchor):unit Now we can get even more interesting sharing going between lines and points. Consider the following definitions: scala> val a = Anchor.create(Point.cartesian(0,0)) a: Anchor = [0.0,0.0] scala> val b = Anchor.create(Point.cartesian(100,0)) b: Anchor = [100.0,0.0] scala> val c = Anchor.create(Point.cartesian(50,50)) c: Anchor = [50.0,50.0] scala> val l = Line.create(a,b) l: Line = [0.0,0.0] <-> [100.0,0.0] scala> val t = Triangle.create(l,c) t: Triangle = [0.0,0.0] <-> [100.0,0.0] [0.0,0.0] <-> [50.0,50.0] [100.0,0.0] <-> [50. Suppose we wanted to create another triangle that looks just like t. An easy way to do that is to simply define: scala> val u1 = t u1: Triangle = [0.0,0.0] <-> [100.0,0.0] [0.0,0.0] <-> [50.0,50.0] [100.0,0.0] <-> [50.0,50.0] But of course, these is maximal sharing between t and u1: 259

+-------------+ c *----------> Anchor <-------+ ------------- +------------+ pos = *-------------------> Point +-------------+ +------------+ xpos = 50 ypos = 50 +-------------+ +------------+ l *----------> Line <-----+ ------------- s = *-------------+ e = *-----------+ +-------------+ +-------------+ b *----------> Anchor <-+ ------------- +------------+ pos = *-------------------> Point +-------------+ +------------+ xpos = 100 ypos = 0 +-------------+ +------------+ a *----------> Anchor <---+ ------------- +------------+ pos = *-------------------> Point +-------------+ +------------+ xpos = 0 +-------------+ ypos = 0 t *----------> Triangle +------------+ +----> ------------- base = *------------+ tip = *---------------+ +-------------+ u1 *-----+ In particular, changing any part of one changes the other: scala> t.settip(anchor.create(point.cartesian(9,9))) scala> u1 260

res2: Triangle = [0.0,0.0] <-> [100.0,0.0] [0.0,0.0] <-> [9.0,9.0] [100.0,0.0] <-> [9.0,9.0] What if we wanted the new triangle to look just like t but not share as much with t? Let s create a new method in Triangle that creates a copy of the current triangle that does not share its fields. object Triangle { def create (b:line, a:anchor):triangle = new TriangleRepr(b,a) private class TriangleRepr (b:line, a:anchor) extends Triangle { private var base:line = b private var tip:anchor = a def side1 ():Line = base def side2 ():Line = Line.create(base.start(),tip) def side3 ():Line = Line.create(base.end(),tip) def setbase (l:line):unit = { base=l def settip (a:anchor):unit = { tip=a def copy ():Triangle = new TriangleRepr(base,tip) override def tostring ():String = side1() + " " + side2() + " " + side3() abstract class Triangle { def side1 ():Line def side2 ():Line def side3 ():Line def setbase (l:line):unit def settip (a:anchor):unit def copy ():Triangle 261

Now, if we redo the above example with a,b,c,l,t, and u1 as above and say: scala> val u2 = t.copy() u2: Triangle = [0.0,0.0] <-> [100.0,0.0] [0.0,0.0] <-> [9.0,9.0] [100.0,0.0] <-> [9.0, we end up with less sharing between t and u2: +-------------+ c *----------> Anchor <-------+ ------------- +------------+ pos = *-------------------> Point +-------------+ +------------+ xpos = 50 ypos = 50 +-------------+ +------------+ l *----------> Line <-----+ ------------- s = *-------------+ e = *-----------+ +-------------+ +-------------+ b *----------> Anchor <-+ ------------- +------------+ pos = *-------------------> Point +-------------+ +------------+ xpos = 100 ypos = 0 +-------------+ +------------+ a *----------> Anchor <---+ ------------- +------------+ pos = *-------------------> Point +-------------+ +------------+ xpos = 0 +-------------+ ypos = 0 t *----------> Triangle +------------+ +----> ------------- base = *------------+ tip = *---------------+ +-------------+ u1 *-----+ 262

+-------------+ u2 *----------> Triangle ------------- base = *------------+ tip = *---------------+ +-------------+ But there is still some sharing. In fact, if we change either of l, a, b, or c, we see that change both in t and in u2: scala> t.side1().end().move(99,99) scala> u2 res2: Triangle = [0.0,0.0] <-> [199.0,99.0] [0.0,0.0] <-> [9.0,9.0] [199.0,99.0] <-> [9.0,9.0] So copy() may not be quite what we want. It does create a fresh copy of t, but because base and tip are simply given the address of the instance pointed to by base and by tip in t, the value of the fields end up the same in both t and u2. So while u2 is a copy of t, they are not fully disjoint. Rather, u2 is what we call a shallow copy of t. The top level of the instances are disjoint (in the sense that their fields live in different places), but any sharing within the values held in the fields is preserved. If we wanted a truly disjoint new triangle, then we need to make what is called a deep copy, that is, a copy that recursively deep copies (creating new instances) for every instance held in every variable, all the way down. Thus: object Triangle { def create (b:line, a:anchor):triangle = new TriangleRepr(b,a) private class TriangleRepr (b:line, a:anchor) extends Triangle { private var base:line = b private var tip:anchor = a def side1 ():Line = base def side2 ():Line = Line.create(base.start(),tip) def side3 ():Line = Line.create(base.end(),tip) def setbase (l:line):unit = { base=l 263

def settip (a:anchor):unit = { tip=a def copy ():Triangle = new TriangleRepr(base,tip) def deepcopy ():Triangle = new TriangleRepr(base.deepCopy(),tip.deepCopy()) override def tostring ():String = side1() + " " + side2() + " " + side3() abstract class Triangle { def side1 ():Line def side2 ():Line def side3 ():Line def setbase (l:line):unit def settip (a:anchor):unit def copy ():Triangle def deepcopy ():Triangle So, you see, to create a deep copy of a triangle, we recursively deep copy all the values of all the relevant fields, and create a new triangle with those new values. This means that we need a deepcopy() method in Anchor and in Line let s do that, and add some shallow copy() methods as well, for completeness: object Anchor { def create (p:point):anchor = new AnchorRepr(p) private class AnchorRepr (p:point) extends Anchor { private var pos = p def position ():Point = pos def move (dx:double, dy:double):unit = pos = pos.move(dx,dy) // move point and reassign // pos to be that point 264

def copy ():Anchor = new AnchorRepr(p) def deepcopy ():Anchor = new AnchorRepr(p) override def tostring ():String = "[" + pos.xcoord() + "," + pos.ycoord() + "]" abstract class Anchor { def position ():Point def move (dx:double, dy:double):unit def copy ():Anchor def deepcopy ():Anchor object Line { def create (s:anchor, e:anchor):line = new LineRepr(s,e) private class LineRepr (inits:anchor, inite:anchor) extends Line { private var s:anchor = inits private var e:anchor = inite def start ():Anchor = s def end ():Anchor = e def setstart (a:anchor):unit = { s = a def setend (a:anchor):unit = { e = a def copy ():Line = new LineRepr(s,e) def deepcopy ():Line = new LineRepr(s.deepCopy(),e.deepCopy()) 265

override def tostring ():String = s + " <-> " + e abstract class Line { def start ():Anchor def end ():Anchor def setstart (a:anchor):unit def setend (a:anchor):unit def copy ():Line def deepcopy ():Line Note that copy() and deepcopy() for Anchor are the same. That s because Points are immutable copying a Point is the same as creating a new Point. So for some classes, a deep copy is going to look the same as a shallow copy. For the sake of clarity, let s keep the two methods distinct, just to make clear that they have different roles, even though they do the same thing. Now, if we continue with our example:redefine a,b,c,l,t as before and say: scala> t res2: Triangle = [0.0,0.0] <-> [199.0,99.0] [0.0,0.0] <-> [9.0,9.0] [199.0,99.0] <-> [9.0,9.0] scala> val u3 = t.deepcopy() u3: Triangle = [0.0,0.0] <-> [199.0,99.0] [0.0,0.0] <-> [9.0,9.0] [199.0,99.0] <-> [9.0,9.0] You get that t and u3 are truly disjoint: each of their start and end fields point to different instances. Thus: scala> t res4: Triangle = [0.0,0.0] <-> [1199.0,1099.0] [0.0,0.0] <-> [9.0,9.0] [1199.0,1099.0] <-> [9.0,9.0] scala> u2 res5: Triangle = [0.0,0.0] <-> [1199.0,1099.0] [0.0,0.0] <-> [9.0,9.0] [1199.0,1099.0] <-> [9.0,9.0] scala> u3 266

res6: Triangle = [0.0,0.0] <-> [199.0,99.0] [0.0,0.0] <-> [9.0,9.0] [199.0,99.0] <-> [9.0,9.0] I will let you draw the resulting diagram. 267