Download presentation
Presentation is loading. Please wait.
1
Summary prepared by Kirk Scott
Unit 10 Adapter Summary prepared by Kirk Scott
2
Design Patterns in Java Chapter 3 Adapter
Summary prepared by Kirk Scott
3
Adapter, Terminology and Statement of Pattern Intent
One code base is a client if it needs to make use of the functionality of methods in another code base, which would be the service provider Observe the possible sequence of events in large scale or disjointed development: The client code is written before the service code, and the client makes calls to methods that do not end up in the service code base specifications and implementation
4
Adapter, Terminology and Statement of Pattern Intent, cont’d.
Client and service code may also simply be written independently, without (advance) knowledge of each other’s interfaces Or, the specifications for the code bases belong to different organizations and the differences between their interfaces are simply set in stone or fixed for other reasons Whatever the reason, the scenario is that the service code contains useful functionality, and the underlying problem is that the client code was written to call methods by names that don’t exist in the service code
5
This may sound like a screw-up and the the ideal solution might seem to be rewriting one or the other of the code bases so that the method names agree However, if the specifications of the service code base have been set, recoding isn’t an option Alternatively, once the code exists, recoding everything so that it agrees may be undesirable because it is too much work
6
In a situation like this, the Adapter pattern can be used
The basic idea is that by insightful use of interfaces and subclasses, adding one class to the system makes it possible for the client code base to use the service code base
7
There are actually several adapter scenarios
The simplest case is when the client code developer planned in advance for adaptation by implementing a Java interface of the necessary methods There are other scenarios which fulfill the intent of adaptation but are somewhat different due to the absence of a Java interface or for other reasons
8
Adapter, Terminology and Statement of Pattern Intent
Statement of Intent: The intent of Adapter is to provide the interface that a client expects while using the services of a class with a different interface
9
Adapting to an Interface
In the ideal case, the client developer realizes that services may eventually be required from another code base The client developer then creates a Java interface which includes all of the methods which the client code calls
10
An Adapter class implements this interface
The Adapter class also extends the class in the service code base which contains the useful methods The implementation of the interface methods in the Adapter class are based on calls to the useful, but “misnamed” methods inherited from the superclass in the service code base
11
The following diagram illustrates this basic version of the Adapter design pattern
13
Concretely, the client makes use of an interface, RequiredInterface, which specifies the use of a method named requiredMethod() The service code base contains an ExistingClass with a method named usefulMethod() which contains the needed functionality The NewClass is an adapter which implements the interface and extends ExistingClass In NewClass, the implementation of requiredMethod() is based on calls to usefulMethod()
14
Before moving on, there is another aspect of this to consider
Apparently the book considers it obvious, but when reading the book, the question arose in my mind In the diagram, the client is shown as making use of the interface by means of a line with an open arrowhead
15
What does it mean for the client to make use of the interface?
Typically, you might think that the client code would contain calls of this sort: newClassObject.requiredMethod() But this can’t be the case The scenario is that the client doesn’t know about the contents of the service code base, and by definition, it would have been written before the adapter class was written
16
How does the client “know” to use an object that conforms to the interface, or how does it acquire an object of a class that implements the interface? Clearly, the client code cannot be rewritten to use objects of class NewClass There are two parts to the answer to this question
17
The first part of the answer is that although not explicitly shown, part of the use of the client consists of passing in references to objects A reference to an object of NewClass could be passed into a method of the client code base How can this be, if the client code does not “know about” NewClass? Read on
18
A second part of the answer is that the input parameter to a method of the client could be typed to the interface, not to a particular class This is an approach that didn’t come up in CS 202, but it will recur in this book Part of the power of interfaces is that they can be used as types
19
If the client developer figured out the interface, then the client code can have objects and parameters typed to that interface wherever needed Then truly, all that’s required for successful adaption is the creation of an adapter class that implements the interface Wherever that interface is needed, references to the adapter class can be used
20
A More Concrete Example
The book next explains more by working with specific example code Oozinoz makes rockets, and in its code base is a class named EventSim, which runs simulations of rocket performance The code base also includes the specifications for an interface, RocketSim, which represents rockets/rocket characteristics EventSim was written to make use of the RocketSim interface This is shown in the following UML diagram
22
The foregoing description and diagram represent the client side of an illustration of the Adapter class In addition, Oozinoz has a PhysicalRocket class which represents rockets The PhysicalRocket class has a set of methods which are useful, and more or less analogous to the methods in the RocketSim interface Unfortunately, they do not have the same names
23
Suppose you would like to run EventSim and apply it to instances of the PhysicalRocket class
You can’t, but the situation is ripe for solution using the Adapter design pattern The following UML diagram illustrates creating a new class, OozinozRocket, which implements the RocketSim interface and extends the PhysicalRocket class
25
Notice that the methods on the client and server sides don’t agree exactly in name or other characteristics Frequently there isn’t a simple one-to-one correspondence between methods with exactly the same function but different names In this respect, the example is relatively realistic Things are rarely rock bottom simple and the adapter will have to deal with the mismatch
26
A brief overview reveals:
There are getMass() and getThrust() methods in both the interface and the class However, these methods take a time parameter in the class methods and take no parameter in the interface
27
There is also a setSimTime() method in the interface
It takes a time parameter Recall that when implementing an interface, it is up to the programmer to supply any needed variables They are not specified in the interface This suggests that some sort of time variable will be lurking somewhere in the interface implementation
28
There is also a getBurnTime() method in the server side class
It is not immediately apparent how this might be used to implement the interface methods in the Adapter class On the other hand, it does seem like a potential source for the value of the time parameter
29
Overall, domain knowledge (an explanation by the authors of how the simulation works) is needed in order to sort out the implementation of the Adapter class The authors add that RocketSim keeps an internal clock (typical of event simulators) and occasionally updates simulated objects by calling the setSimTime() method
30
When implementing the OozinozRocket Adapter class, it will be necessary to maintain a time instance variable which can be passed as a parameter to the PhysicalRocket class methods as needed Although this really hardly seems like enough background information, the author now presents challenge 3.1 As usual, lack of information is no problem—we’re just going to look immediately at the provided solution anyway and try to figure it out
31
Challenge 3.1 Complete the class diagram in Figure 3.3 to show the design of an OozinozRocket class that lets a PhysicalRocket object participate in a simulation as a RocketSim object. Assume that you can’t alter either RocketSim or Physical Rocket. Note that although in a sense the warning “Thou shalt not alter” should go without saying, it is a useful reminder of the reality that the Adapter pattern is intended to address.
32
Solution 3.1
33
There are no surprises in the diagram shown
We were warned that a time variable would be needed Also, all methods of the interface are shown in the Adapter class—as they should be The only potentially unexpected thing is the inclusion of a constructor specification in the Adapter class However, this also doesn’t contain surprises It exactly parallels the constructor in the superclass
34
The devil, then, is in the code writing
In other words, you know what methods you need to include; how do you write them? Once again, domain knowledge will probably be necessary In the simplest of all possible cases, it might be possible to wrap a call to a class method inside the body of an interface method
35
On the other hand, it may also be necessary to write more complex implementations of the interface methods in order to make successful use of the functionality in the class methods The authors next give partial code for the Adapter class, leaving the implementations of two of the methods as challenges
36
package com.oozinoz.firework;
import com.oozinoz.simulation.*; public class OozinozRocket extends PhysicalRocket implements RocketSim { private double time;
37
public OozinozRocket (double burnArea, double burnRate, double fuelMass, double totalMass) { super(burnArea, burnRate, fuelMass, totalMass); }
38
public double getMass()
{ // Challenge! } public double getThrust() public void setSimTime(double time) this.time = time; Note the use of this.time = time This is an evil aberration from all that’s right and good and true…
39
Challenge 3.2 Complete the code for the OozinozRocket class, including methods getMass() and getThrust().
40
Before looking at the solution, consider these two observations
1. Obviously you don’t have to implement getBurnTime() It’s a method in the class, not the interface, and it’s inherited It also turns out that it plays no role in the implementation of the interface methods
41
2. The solution turns out to be deceptively simple
Although the worst-case scenario is that the client developer has no advance knowledge, it seems that in this case the client developer knew or foresaw something about the service code base The interface includes the setSimTime() method
42
Formally, there is no indication in the specifications of the getMass() and getThrust() methods of the interface that a simulation time would be needed in order to implement those methods However, if you look at the PhysicalRocket class and see that the analogous methods require a time parameter, then it becomes necessary to support a time variable in the interface and provide a method for setting its value
43
Solution 3.2 public double getMass() { return getMass(time); }
public double getThrust() return getThrust(time);
44
Just to state the obvious, both getMass() and getThrust() are overloaded in the Adapter class
The class inherits the versions which take parameters and implements the versions which don’t The example ultimately turns out to be an example of the simplest kind, once you understand the treatment of the time variable
45
The interface versions of the methods don’t have a time parameter
The server code versions of the methods do have a time parameter In the adapter code, a call to the client method is wrapped inside the implementation of the adapter version of the method
46
Class and Object Adapters
The approach illustrated previously is know more specifically as a class adapter This is the case where you implement a client interface and extend a service class A different approach has to be taken if the client code base does not specify a particular interface In this second case it’s necessary to create what is known as an object adapter
47
When implementing an object adapter, the idea is that rather than a Java interface in the client code, there is a class with a set of methods that need to be supported The object adapter approach is to make a subclass of the client code class and then make use of an object of the service code base to help implement the methods of the subclass Take a look at the following UML diagram
49
The book describes the object adapter, the NewClass, as adapting by means of delegation
Observe that NewClass is connected to ExistingClass by means of a navigability arrow This means that NewClass has a reference to an object of ExistingClass and can call ExistingClass methods on that object as part of the code implementing the methods in NewClass The idea is that the methods in NewClass override the methods in RequiredClass, providing the needed functionality by means of calls on ExistingClass objects
50
Note that the class adapter and object adapter UML diagrams are different
That is to say, the structure of these two different kinds of adapters is not the same However, they are both classified as the adapter pattern because their intent is exactly the same They differ structurally because in the adapter case the client provides an interface and in the object case the client provides a class It will also become evident later that there are other reasons or cases whether the object adapter is desirable
51
In the previous example, EventSim was the client, and it made use of the predefined RocketSim interface This made it possible for the OozinozRocket class to be written to adapt to the existing PhysicalRocket class
52
Now suppose that the EvenSim client is coded to work directly with objects of the class Skyrocket
The following UML diagram illustrates this basic idea Not surprisingly, the Skyrocket class has methods getMass(), getThrust(), and setSimTime()
54
The foregoing diagram didn’t show the server side, but the idea is similar to what it was before
There are two ways of stating what you want to do 1. You want to use the methods of the existing PhysicalRocket class to support the functionality of the methods of the Skyrocket class 2. You want, in one way or another, to be able to run the client code simulation on an object of the PhysicalRocket class
55
Under this scenario, presumably the methods have been implemented for the Skyrocket class
But we don’t care—we’re going to override them and call methods on a PhysicalRocket object in the overridden versions The next UML diagram shows the outline of an object adapter solution to this problem
57
Before going any further, note the following
If Java supported multiple inheritance, in theory the OozinozSkyrocket class could be a subclass of both SkyRocket and PhysicalRocket In that case, the Class Adapter could more or less be duplicated in solving this problem However, multiple inheritance is a mess and Java eschews it Therefore, it is necessary to seek an alternative solution
58
Before trying to complete the diagram it is possible to make this observation
The Class Adapter pattern relied on typing a parameter, for example, to an interface The Object Adapter pattern relies on simple polymorphism In the client code, a parameter would be typed to the superclass, Skyrocket, and a reference to the subclass, OozinozSkyrocket, could be passed to the client code
59
Note also that the simulation time variable, as in the previous example, has to be dealt with
Observe that in this most recent diagram, the authors have indicated something which wasn’t shown in the original generic diagram Namely, on the client side there is a double instance variable simTime It is marked with a # sign, meaning that it’s declared protected That means that the new subclass under development will have access to its own simTime instance variable without resorting to a call to a getSimTime() method
60
Challenge 3.3 Complete the class diagram in Figure 3.6 to show a design that allows OozinozRocket objects to serve as Skyrocket objects.
62
The critical things to notice in this diagram are the following
As in the generic picture earlier, there is a navigability arrow from the subclass, the OozinozSkyrocket class, to the object class, the PhysicalRocket class In the box representing the OozinozSkyrocket class an instance variable is shown—namely, a reference to an object of the PhysicalRocket class It is this object in the subclass that gives the object adapter pattern its name
63
You should also notice that the setSimTime() method doesn’t have to be overridden
Its implementation in the Skyrocket class is suitable for the subclass and is simply inherited
64
Rather than doing the code of the OozinozSkyrocket class as a challenge, the book simply gives the code This shows how an instance of the PhysicalRocket class is created and used to support the needed functionality The code follows
65
The outset of the OozinozSkyrocket class code.
package com.oozinoz.firework; import com.oozinoz.simulation.*; public class OozinozSkyrocket extends Skyrocket { private PhysicalRocket rocket;
66
The book’s constructor for the subclass, which relies on an instance of the PhysicalRocket class being passed in public OozinozSkyrocket(PhysicalRocket r) { super(r.getMass(0); r.getThrust(0); r.getBurnTime()); rocket = r; }
67
Notice that you are calling methods from the PhysicalRocket class
Those versions of getMass() and getThrust() take a time parameter Not surprisingly, at construction time, the parameter value is time = 0
68
Also notice that in theory you could write the constructor for the subclass to take in the construction parameters for a PhysicalRocket object Then instead of passing in an instance of PhysicalRocket, it could be constructed inside the code for the OozinozSkyrocket constructor This is not better, just an alternative One way or the other, the OozinozSkyrocket class has to end up with a live PhysicalRocket reference inside
69
The method implementations in the OozinozSkyrocket class code
public double getMass() { return rocket.getMass(simTime); } public double getThrust() return rocket.getThrust(simTime);
70
Compare the foregoing with the solution in the Class Adapter case:
public double getMass() { return getMass(time); } public double getThrust() return getThrust(time);
71
In the Class Adapter case you make use of methods inherited from the PhysicalRocket class in order to implement the adapter methods In the Object Adapter case, you receive a reference to or create an instance of the PhysicalRocket class and then call methods in the PhysicalRocket class on that object in order to implement the adapter methods
72
Challenge 3.4 Name one reason why the object adapter design that the OozinozSkyrocket class uses may be more fragile than a class adapter approach.
73
Solution 3.4 The object adapter design that the OozinozSkyrocket class uses may be more fragile than a class adapter approach for the following reasons. There is no specification of the interface that the OozinozSkyrocket class provides. As a result, the Skyrocket might change in ways that would create problems at runtime but go undetected at compile time.
74
The OozinozSkyrocket counts on being able to access the simTime variable of its parent class, although there is no guarantee that this variable will always be declared as protected and no guarantee about the meaning of this field in the Skyrocket class. (We don’t expect the providers to go out of their way to change the Skyrocket code we rely on, but on the other hand, we have limited control over what they do.)
75
The book closes this section by stating that the object adapter approach is riskier than the class adapter approach This was suggested by the answers to challenge 3.4 However, you do what you have to do If no client interface is given, the object adapter works The book additionally notes that if one of the methods in the client superclass had been declared final, meaning that it couldn’t be overridden, then we would have been screwed…
76
The End? The rest of the chapter is on the topic of adapting data for a JTable This is an interesting, practical example that arises out of the Java API 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
77
Adapting Data for a JTable
There is a class in the Java API, JTable, which is designed to display tables The JTable class was written generally, so that it could accept data of various types as table entries This was accomplished by providing an interface, TableModel In theory, it would be possible to write a class adapter using the TableModel and some application domain class The interface is shown on the next overhead
79
There are several reasons that typically for table data a form of object adapter is preferable
The first is that the contents of a table likely stem from a collection of domain objects, not from a single class Also, it turns out that for many of the methods in the TableModel interface it is possible to provide default implementations
80
This is sort of like an incomplete Java adapter class
The Java API provides an abstract class, AbstractTableModel which includes default implementations of most of the methods in the interface This is sort of like an incomplete Java adapter class The Java API points out the following: To create a concrete TableModel as a subclass of AbstractTableModel you need only provide implementations for the following three methods: public int getRowCount(); public int getColumnCount(); public Object getValueAt(int row, int column);
81
A UML diagram for the AbstractTableModel class is shown on the next overhead
It indicates the three methods, getColumnCount(), getRowCount(), and getValueAt() Presumably these methods are declared abstract in the class That’s how come you need to write a concrete subclass that implements them
83
Now it’s possible to use the Object Adapter design pattern with this class
In the book’s example, the RocketTableModel class extends the AbstractTableModel class The RocketTableModel class implements, among other things, the three needed methods It also has an array of instances of the Rocket class which are the source of information for rows in the table This is shown in the next diagram
85
Notice that the UML diagram for the RocketTableModel shows two instance variables, one an array of rockets, another an array of column names A constructor is shown for the class which takes an array of rockets as an input parameter Also, in addition to the 3 expected methods, an additional method, getColumnName() is shown
86
AbstractTableModel implements the TableModel interface
By having RocketTableModel extend AbstractTableModel, you are creating an object adapter This approach is handy because the RocketTableModel can have a handle on multiple instances of the Rocket class Witness the array of rockets instance variable and parameter in the constructor
87
It is also conceptually necessary to have an object adapter rather than a class adapter
Under the Class Adapter design pattern you would write an adapter which implemented the interface directly and extended the Rocket class But this wouldn’t make sense, because the purpose of the adapter in this example is to extract information about more than one rocket Such a Class Adapter class would not be a subclass of the Rocket class, as needed in order for that design pattern to be applied
88
The book restates the characteristics of the two kinds of adapters in this way:
A class adapter subclasses an existing class and implements a target interface An object adapter subclasses a target class and delegates to an existing class The use of the word delegates is worth remembering The composition/navigability arrow in the diagram indicates that the RocketTableModel makes use of multiple instances of the Rocket class This is succinctly summarized in the term “delegates”
89
The book finally illustrates the purpose of all of this with a screenshot
It follows on the next overhead
91
Next the book gives partial code for an implementation of the RocketTableModel class
Since the missing pieces are marked “Challenge!” you can predict what comes after the partial code The code follows
92
import javax.swing.table.*;
import com.oozinoz.firework.Rocket; public class RocketTableModel extends AbstractTableModel { protected Rocket[] rockets; protected String[] columnNames = new String[] { "Name", "Price", "Apogee" }; public RocketTableModel(Rocket[] rockets) this.rockets = rockets; }
93
public int getColumnCount()
{ // Challenge! } public String getColumnName(int i) public int getRowCount() public Object getValueAt(int row, int col)
94
Challenge 3.5 Fill in the code for the RocketTableModel methods that adapt an array of Rocket objects to serve as a TableModel
95
import javax.swing.table.*;
import com.oozinoz.firework.Rocket; public class RocketTableModel extends AbstractTableModel { protected Rocket[] rockets; protected String[] columnNames = new String[] { "Name", "Price", "Apogee" }; public RocketTableModel(Rocket[] rockets) { this.rockets = rockets; }
96
public int getColumnCount()
{ return columnNames.length; } public String getColumnName(int i) return columnNames[i]; public int getRowCount() return rockets.length;
97
public Object getValueAt(int row, int col)
{ switch (col) case 0: return rockets[row].getName(); case 1: return rockets[row].getPrice(); case 2: return new Double(rockets[row].getApogee()); default: return null; }
98
The book provides example application code which uses the RocketTableModel adapter
In this code, instances of Rocket are created and placed in an array An instance of RocketTableModel is constructed with the rocket array passed in as a parameter Swing classes are used to display the table Code for this example application follows
99
import java.awt.Component;
import java.awt.Font; import javax.swing.*; import com.oozinoz.firework.Rocket; import com.oozinoz.utility.Dollars; public class ShowRocketTable { public static void main(String[] args) setFonts(); JTable table = new JTable(getRocketTable()); table.setRowHeight(36); JScrollPane pane = new JScrollPane(table); pane.setPreferredSize(new java.awt.Dimension(300, 100)); display(pane, " Rockets"); }
100
public static void display(Component c, String title)
{ JFrame frame = new JFrame(title); frame.getContentPane().add(c); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } private static RocketTableModel getRocketTable() Rocket r1 = new Rocket("Shooter", 1.0, new Dollars(3.95), 50.0, 4.5); Rocket r2 = new Rocket("Orbit", 2.0, new Dollars(29.03), 5000, 3.2); return new RocketTableModel(new Rocket[] { r1, r2 }); private static void setFonts() Font font = new Font("Dialog", Font.PLAIN, 18); UIManager.put("Table.font", font); UIManager.put("TableHeader.font", font);
101
The book sums up this example in this way
The ShowRocketTable class is a short application that can show rocket information in an instance of JTable It can be short and sweet because of the existence of the TableModel interface, the AbstractTableModel class, and the RocketTableModel class which extends the abstract class
102
The RocketTableModel implements the object adapter design pattern
It has instances of the Rocket class in it It is information about these instances of the Rocket class that is used to fill the rows of the JTable generated by the ShowRocketTable application In other words, the JTable class can’t know in advance which of the many types of data an application may wish to display in its cells The adapter design adapts rocket data for display in a JTable
103
Identifying Adapters Consider the following UML diagram for the Java MouseAdapter class, and the challenge that follows it
105
Challenge 3.6 Are you applying the Adapter pattern when you use the MouseAdapter class? Explain how (or why not).
106
Solution 3.6 One argument: When the user clicks the mouse, I need to translate, or adapt, the resulting Swing call into an appropriate action. In other words, when I need to adapt GUI events to my application’s interface, I use Swing adapter classes. I am translating from one interface to another, fulfilling the intent of the Adapter pattern.
107
A counterargument: The “adapter” classes in Swing are stubs: They don’t translate or adapt anything. You subclass these classes, overriding the methods you need to do something. If anything, it is your methods and your class that form an example of Adapter. Had the Swing “adapter” been named something like DefaultMouseListener, this argument never would have arisen.
108
Summary A class adapter implements an interface and extends a class in order to adapt the class to the interface An object adapter extends a class and makes use of objects in order to adapt the objects to the class JTable, TableModel, and AbstractTableModel provide examples of an adapter pattern in the Java API For practical and conceptual reasons, the table example is an example of an object adapter If you have a grip on the basic idea underlying adapters, you may be able to use them in your own code
109
The End
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.