Professional Documents
Culture Documents
Matthew Huntbach
matthew.huntbach@qmul.ac.uk
Using these slides for FOOP
• This is a set of slides that was originally used for the module
EBU5304 Software Engineering
• The module is part of Queen Mary’s Joint Programme with the
Beijing University of Posts and Telecommunications
• I assist in the teaching of this module, in particular covering two
sections of the Software Engineering module that are taught in
its second half and are about aspects of software engineering
that directly involve writing code using Object Oriented
Programming techniques
• So I have decided to make use of these slides for the ECS658U
Further Object Oriented Programming module at Queen Mary,
making only minor modifications to them
• Some of what is in these slides was also in the previous slides,
but it is useful to see these important aspects covered again,
using different examples to illustrate them
Introduction and overview
What is Software Engineering?
before
var2=var1;
after
var2=var1;
val
val
val
1 * * 1
1 1 1 1
DatabaseStorer1 PayrollCalculator1
• This is not ideal, as the PayRoll, Database and Display objects need
to “know” about Employee objects, which are passed to them through
methods calculatePay, saveToDatabase, and reportHours using
the argument this
• A better way to resolve it is for the delegation to be the other way round:
class PayrollCalculator
{ …
private Employee emp;
private PayRoll pr;
…
public Money calculatePay(…) { … }
}
class DataBaseStorer
{ …
private Employee emp;
private Database db;
…
public void saveToDatabase(…) { … }
}
// Similar for HoursFormatter
1 * 1
Database DatabaseStorer
1 Employee
1 * 1
PayRoll PayrollCalculator
1
1 1
* HoursFormatter
Display
Dependency
• We wrote of one class “knowing” another, but there are degrees
of linkage between classes
• In our first design, an Employee object HAS-A
PayrollCalculator1, a DatabaseStorer1 and an
HoursFormatter1 object, possibly shared by other Employee
objects. PayrollCalculator1 etc objects do not have an
Employee object as part of their structure, but they have a
method which takes an Employee object and calls methods on it
• In our second design, a PayrollCalculator object has an
Employee object and a PayRoll object, the same Employee
object will also be referenced as part of the structure of a
DataBaseStorer object and an HoursFormatter object
• This is not an IS-A relationship (inheritance)
• Any linkage between classes is a “dependency”, a “weak
dependency” is when a class takes object of another class as
method arguments, but does not call methods on them
Association, Aggregation and Composition
• An “association” is when an object of one class HAS-A link to
an object of another class through an instance variable
• An “aggregation” is a form of association when an object of one
class is made up of objects of other classes - in our second
design a DatabaseStorer object is an aggregation of an
Employee object and a Database object
• A “composition” is a form of aggregation where the objects
joined together exist only to make the aggregation - this is NOT
the case with a DatabaseStorer object as it shares its
Employee and Database objects with other objects which are
also aggregations such as the PayrollStorer object for that
Employee object
• Be careful - these terms are not strictly defined, different authors
may use them with slightly different meanings
Open-Closed Principle (OCP)
• The nQuacks code does not have to be changed to deal with this
• The Duck and DuckCall code do not have to be changed
• But if we had to amend Quacker to introduce this new variation, so
that a Quacker object could represent a duck, a duck-call or a rubber
duck, we would also have to amend the nQuacks1 code
The Dependency-Inversion Principle (DIP)
• The Dependency Inversion Principle can be stated as:
Ø High-level modules should not depend on low-level modules;
instead, both should depend on abstractions
Ø Abstractions should not depend on details; instead, details should
depend on abstractions
• The attempt to generalise by creating a complex Quacker class was an
example of a high-level module which is dependent on a low-level
module, because the nQuacks1 method which used the Quacker class
objects needed to know the details of how those objects work
• The nQuacks method only depends on the abstraction Quackable, it
leaves the details to the classes which implement that interface
• So the nQuacks method shows an example of keeping to the DIP, and
the nQuacks1 method shows an example of not keeping to the DIP
• A simple way of keeping to the DIP is always to use interface types for
parameters to methods and variables in classes
Dependency on abstractions
• The idea of abstraction is that we try to decide what are the important
aspects of some type of object that we need to consider, and what are
unimportant details which we can leave out of consideration
• An abstraction is a description of a type of object just in terms of those
aspects which we need to know about it
• An important part of good program design is to work out what
abstractions we want to define and use
• Code which refers to objects only through variables of an interface type
is dependent only on the abstraction which that interface type defines
• In our example, the abstraction decided on was that we view an object
just in terms of one method which makes a particular sort of noise, we do
not consider the details of the different variations of that noise
• That means detailed decisions on what noise is made and how are left to
the classes which implement the interface, so the details of these
classes are dependent on the interfaces they implement
The Interface-Segregation Principle (ISP)
• The ISP can be expressed as “Clients should not be forced to depend on
methods they do not use”
• The DIP suggests the use of interface types, but the ISP goes further
and stresses the importance of small interface types
• A small interface type is one which has few methods in it
• The ISP suggests if you have an interface type with a lot of methods in it
(a “fat” interface), you should see if it could be broken up into several
interfaces
• A class can implement more than one interface, so a class that provides
all the methods from the original interface can be declared as
implementing all the new small interfaces
• However, it means that new classes which provide some of the services
only have to implement those small interfaces which are relevant to them
• Classes which use the services should refer to them through variables of
interface types which cover only those methods they need
Interface-Segregation Principle example
• As a simple example of the ISP, suppose instead of Quackable we
had decided to have a general interface for duck-like objects:
interface DuckLike
{
void quack();
void fly();
}
• Ducks can fly, but duck-calls cannot, so if we had started off with this,
we would have to give DuckCall a meaningless fly method
• Instead we can have a separate interface
interface Flyer
{
void fly();
}
with
class Duck implements Quackable, Flyer
Java’s interface Comparable<T>
• As another example of the ISP, Java provides the following interface:
interface Comparable<T> {
int compareTo(T obj)
}
• Two objects of a type which implements this interface can be compared
by calling obj1.compareTo(obj2) which returns a negative integer if
obj1 is less than obj2, a positive integer if obj1 is greater than obj2
and 0 if they are equal in “natural order”
• This enables methods to be written such as:
<T extends Comparable<T>> T most(ArrayList<T> list) {
T mostSoFar = list.get(0);
for(int i=1; i<list.size(); i++) {
T element=list.get(i);
if(mostSoFar.compareTo(element)<0)
mostSoFar=element;
}
return mostSoFar;
}
Generalisation using Comparable<T>
In the method
<T extends Comparable<T>> T most(ArrayList<T> list) {
T mostSoFar = list.get(0);
for(int i=1; i<list.size(); i++) {
T element=list.get(i);
if(mostSoFar.compareTo(element)<0)
mostSoFar=element;
}
return mostSoFar;
}
the use of an interface with just the method compareTo means the code is
generalised and does not have dependency on any aspect of the elements
of the ArrayList it takes, except that they have their own method
compareTo.
• An additional generalisation technique here is the use of a type variable
instead of a specific type of element for the ArrayList
• Java provides built-in methods using these generalisation techniques in
its class Collections.
Comparator<T> and Comparable<T>
• Note the distinction between:
interface Comparable<T> {
int compareTo(T obj)
}
and what we saw used previously, which is defined by:
interface Comparator<T> {
int compare(T obj1, T obj2)
}
• Here is a further generalisation of the use of a Comparator, by using a
type variable:
static <E> E most(ArrayList<E> list, Comparator<E> comp) {
E mostSoFar=list.get(0);
for(int i=1; i<list.size(); i++) {
E element=list.get(i);
if(comp.compare(element,mostSoFar)>0)
mostSofar=element;
}
return mostSoFar;
}
Generalising with Type Variables
• Taking the generalisation a bit further is:
static <E> E most(ArrayList<E> list, Comparator<? super E> comp)
{
E mostSoFar=list.get(0);
for(int i=1; i<list.size(); i++) {
E element=list.get(i);
if(comp.compare(element,mostSoFar)>0)
mostSofar=element;
}
return mostSoFar;
}
• The reason for using Comparator<? super E> rather than
Comparator<E> is that E is set to a particular type, but a Comparator of a
supertype of E can compare objects of that type.
• For example, if we have type BankAccount, and a subtype of
BankAccount such as FeeAccount, this enables an
ArrayList<FeeAccount> to be processed with a
Comparator<BankAccount>, with E set to FeeAccount indicating that
the object returned must be of type FeeAccount.
Generalising with Type Variables
• It could be put this way round:
static <E> E most(ArrayList<? extends E> list, Comparator<E> comp)
{
E mostSoFar=list.get(0);
for(int i=1; i<list.size(); i++) {
E element=list.get(i);
if(comp.compare(element,mostSoFar)>0)
mostSofar=element;
}
return mostSoFar;
}
• However this means that if the arguments are an ArrayList<FeeAccount>
and a Comparator<BankAccount>, then E is set to BankAccount, rather
than FeeAccount.
• So that means the method call will be treated in a way that means it could
return an object of type BankAccount that is not of the subtype
FeeAccount.
Inheritance and Generic Types
• A variable of type BankAccount can be set to refer to an object whose actual
type is a subtype of BankAccount, such as FeeAccount.
• However, a variable of a generic type with the generic value set to
BankAccount cannot be set to an object of the same generic type with the
generic value set to a subtype.
• So a variable of type ArrayList<BankAccount> cannot be set to refer to
an object of type ArrayList<FeeAccount>.
• This is to avoid something like list.add(acc) where list is a variable of
type ArrayList<BankAccount> that refers to an object of type
ArrayList<FeeAccount> and acc is a variable of type BankAccount or
another subtype of BankAccount that refers to an object that is not of type
FeeAccount.
• A variable of type ArrayList<? extends BankAccount> can be set to an
ArrayList<BankAccount> object, or an ArrayList whose contents is a
subclass of BankAccount such as ArrayList<FeeAccount>.
Specialising Type Variables
• Alternatively, a type variable can be declared in a way that means it must be
set to BankAccount or a subtype of BankAccount, such as FeeAccount.
• This is another way of generalising code, it means methods from type
BankAccount can be called on variables whose type is the type variable.
• For example:
static <E extends BankAccount> E mostBalance(ArrayList<E> list)
{
E mostSoFar=list.get(0);
for(int i=1; i<list.size(); i++) {
E account=list.get(i);
if(account.getBalance()>mostSoFar.getBalance())
mostSofar=account;
}
return mostSoFar;
}
• This means that if the call to the method mostBalance takes an
ArrayList<FeeAccount> it is declared in a way that guarantees it returns
a FeeAccount object.
Polymorphism and Dynamic Binding
• The ability to write code that works for many different
classes is known as polymorphism (in technical terms,
“poly” indicates “many”, “morph” indicates “shape” or
“appearance”, “ism” indicates an “idea” or “principle”;
remember “anthropomorphism”?)
• The decision on which code to use when a method call
is executed is made at run-time, it might depend on
factors that are not known at compile time - this is
called dynamic binding (sometimes also known as
“late binding”)
Mutable and immutable
class Rectangle { // Is mutable class Colour { // Is immutable
private int height, width; private int red, green, blue;
Colour myColour;
if(rect instanceof ColouredRectangle) {
ColouredRectangle temp = (ColouredRectangle) rect;
myColour = temp.getColour();
} else
myColour = new Colour(0,0,0);
int h = rect.getHeight();
int w = rect.getWidth();
rect.setWidth(w+1);
int area = rect.getArea();
So …
But now …
rect2.equals(rect1) is true - uses code from ColouredRectangle
rect1.equals(rect3) is true - uses code from Rectangle
rect2.equals(rect3) is false - uses code from ColouredRectangle
Isn’t this a problem since if x=y and y=z shouldn’t x=z?
If we solved this problem by writing special code in method equals in
class Rectangle, we would be breaking the Open Closed Principle
(OCP)
interface Rectangle {
public void setWidth(int width);
public int getWidth();
public void setHeight(int height);
public int getHeight();
public int area();
}
with Rectangle replaced by
class PlainRectangle implements Rectangle
and
class ColouredRectangle implements Rectangle
which now needs code for the methods in interface Rectangle as it no
longer inherits code for them
Overriding compareTo
• The method compareTo can be overridden in a similar way to equals
• You are most likely to see compareTo as a String operator, if str1
and str2 are of type String then str1.compareTo(str2) returns:
– A negative integer if str1 is less than str2 alphabetically
– A positive integer if str1 is greater than str2 alphabetically
– 0 if str1 and str2 are equal alphabetically
• But it is more general, objects other classes can be compared in a
similar way
• All classes have equals, but only some classes have a compareTo
method
• If a class called Thing is declared as implements
Comparable<Thing> then it must have its own compareTo method
with header:
public int compareTo(Thing obj)
• We can write such a method in our own class if we want objects of that
class to have a natural order
How to study this material