CS305/503, Spring 2009 Advanced OOP, Boundary Cases. Michael Barnathan.

31
CS305/503, Spring 2009 Advanced OOP, Boundary Cases. Michael Barnathan
  • date post

    20-Dec-2015
  • Category

    Documents

  • view

    215
  • download

    1

Transcript of CS305/503, Spring 2009 Advanced OOP, Boundary Cases. Michael Barnathan.

CS305/503, Spring 2009Advanced OOP, Boundary Cases.

Michael Barnathan

Assignment 1 Review

• Distribution: Most grades around 92, 3 100s.

Assignment 1 Review• This will be the easiest assignment given.• Primary Purpose:

– To get comfortable with the Java environment.• Some of you had problems with the IDE. An IDE is overkill and will slow you

down on a project of this size. IDE issues are never an acceptable excuse.– To get you thinking about boundary cases.

• Cases that occur at extreme or unexpected parameter values (100, the empty string, NaN, null, etc.).

• Primary issues:– Programs froze when secret = 100.– Counter was off by one (make sure you count the last guess!)– Repeated code, magic numbers.– Some of you broke encapsulation of the Guesser class.

• No points were taken off for this, but they will be in the future.

Boundary Cases• These have an impact on the stability and

security of your software.– Stability: things may randomly fail (for instance, your

programs would freeze up 1% of the time).– Security: attackers very frequently exploit boundary

cases and will look for those first.• What are they?

– Extreme values: in a range from 1 to 100, 1 and 100 are both boundary cases to test.

– Permitted but unexpected: what if I passed in 0, Integer.MAX_VALUE, or -20?

Case Study: The Zune

• Does anyone have one?• Did anyone use one on December 31?

– Did it freeze?– “30GB Zunes Failing Everywhere, All At Once”

• …What’s so special about December 31, 2008?– It’s the last day of the year, but so was 12/31/07…– Ah, but 2008 was a leap year!

Microsoft’s Official Fix:

Wait until January 1.

The Source:• What makes this really interesting is that the

offending source code is available.• Let’s take a look.

– Line 259 starts the troublesome block.– (Note the use of the ternary operator on line 170)

• See it?– 263: Days will never be greater than 366.– The code inside of that if will never execute!– And the loop will never terminate!

• This will happen again in 2012 if not patched.

Other Issues

• So we just fix it there and we’re set, right?– Not quite.

• Here’s why repeating code is bad:– Look on line 557. There it is again!– Now we need to change that too!– If you repeat code, fixing these issues becomes a

wild goose chase.

• DRY Principle: “Don’t Repeat Yourself”.– If you can write it once, don’t write it twice.

Your Code• A typical solution looked something like this:

public class Guesser {//…My code…

public static void main(String[] args) {int low=1;int high=Guesser.MAXNUM; int counter=0;

Guesser guess = new Guesser();

while (high > low) {

int mid = (low + high) / 2;char ans = guess.guessNumber(mid);

if (ans == '>') {

low = mid;counter++;

} else if (ans == '<') { high = mid;

counter++;}

else {System.out.println(counter);counter++;break;

} }

}}

So if the secret is 100…

Infinite loop in boundary case.

DRY

• Low = 0, High = 100, Mid = 50• Low = 50, High = 100, Mid = 75• Low = 75, High = 100, Mid = 87• Low = 87, High = 100, Mid = 93• Low = 93, High = 100, Mid = 96• Low = 96, High = 100, Mid = 98• Low = 98, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99• Low = 99, High = 100, Mid = 99•… Ad infinitum

Off-by-one Solution: Exclude the guess.

Breaking Encapsulation• By putting your code directly into the Guesser class,

you were allowing it to read the secret number, breaking the encapsulation of the class.

• A better way would be to define a GuessingGame class which uses the Guesser.

• This would also separate the details of how the secret number is being generated from the algorithm used to guess it (separation of concerns). This helps make code modular and maintainable.

• I will take style points off for this in the future.

• Any other questions about the assignment?

Review Questions

• What is the efficiency of the three sorting algorithms we discussed previously?

• What are their primary advantages and disadvantages?• Is there any reason you’d want to use one over

another?• What does it mean for a sort to be “stable”?• What does “CRUD” stand for and why are these

algorithms important?• What is the tradeoff involved with using sorted arrays

vs. unsorted arrays?

Here’s what we’ll be learning:• Object Oriented Programming:

– Inheritance.– Polymorphism.– Interfaces and implementations.– Upcasting and downcasting.– Abstract and final classes.– Use of generics.

• We’ll have a lab on sorting and the Vector class next time.

Inheritance• Classes can “inherit” from each other.• The “derived” (child) class automatically starts out with all

of the “base class”’s (parent’s) properties and methods.• You can then “override” methods by redefining them in the

derived class.• Inheritance is properly used when one class “is a” ‘nother

(“is a” is standard terminology).– For example, a Truck “is a” Vehicle.

• Every class in Java inherits automatically from the Object class. This is the only example of multiple inheritance permissible in Java.– Object defines methods such as equals() and toString().

Inheritance Hierarchy

• Inheritance defines a hierarchy, with base classes above derived classes:

Inheritance in Javapublic class Vehicle { //Base class.

public void go() { System.out.println(“Vroom!”); }public void honk() { System.out.println(“Beep!”); }

}

public class Truck extends Vehicle {//Derived class.public void honk() { System.out.println(“HONK!”); }

}

Truck t = new Truck();t.go(); //Valid. Trucks inherit go().System.out.println(t.honk()); //“HONK!”, not “Beep!”

Polymorphism

• The primary use of inheritance is for “polymorphism” (poly = many, morphos = forms).

• This is the practice of using a reference to the base class with an object of a derived class.

• Any overridden methods called on the reference will exhibit the behavior of the derived class:

Vehicle v = new Truck(); //Valid!v.honk(); //“HONK!”

Polymorphism• The real power of this is apparent when you use it in

an array or other data structure.• That’s because you can automatically exhibit different

behaviors depending on what each object is:

Vehicle[] cars = new Vehicle[2];cars[0] = new Vehicle();cars[1] = new Truck();

for (int i = 0; i < cars.length; i++)cars[i].honk(); //“Beep! HONK!”

Wait!• “Ah, but arrays are homogenous!”, you say.• Yes, and we’re perfectly fine.• They all contain Vehicle references.

– You’re storing references in the array, not objects.• Everything inherits from Object…

– So yeah, you can store Objects in arrays.– And thereby store any combination of classes!

• There’s a catch to this:– You can only call base class methods through a base class reference.– The behavior will be that of the derived class.– But the choice of methods is that of the base class.

• Bummer, but it makes sense:– You have no idea what those Objects really are, so all you can do is call things

common to every Object: equals(), clone(), toString(), etc.– Otherwise you might call a function that doesn’t exist.

Downcasting• “But I do know what I just put into that array!”, you say.• “Yeah, yeah, tell it to the compiler”, I respond.• So you did:

Object[] arr = new Object[2];arr[0] = new Truck();arr[1] = new Hovercraft();

arr[0].honk(); //Error, arr[0] is an Object.((Truck) arr[0]).honk(); //Now it works; we told Java it was really a Truck.

• This is called a downcast, because you are casting down the inheritance hierarchy.– Object is at the top, of course.– Vehicle is right below it (along with every other base class).– Truck is below Vehicle because it inherits from Vehicle.

• Make sure that you downcast into a type that the variable is compatible with (either the derived type or a base class above it), or Java will throw an exception. You can’t cast that Hovercraft to a Truck.

Upcasting

• And you can go the other way too.• This is usually done implicitly by Java.

– e.g. Object o = new Truck();• But you can also do it explicitly if you wish:

– Object o = (Object) new Truck();• You can’t access overridden base class

methods this way, though:– ((Vehicle) new Truck()).honk(); //”HONK!”– The object is still a truck, so you’re still getting a

truck honk. (Polymorphism at work!)

“Super”• There is only one way to access overridden base

class methods in Java: “super”.• “super” refers to the class you inherit from.• Inside of the Truck class, super() refers to

vehicle…• And if you were to call super.honk() somewhere

inside of Truck, it would “Beep!”• This is commonly used to call a base class’

constructor in the derived class’ constructor.– If you do this, make sure it’s the first statement.

Interfaces and Implementations• Let’s say you’re more interested in what a class can do

rather than what it is.• Inheritance is not appropriate here.• What you need is an interface.

– In Java, an interface is a “contract”.– It defines some methods.– In exchange for implementing those methods, Java will let

you call your object using a reference to the interface type.

– That’s because it knows that the methods in the interface are implemented and it can safely call them.

• It made you write them. But now it knows they exist for sure.– This has the same benefits as polymorphism.

Defining an Interface

• Interfaces in Java are defined like this:

interface Honkable {public void honk();//Other functions can go here too.//But you just define them; you don’t//implement them here.

}

Implementing an Interface• Vehicle could then implement Honkable:

public class Vehicle implements Honkable {//honk() must be implemented now to compile.public void honk() { … }

}

• Unlike inheritance, you can implement more than one interface in Java. Just separate the interface names with commas.– You’ll need to write the methods for each, of course.

The Reward

• You can now do this:

Honkable[] h = new Honkable[2];h[0] = new Vehicle();h[1] = new FrenchHorn();

for (int i = 0; i < h.length; i++)h[i].honk(); //All things Honkable have this.

Abstract Classes• A class is abstract if it defers implementing one or more methods to

a derived class.• The unimplemented methods must be preceded by the keyword

abstract.• You can also precede the class definition itself with the abstract

keyword.• Either way, the end result is that people cannot create objects of

your abstract class. They must create objects of non-abstract derived classes.– This doesn’t mean they can’t call the abstract method, however.

Through polymorphism, they can (the method in the derived class, which is no longer abstract, will execute)

– Also, not every method in the class needs to be abstract. Some could have base class implementations, which are carried down to the derived classes.

Abstract Classes• So why are they useful, then?• Because you can define common behavior to a category of things even if there’s no common

implementation of that behavior.• Example:

public abstract class Shape {//There’s no area of a “shape”, but it makes sense to talk about shapes having areas.public abstract double area();

}

public class Square extends Shape {private double sidelen;public double area() { return sidelen * sidelen; } //Squares have an area.

}

public class Triangle extends Shape {private double base;private double height;public double area() { return .(base * height) / 2; } //Triangles have an area.

}

Abstract Classes: Why not use an interface instead?

• Why not use a hasArea interface in the previous example?

• Because then your Squares, Circles, and Triangles wouldn’t be Shapes!– There might be useful properties or non-abstract methods

carried over from the Shape class that you want to use.– Having one abstract method doesn’t mean the others all

have to be abstract. The non-abstract methods will be inherited by the derived classes.

• You can’t do this with interfaces.– More philosophically, it fails to model the “is a shape”

relationship between classes.

Final Classes

• Inheritance is great, but sometimes you don’t want people deriving from your classes.

• No problem; just declare the class final.

public final class Planet { … }

//Error, Planet is final - sorry, Pluto.public class Planetoid extends Planet { … }

Using Generics

• Generic classes are sort of complex to define in Java, though not beyond your current knowledge.

• We’ll get to them some other time.• But using them is simple:

Vector<String> vstr = new Vector<String>();vstr.add(new String(“Foobar”));//Works.vstr.add(new Truck()); //Nope.

Abstractions

• The lesson:– Abstraction is the root of intuitive understanding

of complex systems. Your ability to use the Internet without understanding everything down to how the glass in the fiber optic cables was made is a testament to this. Abstraction is the only way to build a complex system of any sort.

• Next class: Sorting Lab.