Abstract Data Types II
Download
Report
Transcript Abstract Data Types II
Abstract Data Types II
Sufficient operations
A set of operations on an ADT is sufficient if, together,
they meet all the requirements
They must be able to create all the values and perform all the
operations required by the application
Remember that the application cannot directly access the internal
values
They should be able to create all the values and perform all
the operations required by any application in a given class of
applications
2
Necessary operations
An operation on an ADT is necessary if omitting it
would fail to meet the requirements
If the application can implement an operation easily and
efficiently by combining other operations, that operation is
unnecessary
It’s OK to have unnecessary operations if they add
significantly to the convenience of using the ADT
3
Necessary and sufficient operations
Notice that “sufficient” applies to a set of operations,
while “necessary” applies to individual operations in
that set
“Necessary” is relative to the set; an operation that is
necessary in one set may not be necessary in another
A necessary and sufficient set of operations is a set
which is sufficient (meets all the requirements), but
would not be sufficient if any one operation were
omitted
4
Convenience operations
An operation is a convenience operation if it could be
accomplished by some overly complex combination of
other operations
Convenience operations should be justified
Will it be used often?
Does it really simplify the user’s task?
Would the user expect this operation to be provided?
Is it significantly more efficient?
5
Necessary and sufficient operations
A class should define a necessary and sufficient
set of operations
Convenience operations should be justified
Similarly, a class should have a necessary and
sufficient data representation
In general, a class should not contain data that can be
easily computed from other data in the class
6
Example: Strings
Necessary and sufficient operators:
A constructor: public String(char[] chs)
Ways to access data:
public int length()
public charAt(int index)
Would you be happy with just these?
If you invented the String class, could you justify
operations such as equals and string concatenation?
Convenience operators aren’t all bad!
7
Types of operations
A constructor creates a legal value of the ADT (typically
from input values)
An accessor uses a value of the ADT to compute a value
of some other type
A transformer uses a value of the ADT to compute
another value of the same ADT
A mutative transformer changes the value of the ADT it is
given
An applicative transformer takes one value of an ADT and,
without changing it, returns a new value of the same ADT
8
Requirements
The constructors and transformers must together be
able to create all legal values of the ADT
A constructor or transformer should never create an illegal
value
It’s nice if the constructors alone can create all legal values,
but sometimes this results in constructors with too many
parameters for reasonable convenience
The accessors must be able to extract any data needed
by the application
9
Operations in Java
Constructors can be implemented with Java
constructors
A constructor’s job is to construct an object of a class in
a valid state
That should be a constructor’s only job
Accessors and transformers can be implemented
with Java methods
Mutative transformers are typically (but not always)
implemented as void methods
Sometimes they both modify an object and return it
10
Factory methods
The problem with a constructor is that it will always construct an
object of a given type
This isn’t always what you want
The constructor might be called with illegal values of the parameters
You may wish to create unique instances of objects
A common solution to these problems is to embed the call or calls
to a private constructor inside a static “factory” method
Example:
public static Animal create(String voice) {
if (voice.equals("woof")) return new Dog();
if (voice.equals("meow")) return new Cat();
if (voice.equals("moo")) return new Cow();
throw new IllegalArgumentException(voice);
}
11
Factory methods II
With a normal constructor,
public class Add {
public Add( ) { }
}
each call to new Add() creates another Add object
These objects are not equal unless you define your own equals(Object o)
method
But with a factory method,
public class Add {
static Add add = new Add(); // done once when class is loaded
private Add( ) { } // hide this constructor from the world!
public static Add getAdd( ) {
return add;
}
}
You can only ever have one Add object
Hence, the default equals method (same as ==) always works
12
Example: String
Constructors:
Accessors:
public int length()
public char charAt()
Transformers (applicative only):
"This is syntactic sugar for a constructor"
public String(char[] chs)
public String substring(int i, int j)
public String concat(String that) (also +)
Etc.
13
Immutable objects
A String is immutable: it cannot be changed
The String class has no mutative transformers
Advantages:
Operations such as string concatenation create new Strings
Efficient (for most uses)
Easy to use and simple to understand (changing a String in
one object doesn’t change it in other objects)
Disadvantage:
Every call to a transformer creates a new String
14
Example: StringBuffer
Constructors:
Accessors:
none
Transformers (mutative):
public int length()
public char charAt()
Transformers (applicative):
public StringBuffer(String s)
public StringBuffer append(Object obj)
Etc.
15
Mutable objects
A StringBuffer is mutable: it can be changed
The StringBuffer class has both applicative and
mutative transformers
Advantage:
Disadvantage:
Efficient (for doing a lot of string manipulation)
Can be confusing (example coming up shortly)
Operations on Strings are done by converting to
StringBuffers, doing the work, and converting
back
16
Safe use of Strings
public class Person {
private String name;
Person(String name) {
this.name = name;
}
}
String jan = "Jan";
Person doctor = new Person(jan);
String dan = "D" + jan.substring(1, 2);
Person secretary = new Person(dan);
17
Unsafe use of StringBuffers
public class Person {
private StringBuffer name;
Person(StringBuffer name) {
this.name = name;
}
}
StringBuffer buffer = new StringBuffer("Jan");
Person doctor = new Person(buffer);
buffer.setCharAt(0, 'D');
Person secretary = new Person(buffer);
18
Summary
A class should define a necessary and sufficient set of
operations
Convenience operations should be justified
Operations can be classified as:
Constructors
Accessors
Transformers (applicative or mutative)
Immutable objects are often preferable to mutable
objects
19
The End
20