Relevant Reading Material
UML sequence and collaboration diagrams
We will use UML sequence and UML collaboration diagrams to describe interactions between objects in our designs.
UML Sequence Diagrams
UML Sequence Diagrams are used to model the flow of login within your system. Sequence Diagrams are typically used to model:
-
Usage scenarios. A sequence of operations that a client/code use your system.
-
The logic of methods. Designate the steps taken during the excution of a method including the objects called, arguments passed etc.
-
The logic of a component/service. Similar to methods but the entities used can be a component, a web service etc.
Consider the code for AList
in the scenario were we have a 3 element list and we call size()
.
UML Collaboration Diagrams
UML Collaboration diagrams are used to model the dynamic behaviour of your software (much like sequence diagrams). Collaboration fiagrams show the message flow between objects and also associations between classes.
Collaboration Diagrams are typically used to
-
Provide a birds-eye view of collaborating objects
-
Model the logic of the implementation of a complex operation
-
Show the roles of objects within your system
Lists, again
ADT descripes the operations of a List, it does not specify anything about the internal data structure to use in order to implement these operations.
Our goal is to provide to clients of our code the ADT without exposing our internal data structure or implementation details. By doing so, implemenation as well as data structure changes that satisfy the LSP do not affect our client code.
Our current implementation of List
however does expose to our clients implementation details.
-
To create a new empty list client code needs to use the
new Empty()
-
To create a new non-empty list client code needs to use
new Cons()
-
Clients could cast an object to
Cons
and then get access tofirst
andrest
through your getter methods and even worse setter methods (more on setters later) -
Client code relies on the
List
class. Changing our design (for example to use other data structures provided by the Java Language or a different design) will affect our client code.
Interfaces
A Java interface allows
-
the definition of constants
-
the definition of method signatures
-
the defintion of default implementation(s) for method(s)
All elements of an interface are public
.
Interfaces can inherit from other interfaces, just like classes, but they can extend 1 or more interfaces unlike classes; a property called mutliple inheritance.
public interface List {
// constant declarations ... see later today
// method signatures
Integer size();
Integer last();
List addToEnd(Integer element);
Integer elementAt(Integer index);
List add()
// default implementations
}
Some points on interfaces:
-
There is not constructor.
-
There are no instance fields.
-
All elements of an interface are
public
Using an interface
Classes can then implement zero, one or more interfaces by using the keyword implements
.
When a class implements an interface it has to provide implementations for each of the methods signatures in the interface that it implements. The class can also override default implementations inherited from an interface.
public class AList implements List {
abstract public Integer size();
abstract public Integer last();
abstract public List addToEnd(Integer element);
abstract public Integer elementAt(Integer index);
public List add(Integer ele) {
return new Cons(ele, this);
}
// default implementations
}
Interfaces and static types
A class that directly or indirectly implements an interface, I
can statically have the type I
.
By using the static type I
we can further impose restrictions to client code that now can only call
on methods whose signature is defined in I
or any of its super-interfaces.
Coding to an interface
The design rule to remember here is that it is better to code to an interface. The word interface in the preceding sentence refers to the operations that define a system and not to Java’s interface. A Java interface is the language feature that aspires to help developers follow this design rule.
Another possible way of stating the rule to avoid confusion is, code against the ADT.
-
An interface has a more direct mapping to an ADT.
-
no information on data structure
-
no fields or data inherited in your code when we extend an interface
-
all implementing classes have to provide an implementation for each method
-
-
Clients are only aware of operations available because of the interface.
-
Implementations of the interface can change without altering your clients.
A better way to create instances
Java provides static
fields and methods. The keyword static
designates methods and fields that are part of the Class or Interface and not instances of the class.
public interface List {
public static List createEmptyList() {
return new Empty();
}
}
The method createEmptyList
is a static
method on the interface List
.
-
It is not part of an object.
-
It is not inherited to other sub-interfaces.
Static methods can refer to other static methods or static fields. They cannot use
-
this
-
super
-
instance methods
-
instance fields
You have seen static methods and fields, recall
Math.PI // static field in class Math
System.out // static field in class System. out is field of type PrintStream
Math.sqrt(10.0); // call the static method sqrt on class Math
Testing
There are various kinds of testing. In this class we will focus on two of those
-
Blackbox testing
-
Glassbox (whitebox) testing
Blackbox testing
Blackbox testing refers to tests that exercise the public interface of a module. For an ADT, this means all the tests that exercise the operations and the specification of each operation found in the ADT to ensure that the code does in fact implement correctly the ADT.
Changes to the internal implementation of the ADT should not break any of the blackbox tests.
Glassbox testing
Glassbox testing refers to the tests that exercise the internal implementation of a module, e.g., non-public methods, data structure state during method calls (before and after) internal invariants, etc.
Changes to the internal implementation of an ADT might and typically do break glassbox testing.