Download cs304ch6

Document related concepts

Open Database Connectivity wikipedia , lookup

Transcript
Design Patterns in Java
Chapter 6
Bridge
Summary prepared by Kirk Scott
1
Bridge
• The Bridge design pattern, like many others, is
based on identifying abstraction in an
application
• When the abstraction is identified, it is
factored out and implemented separately
• In other words, the design pattern leads to an
abstract class or more likely, a Java interface
2
• When you see a UML diagram of a set of classes
which implement the Bridge design pattern, you
will note the following
– It consists of more than just implementing an abstract
class at the top of a hierarchy
– It consists of more than just designing an interface
and implementing the interface in various classes
• The design pattern is structurally more clever
than that
• It has the effect of eliminating unnecessary
duplication in the implementation code
3
• The Bridge design pattern is also known as the
Driver pattern
• This terminology arises in the context of database
drivers
• It also arises in the context of printers and other
computer devices, for example
• The term bridge is descriptive of how the
structure of the pattern looks in UML
• That term driver is descriptive of the functionality
of the pattern in an application context
4
Bridge
• Book definintion: The intent of the Bridge
pattern is to decouple an abstraction from the
implementation of its abstract operations, so
that the abstraction and its implementation
can vary independently.
• Comment mode on:
• I don’t find this statement very helpful
• I don’t think the idea becomes clear until an
example has been developed
5
An Ordinary Abstraction: On the Way
to Bridge
• The book reviews the general ideas of
abstraction in class and hierarchy design as a
basis for taking up the Bridge pattern
• The UML diagram on the following overhead
shows two different machine controllers for
two different kinds of machines
6
7
• The controller classes have some methods that
probably have the same functionality even though they
have different names
• The usual explanation of the difference in method
names applies
• The classes come from different sources
• For example, the machines may be made by different
manufacturers which supply some of the software
needed to integrate the machines into an automated
production system
• For this reason, the implementations of the given
classes can’t be changed
8
• If possible, it would probably be both conceptually and
practically desirable to create an abstract superclass for
the machine controller classes
• The superclass would potentially contain constructors
and some concrete methods that the subclasses could
inherit
• It would also contain abstract declarations of common
methods which the subclasses would implement
• However, if the class implementations can’t be
changed, building a hierarchy above them in this way
can’t be done
9
• Challenge 6.1
• State how you could apply a design pattern to
allow controlling various machines with a
common interface
• Comment mode on:
• This challenge stems immediately from the
foregoing observation about the classes
• The question is, how do you abstract out a
common interface when you can’t change the
classes themselves?
10
• Solution 6.1
• To control various machines with a common
interface, you can apply the Adapter pattern,
creating an adapter class for each controller.
Each adapter class can translate the standard
interface calls into calls that existing
controllers support.
11
• Comment mode on:
• Although the book didn’t state this in
advance, it becomes apparent that the Bridge
pattern is building on the Adapter pattern
• The UML diagram on the following overhead
shows the current state of development of the
introductory scenario
• It is followed by commentary
12
13
• At the upper left, the abstract
MachineManager class defines the common
interface, ultimately for controllers
• The MachineManager class has abstract
methods as well as one concrete method
• The MachineManager class doesn’t connect
directly with the manager classes
14
• At the bottom of the design are two other
new classes, FuserManager and
StarPressManager
• These classes are subclasses of
MachineManager
• These classes have a reference to a
FuserController object and a
StarPressController object, respectively
15
• What the UML diagram shows is two occurrences
of the Object adapter design pattern
• There have to be two new classes, FuserManager
and StarPressManager, and two occurrences of
the Adapter design pattern
• This is because each of those new classes adapts
one of the two different, distinct controller
objects
16
• Challenge 6.2
• Write a shutdown() method that will stop
processing for the MachineManager class,
discharge the bin that was in process, and
stop the machine
• As usual, the answer isn’t that difficult, but
without problem domain knowledge it’s
difficult to predict what it will be
17
•
•
•
•
•
•
•
Solution 6.2
public void shutdown()
{
stopProcess();
conveyOut();
stopMachine();
}
18
• Comment mode on:
• The code illustrates the technique shown in the
chapter on composites
• It is possible to implement a concrete method in the
abstract superclass that calls abstract methods in the
superclass
• These calls to abstract methods in the superclass rely
on the implementations of the methods in the
subclasses
• The implementations of the methods in the subclasses
ultimately rely on the methods in the adapted objects
19
From Abstraction to Bridge
• The collection of classes introduced so far is
based on a MachineManager class and subclasses
FuserManager and StarPressManager
• In other words, there is a hierarchy based on
different kinds of machines
• Suppose you want to introduce a new kind of
concept, that of a machine manager that includes
a handshaking functionality
• Handshaking refers to the idea of passing status
messages back and forth
20
• In addition to handshaking machine
managers, you’d like to keep the old, nonhandshaking machine managers
• Let handshaking be abbreviated Hsk in class
names
• The following UML diagram illustrates the new
class hierarchy
21
22
• Before going any further with the book’s
explanation, notice that a picture like this came
up in CS 202
• The idea was that there was an inheritance
hierarchy of foods, and you also wanted to
implement the concept of taxability
• Since Java doesn’t support multiple inheritance,
taxability was done with an interface
• The following UML diagram illustrated this in CS
202
23
FoodV5
PackagedFoodV5
BulkFoodV5
TaxedPackagedFoodV5
TaxedBulkFoodV5
«interface»
Taxable Interface
24
• The book’s example and the CS 202 example
are analogous in structure
• The only difference is that the CS 202 example
explicitly identifies an interface
• The thing that should strike you at this point is
that something is wrong
• Namely, how come, when implementing an
interface, every subclass in the inheritance
hierarchy has to have a new subclass?
25
• According to the object-orientation brainwashing,
using object-oriented concepts like inheritance,
you shouldn’t have to re-implement common
things
• But in this case, every taxable subclass, or in the
book’s example, every handshaking manager
class, will have to implement
taxability/handshaking
• Even though the subclasses are different, it is
highly likely that for many of the subclasses, the
implementing code will be largely the same
26
• The book’s UML diagram is shown again below
for reference
27
28
• The book’s diagram shows a setTimeout(:double)
method in both HskFuserManager and
HskStarPressManager
• The book points out that this is a good example
of a method which may be exactly the same in
both
• It can’t be pushed up higher in the hierarchy
because the superclasses of Hsk classes are nonhandshaking classes without this characteristic
29
• As noted in reference to the CS 202 code,
abstracting setTimeout() into an interface also
doesn’t solve the problem, because interfaces
don’t contain implementations
• If the number of subclasses increases, two
practical problems result
30
• 1. You have to write duplicate code x times
• This is not too bad because you can just copy and
paste
• 2. If the design and implementation change in
the future, you have to remember that there
were x places with common code and change
each of them
• Copy and paste helps with the mechanics, but
keeping track of where repetition occurs in
designs is not pleasant
31
• The book takes the approach of trying to
describe in words how the Bridge pattern can
solve this problem
• It then goes through some UML diagrams
illustrating it
• I think it is easier to understand by looking at
the UML diagrams first and then wading
through the words
32
• The book does the UML diagrams as challenges
• The explanation of the first UML diagram is that it
shows the overall structure of the Bridge pattern,
while teasing you about what the components of
it are
• This can be skipped over quickly so that you can
see the complete solution
• Then work backwards trying to understand in
words what was done
• The incomplete UML diagram is shown on the
following overhead
33
34
• Challenge 6.3
• Figure 6.4 shows the MachineManager
hierarchy refactored into a bridge. Fill in the
missing labels.
35
• Before showing the solution, on the following
overhead the starting point is shown again
• In other words, this is what you’ve got, which
is undesirable
36
37
• Solution 6.4
• Figure B.5 shows a solution
• In other words, this is the redesign using the
Bridge pattern
38
39
• There is much to explain here, and many
different ways to go about it
• Overall, structurally, simplistically, you can
refer to the left hand side and the right hand
side of the diagram
• Of course, these designations are arbitrary,
but we will take them as shown in the solution
UML
40
• On the left hand side, the MachineManager2
class remains as a superclass (the 2 is just a
version number, indicating that this is the
MachineManager class in the solution
• A hierarchy grows underneath it, but it’s not a
hierarchy of different machine manager classes
for different kinds of machines
• It is a simple hierarchy with one subclass, the
machine manager subclass with handshaking,
HskMachineManager2 (with version number)
41
• The book’s example never introduced the idea of
an interface, but my example did
• It is worth noting that the concept that was
captured as an interface in my example becomes
a superclass/subclass relationship in the solution
• It is also worth noting that the MachineManager2
class differs from the MachineManager class
because it has a reference to something which
implements the new interface which appears in
this design, the MachineDriver interface
42
• The MachineDriver interface appears at the
top of the right hand side of the diagram
• Before going further, note that its name
contains the word “driver”
• Presumably it could also have been named
MachineBridge
43
• The underlying idea is that the MachineDriver interface
ultimately makes it possible for the MachineManager2
class to make use of instances of any kind of machine
• Visually, the MachineDriver class itself is the bridge
between the manager hierarchy and the machines
• It is important to note, once again, that the link to the
bridge is with a single, open-headed arrow—the
manager has a reference to something that
implements the interface
• For me, the arrow to the MachineDriver class is the
reminder that a bridge is coming in the design
44
• Continuing to consider the MachineDriver
interface:
• It contains method definitions that are
common to machines
• Underneath it in the diagram are classes
named FuserDriver and StartPressDriver that
implement the MachineDriver interface
45
• Again, there is a bit of a turn-about from the
original design
• The original design had an inheritance hierarchy
based on machine manager type
• In the new design the abstractions corresponding
to different kinds of machines don’t appear in an
inheritance hierarchy
• Instead, the different machine drivers, those
abstractions which represent different machines,
implement a common interface
46
• Returning to how the overall structure works:
• An instance of plain MachineManager2
doesn’t have handshaking
• It is a manager of a fuser, for example, by
virtue of the fact that it has a reference to an
object of FuserDriver, which implements the
MachineDriver interface
• The same kind of explanation applies if it’s a
manager of a StarPress
47
• Now observe that it works the same way for
HskMachineManager2
• The reference arrow only appears between
MachineManager2 and MachineDriver
• However, HskMachineManager2 is a subclass
of MachineManager2
• Therefore, it also has a reference to
MachineDriver
48
• Therefore, an instance of
HskMachineManager2, the kind of manager
that has handshaking, manages a certain kind
of machine by virtue of the specific kind of
driver it has a reference to
• Look at the UML diagram again and then
consider the additional comments that follow
it
49
50
• In general, the pattern is based on a decoupling
of managers and machines
• The idea is that the left hand side, the hierarchy
based on machine manager types, can grow and
change
• The right hand side, based on machine types, can
grow and change
• They can grow and change independently
• Changes on one side don’t require (multiple)
changes on the other
51
• More specifically, what you observe is a
decoupling between the abstraction of a machine
manager and the implementation of its abstract
methods
• In the original design the MachineManager class
was abstract and contained abstract methods
• In the new design, the MachineManager2 class,
although not declared abstract, is generically a
kind of abstraction, as is any superclass in a
hierarchy
52
• The key point is this:
• All of the abstract methods that had been in
MachineManager no longer exist in
MachineManager2
• The declarations of all of those methods have
been moved to the MachineDriver interface
• The bridge, or driver is the interface where all of
the abstract methods are moved to
• This is how the separation, or decoupling of the
pattern is accomplished
53
• The explanation for this is not mysterious
• In the original design the machine manager sat above a
hierarchy of classes that concretely implemented the
abstraction of different kinds of machines
• The machines have moved to the right hand side of the
design, so the methods associated with them can be
moved to the interface above them
• All that remains in the machine manager are methods
intrinsic to management—and these can be inherited
by the handshaking subclass, which is also contains
purely management code
54
• Moving the abstract, machine management
methods which are machine specific out of the
machine manager class and into the machine
driver interface has two results:
• The machine management hierarchy can grow
based solely on machine management
characteristics like handshaking
• The driver/machine ‘hierarchy’ can grow to an
arbitrary number of different kinds of machines
55
Defining the Term Driver More
Specifically
• Observe again that an instance of
MachineManager2 has a reference to an object
of the type MachineDriver
• The MachineDriver interface is designed for the
use of the machine manager
• The book expresses the relationship in this way:
• The concrete driver classes that implement the
driver interface adapt manager to requests to
specific kinds of machines
56
• It is these concrete classes like FuserDriver and
StarPressDriver that are the actual drivers
• Finally, this is the book definition of a driver:
• A driver is an object that operates a computer
system or an external device according to a wellspecified interface.
• The book also makes this statement, which
explains the relationship between a bridge and a
driver:
• Drivers provide the most common example of the
Bridge pattern in practice.
57
Drivers as Bridges
• Look at the UML diagram of the new design
for the nth time
58
59
• The book states that each driver is an instance of
the Adapter pattern
• Consider the right hand side of the diagram
• It corresponds to the left hand side of the UML
diagram for the Class Adapter design pattern
• Each concrete machine driver class implements
the machine driver interface, adapting a
particular kind of machine to a client
• The client in this case is a machine manager
60
• If you want to be picky, notice that the definition of a
driver has nothing to with developing the inheritance
hierarchy on the left hand side of the new design
• The left hand side of the design is simply client code,
the bridge class is an adapter, and all that’s missing
from the picture are the machine classes which the
drivers adapt to
• In other words, a driver is a bridge due to its intent
• Structurally, a driver is an application of the idea of
adaptation presented earlier
61
• This view of bridges/drivers leads to some higher level
observations about object-oriented design of software
systems
• Restating at a higher level the idea of decoupling which
was raised earlier:
• This design separates application development (what
happens on the machine manager side) from the
development of the specific machine drivers
• This is a consequence of having introduced an interface
into the design
• The implicit development order here is interface first,
drivers second
62
• You can also view this from the opposite perspective:
• You decide that the design is to be based on drivers
• Then it becomes necessary to develop an abstract
model (interface) for the machine(s)/system(s) to be
driven
• Then in theory, the client (the manager) can do
anything it wants to to any machine through this
common interface
• The development order here is the concept of drivers
first, then develop the common interface, then
implement the drivers to the interface specifications
63
A Limitation to this Approach
• The limitation of the bridge/driver approach
has to do with the design of hierarchies and
interfaces in general, and is not specifically
related to bridges
• When the common interface is designed, the
idea is that every method in the interface
applies to each machine that implements it
64
• However, by definition, machines differ from
each other and each one may have unique
methods of its own
• If those methods are not in common, they may
not be put into the interface
• As a result, individual machines will have
capabilities that a client, a machine manager,
can’t control through calls to methods in the
interface, because the methods aren’t in the
shared interface
65
• There are two approaches to solving this
problem
• 1. Write special case code in the machine
manager
• This code would have to check whether an
object was an instance of a given class before
calling on it a unique method belonging only
to that class
66
• 2. Go ahead and put even unique methods into
the common interface
• Then in the real driver classes, include only
dummy implementations of those methods for
the classes that they don’t apply to
• This eliminates the need for checking code in the
client
• It does imply that the client code needs to be
written under the assumption that for some
method calls ‘nothing may happen’
67
Database Drivers
• An everyday example of the use of drivers in
software arises in the database world
• You may have seen the acronyms ODBC and
JDBC
• They stand for open database connectivity
and Java database connectivity
• They are standards that make it possible to
mount and use different database systems in a
given computer environment
68
• JDBC can be briefly described as an
application programming interface for running
SQL statements
• The key word in the previous statement is
‘interface’
• The API defines a set of valid database calls
that an application program can execute
69
• A JDBC compliant dbms driver implements the
interface and supports the calls
• There is a separate driver for each different
dbms that is supported
• These drivers adapt the interface method call
to the native call in that dbms that supports it
70
• The previous discussion was conducted as if
the client is a single class, there is one
driver/adapter, and one underlying “thing”
that is adapted to
• Of course, reality can be more complex
• Obviously, complexity on the application side
is the application programmer’s concern
71
• Note that in the db world, drivers are things that are
givens, and the application programmer either uses
them or not, but doesn’t develop them
• If they are complex, that is the problem of the
organization that produced the drivers
• The JDBC driver implementation may adapt to more
than one class, for example
• All the application programmer has to worry about is a
single instance of the driver and the interface for
making calls to it
• The diagram on the following overhead gives a simple
overview of the situation
72
73
The End?
• The rest of the chapter continues the topic of
JDBC database drivers
• If there is time, it will be covered in class
• Otherwise, it will be up to students to read
the sections in the book and the remainder of
these overheads on their own
74
• The book continues the JDBC discussion with
some example code
• In effect what you will see is an example of
how to embed SQL access to a database into a
Java program
• The book summarizes the use of a JDBC driver
in this way:
• You load it, connect to a database, and create
a Statement object
75
• Here is the code for those steps:
•
•
•
Class.forName(driverName);
Connection c = DriverManager.getConnection(url, user, pwd);
Statement stmt = c.createStatement();
76
• In the API for a Statement object there is a
method executeQuery() that accepts an SQL
query as a parameter
• This method returns something typed as a
ResultSet
• The ResultSet may contain multiple table rows
• Like one of the Java Collection classes, it is
possible to iterate over the ResultSet, acquiring
the data values from one row at a time
77
• Here is the code for those steps:
• ResultSet result = stmt.executeQuery(“SELECT
name, apogee FROM firework”);
• while(result.next())
• {
•
String name = result.getString(“name”);
•
int apogee = result.getInt(“apogee”);
•
System.out.println(name + “, “ + apogee);
• }
78
• Next, the book shows an incomplete UML
sequence diagram based on the code example
• This is given on the next overhead
• It will be followed by a challenge
79
80
• Challenge 6.4
• Figure 6.5 shows a UML sequence diagram
that illustrates the message flow in a typical
JDBC application. Fill in the missing type
names and the missing message name in this
illustration.
81
Solution 6.4
82
• Challenge 6.5
• Suppose that at Oozinoz, we currently have only SQL
Server databases. Provide an argument that we should
use readers and adapters that are specific to SQL
Server. Provide another argument that we should not
do this.
• Comment mode on:
• What they are struggling to ask here is whether it
makes sense to rely on the existing, commercially
available database driver interfaces, or whether it
makes sense to simply code to the native standard for
SQL Server
83
• Solution 6.5
• Two arguments in favor of writing code specific to SQL
server are as follows.
• 1. We can’t predict the future, so spending money
now to prepare for eventualities that may never occur
is a classic mistake. We have SQL Server now, and
more speed means better response times, which is
money in the bank today.
• 2. By committing to SQL Server, we can use every
feature available in the database, without worrying
whether other database drivers support it.
84
• Two arguments in favor of using the generic SQL
drivers are as follows.
• 1. If we use generic SQL objects to write our code, it
will be easier to modify it if we ever change database
providers and start using, say, Oracle. By locking the
code into SQL Server, we diminish our ability to benefit
from the competitive database market.
• 2. Using generic drivers will let us write experimental
code that runs against inexpensive databases, such as
MySQL, without relying on a test SQL Server database.
85
Summary
• The Bridge pattern ultimately results from
abstraction in a design and a need to ‘factor’
in more than one way
• Given an abstract class (with abstract
methods) the time may come when you would
like to extend it in an orthogonal hierarchy
• Java doesn’t support multiple inheritance, so
that is not directly possible
86
• The Bridge pattern means that the abstract
methods are moved into an interface
• There can be more than one class that
implements the interface
• These classes would have been subclasses of the
original class in the old design
• In the new design, the original class then makes
use of an object that implements the interface
• In the new design, the new hierarchy can be
implemented as subclasses of the original class
87
• Specifically, the abstraction and its
implementation have been decoupled
• More generally, it’s clear that there can be an
unlimited number of classes that implement the
new interface
• Plus, the new hierarchy can grow as needed
• Subclasses of the new hierarchy will be able to
use objects that implement the interface
• This is because the subclasses will inherit the
reference to such objects from the original class,
which now has a reference added to it
88
• The end result of all this is not having to duplicate
method code because one hierarchy is laid over
another
• On the other hand, you also have to deal with the
problem of methods that are unique to individual
classes that implement the hierarchy
• Should they be left out of the interface?
• Or does it make sense to include them in the
hierarchy even though some or most of the
implementing classes will have to provide bogus
implementations of those methods?
89
• Drivers are the most common example of the
application of the Bridge pattern
• Database drivers provide a good software
example of drivers
• Database drivers illustrate the trade-off
between using drivers and not using drivers
• Drivers give you flexibility and generality
• Drivers may not support methods that are
unique to a given database
90
• Not using drivers means writing code specific to a
given database
• On the one hand, this may mean being able to
use unique features and getting good
performance
• On the other hand, it locks your software into
that specific database
• It is not always clear which choice is better, but it
is important to be aware of the trade-off when
making the choice
91
The End
92