Abstract Data Types II

Download Report

Transcript Abstract Data Types II

Abstract Data Types II
Sufficient operations
• Operations on an ADT are sufficient if 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
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
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?
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
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!
Types of operations (p. 113)
• A constructor is creates a value of the ADT 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
computer another value of the ADT
– A mutative transformer changes the value of the ADT it
is given
– An applicative transformer takes one ADT and, without
changing it, returns a new ADT
Requirements
• The constructors and transformers must
together be able to create all legal values of
the ADT
• The accessors must be able to extract any
data needed by the application
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
Example: String
• Constructors:
– "This is syntactic sugar for a constructor"
– public String(char[] chs)
• Accessors:
– public int length()
– public char charAt()
• Transformers (applicative only):
– public String substring(int i, int j)
– public String concat(String that) (also +)
• Etc.
Immutable objects
• A String is immutable: it cannot be changed
• The String class has no mutative transformers
– Operations such as string concatenation create new
Strings
• Advantages:
– 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
Example: StringBuffer
• Constructors:
– public StringBuffer(String s)
• Accessors:
– public int length()
– public char charAt()
• Transformers (applicative):
– public String substring(int i, int j)
• Transformers (mutative):
– public StringBuffer append(Object obj)
• Etc.
Mutable objects
• A StringBuffer is mutable: it can be changed
• The StringBuffer class has both applicative and
mutative transformers
• Advantage:
– Efficient (for doing a lot of string manipulation)
• Disadvantage:
– Can be confusing (example coming up shortly)
• Operations on Strings are done by converting to
StringBuffers, doing the work, and converting back
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);
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);
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
The End