Transcript Chapter 23

Chapter 23
COMPOSITE
COMPOSITE Shape Example
<<interface>>
Shape
0,,*
<<delegates>>
+draw()
Circle
Square
Composite
Shape
+add()
+draw()
• keeps a list of many Shape
instances
• when draw() is called, it delegates to
all Shape instances in the list
• appears to system to be a single
Shape, can be passed to any
function or object that takes a Shape
• really a proxy for a group of Shapes
COMPOSITE Shape Example Code
public interface Shape
{
public void draw();
}
public class CompositeShape implements Shape {
private Vector itsShapes = new Vector();
public void add(Shape s) {
itsShapes.add(s);
}
public void draw(){
for (int i = 0; i < itsShapes.size(); i++)
{
Shape shape = (Shape) itsShapes.elementAt(i);
shape.draw();
}
}
}
COMPOSITE Shape Example Test Code
public void testCompositeWithNShapes() throws Exception
{
CompositeShape s = new CompositeShape();
for (int i = 0; i < 100; i++)
{
s.add(new TestShape());
}
s.draw();
assertEquals(100, drawCount);
}
Tests include Null and TestOneShape, not shown
Notice the call to s.draw(), same as if s were a Shape object
COMPOSITE Sensor/Command Example



Previous example: Sensor objects respond to event (relay,
clutch, etc.), must execute some Command
Used a Command structure with a do() method
What if a Sensor needs to execute multiple commands?
Sensor
Sensor
0..*
Command
Command
Composite
Command
0..*
Alternative 1: Sensor maintains a list of
Command objects. Observation: Always
just iterated over list and called do()
Alternative 2: No modifications to Sensor or
Command classes, just create a
CompositeCommand using COMPOSITE
pattern.
One-to-one relationship is easier to code, understand and maintain than a one-tomany relationship. NOTE: Only use COMPOSITE when every object in list is treated
identically (e.g., just call do(), just call draw(), etc.)
Chapter 24
Observer – Backing into a
Pattern
To Do:
This chapter gives a demonstration of
how the design of a program can
evolve to use a pattern.
 Read the chapter and answer the
chapter questions

Chapter 25
ABSTRACT SERVER and
Adapter
Bridge not covered
Motivating Example


Design software that runs inside a simple table
lamp. Table lamp has a remote switch and light.
You can ask switch if it is on or off, and you can tell
the light to turn on or off.
Various proposed solutions: switch object + lamp
object, lamp object that contains switch + light,
electricity as object, power-cord as object??
Naïve first attempt
Violates 2 principles:
 Open-Closed Principle (OCP) because requires Light
everywhere we need a Switch. Not easy to extend
Switch to control other types of objects
 Dependency-Inversion Principle (DIP) because Switch
is dependent on concrete class, prefer to depend on
abstract class
Remote
Switch
Light
+ turnOn
+ turnOff
Simple Code
public class RemoteSwitch {
private Light light;
private boolean switchedOn = false;
public RemoteSwitch() {
light = new Light();
}
public class Light {
public void turnOn() {
System.out.println("Light is on");
}
public void pressSwitch() {
if (switchedOn) {
}
light.turnOff();
switchedOn = false;
} else {
light.turnOn();
switchedOn = true;
} }
public static void main(String[] args){
RemoteSwitch remote = new
RemoteSwitch();
remote.pressSwitch();
remote.pressSwitch();
} }
public void turnOff() {
System.out.println("Light is off");
}
Naïve Design – Try to Reuse
Remote
Switch
Fan
Switch
Light
+ turnOn
+ turnOff
Fan
+ turnOn
+ turnOff
Can’t just create a subclass of Switch because
FanSwitch still inherits dependency upon Light.
Try to reuse
public class FanSwitch extends
RemoteSwitch {
private Fan fan;
public class Fan {
public void turnOn() {
System.out.println("Fan is on");
}
public FanSwitch() {
fan = new Fan();
}
public void turnOff() {
System.out.println("Fan is off");
}
// Hmmm, how do I reuse anything?
}
}
QUICK EX (without looking ahead): What’s a solution?
ABSTRACT SERVER design pattern
Switch
<<interface>>
Switchable
+ turnOn
+ turnOff
Fan
+ turnOn
+ turnOff
Light
+ turnOn
+ turnOff
Introduce an interface* in between Switch and
Light. Now Switch can control anything that
implements that interface.
Notice the name is Switchable not ILight.
AbstractServer is a pattern,
DIP and OCP are principles
* Remember that an interface can have abstract functions (function signatures)
and constants, but no instance variables. Use the keyword implements.
Some code
public interface Switchable {
public void turnOn();
public void turnOff();
}
public class Light implements Switchable {
public void turnOn(){
System.out.println("Light is on");
}
public void turnOff(){
System.out.println("Light is off");
}
public class Fan implements Switchable {
}
public void turnOn(){
System.out.println("Fan is on");
}
public void turnOff(){
System.out.println("Fan is off");
}
}
And the RemoteSwitch:
public static void main(String[] args)
{
TheRemote lightRemote = new
TheRemote(new Light());
TheRemote fanRemote = new
public TheRemote(Switchable switchedObj)
TheRemote(new Fan());
{
this.switchedObject = switchedObj;
lightRemote.pressSwitch();
}
fanRemote.pressSwitch();
fanRemote.pressSwitch();
public void pressSwitch() {
lightRemote.pressSwitch();
if (switchedOn) {
}
switchedObject.turnOff();
}
switchedOn = false;
} else
{
switchedObject.turnOn();
switchedOn = true;
}
}
public class TheRemote {
private Switchable switchedObject;
private boolean switchedOn = false;
Adapter Pattern
ADAPTER

No access to source code


Assume have CDPlayer class which has required
functionality (e.g., cdOn, cdOff) but function names don’t
match interface
Can add “adapter” class which delegates to CDPlayer
Switch
Fan
+ turnOn
+ turnOff
<<interface>>
Switchable
+ turnOn
+ turnOff
CDAdapter
CDPlayer cd
+ turnOn
+ turnOff
Extra overhead instantiating the adapter
Time and space for delegation.
Use this if needed, prefer ABSTRACT SERVER
<<delegates>>
CDPlayer
+ cdOn
+ cdOff
ADAPTER

Problem may be that class is not Switchable (even
though it has the right functions)
Switch
Fan
+ turnOn
+ turnOff
<<interface>>
Switchable
+ turnOn
+ turnOff
TVAdapter
TV tv
+ turnOn
+ turnOff
<<delegates>>
TV
+ turnOn
+ turnOff
Some Code
public interface Switchable {
public void turnOn();
public void turnOff();
}
public class CDPlayer {
public void cdOn(){
System.out.println("CD is on");
}
public void cdOff(){
System.out.println("CD is off");
}
}
public class CDAdapter implements Switchable {
private CDPlayer cd;
public CDAdapter(){
cd = new CDPlayer();
which to use depends on app – if
}
CD player is created already, use
public CDAdapter(CDPlayer cd){
2nd option
this.cd = cd;
}
public void turnOn(){ cd.cdOn(); }
Delegation!
public void turnOff(){ cd.cdOff(); }
}
Add to TheRemote
public static void main(String[] args) {
// controlling fan and light
CDAdapter adapter = new CDAdapter();
TheRemote cdRemote= new TheRemote(adapter);
cdRemote.pressSwitch();
cdRemote.pressSwitch();
CDAdapter adapter2 = new CDAdapter(new CDPlayer());
TheRemote cdRemote2= new TheRemote(adapter2);
cdRemote2.pressSwitch();
cdRemote2.pressSwitch();
}
More Code
public interface Switchable {
public void turnOn();
public void turnOff();
}
public class TV {
public void turnOn(){
System.out.println("TV is on");
}
public void turnOff(){
System.out.println("TV is off");
}
}
public class TVAdapter implements Switchable {
private TV tv;
public TVAdapter(){
tv = new TV();
}
public TVAdapter(TV tv){
this.tv = tv;
}
public void turnOn(){ tv.turnOn(); }
Delegation!
public void turnOff(){ tv.turnOff(); }
}
Chapter 26
PROXY and
STAIRWAY TO HEAVEN:
Managing Third Party APIs
Barriers to cross
Move data from program to database,
cross the database barrier
 Send message from one computer to
another, cross the network barrier

Database
Can be complex, use patterns to help us cross
these barriers, keep program centered on
more interesting issues…
Example problem
Customer
- name
- address
- billingInformation
0..*
Order
- date
- status
Simple shopping cart object model
0..*
Product
- name
- price
- sku
Item
- quantity
0..*
cusid
Customer
-cusid
- name
- address
- billingInformation
Order
- orderId
- cusid
- date
- status
Shopping cart relational data model
orderID
0..*
sku
Item
- orderId
- quantity
Product
- sku
- name
- price
Code for two examples
public void addItem(Product p, int qty)
{
Item item = new Item(p,qty);
itsItems.add(item);
}
public void addItem(Product p, String sku, int qty)
{
Statement s = itsConnection.CreateStatement();
s.executeUpdate(“insert into items values(
“ + orderId + “,” + sku + “,” + qty + “)”);
}
Same logical function, first ignores database, second glorifies it!
PROXY pattern

Test program to interact with database:
public void testOrderPrice() throws Exception
{
OrderImp o = new OrderImp("Bob");
Product toothpaste = new
ProductImp("sku1", "Toothpaste", 129);
o.addItem(toothpaste, 1);
assertEquals(129, o.total());
Product mouthwash = new
ProductImp("sku2", "Mouthwash", 342);
o.addItem(mouthwash, 2);
assertEquals(813, o.total());
}
Uses object model, does not assume anything about database.
Source for Order, Product, Item in textbook.
PROXY static model
<<interface>>
Product
DB
Product
DB Proxy
<<delegates>>
Product
Implementation
Proxied objects are split into 3 parts:
•Interface with all methods clients need to invoke
•Class that implements those methods
•Proxy that knows about the database
ProductImplementation implements Product interface (set/get price etc)
ProductDBProxy implements methods to fetch product from database,
create an instance of ProductImplementation, delegate messages to it.
Neither client nor ProductImplementation need to know about proxy.
PROXY dynamic model
Product
DB Proxy
getPrice()
price
DB
created by DB
retrieveProduct(sku)
Product
Implementation
Product
getPrice()
price
Using a proxy is nontrivial.
PROXY shopping cart



Will use proxy for
Product class
Implemented as a
simple dictionary – no
table manipulation
(simple example)
Require database
utility to store/retrieve
product data
test shows that DB
works, at least
minimally
public class DBTest extends TestCase
// some methods not shown
{
public void setUp() throws Exception {
DB.init();
DB.clear();
}
public void tearDown() throws Exception {
DB.close();
}
public void testStoreProduct() throws Exception
{
ProductData storedProduct = new
ProductData("MyProduct", 1234, "999");
DB.store(storedProduct);
ProductData retrievedProduct =
DB.getProductData("999");
assertEquals(storedProduct, retrievedProduct);
}
PROXY shopping cart code
public class ProductData
{
public String name;
public int price;
public String sku;
public ProductData() { }
public ProductData(String name, int price, String sku) {
this.name = name;
this.price = price;
this.sku = sku;
}
public boolean equals(Object o) {
ProductData pd = (ProductData)o;
return name.equals(pd.name) &&
sku.equals(pd.sku) && price==pd.price;
}
public String toString() {
return ("ProductData("+sku+","+name+","+price+")");
}
}
shopping cart code, continued
public class DB {
private static Connection con;
public static void init() throws Exception {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // load driver
con = DriverManager.getConnection("jdbc:odbc:PPP Shopping Cart");
}
public static void store(ProductData pd) throws Exception {
PreparedStatement s = buildProductInsertionStatement(pd);
executeStatement(s);
}
private static PreparedStatement buildProductInsertionStatement(ProductData pd)
throws SQLException {
PreparedStatement s = con.prepareStatement
("INSERT into Products VALUES (?, ?, ?)");
s.setString(1, pd.sku);
s.setString(2, pd.name);
s.setInt(3, pd.price);
return s;
}
shopping cart code, continued
public static ProductData getProductData(String sku) throws Exception {
PreparedStatement s = buildProductQueryStatement(sku);
ResultSet rs = s.executeQuery();
ProductData pd = null;
if (rs.next()) {
pd = extractProductDataFromResultSet(rs);
rs.close();
This is the method that returns the ProductData
}
s.close();
return pd;
}
private static PreparedStatement buildProductQueryStatement(String sku)
throws SQLException {
PreparedStatement s = con.prepareStatement
("SELECT * FROM Products WHERE sku = ?;");
s.setString(1, sku);
return s;
Query by sku, probably reuse
}
shopping cart code, continued
private static ProductData extractProductDataFromResultSet(ResultSet rs)
throws SQLException {
ProductData pd = new ProductData();
pd.sku = rs.getString(1);
pd.name = rs.getString(2);
Use database result to create
pd.price = rs.getInt(3);
ProductData
return pd;
}
public static void store(ItemData id) throws Exception {
PreparedStatement s = buildItemInsersionStatement(id);
executeStatement(s);
}
private static PreparedStatement buildItemInsersionStatement(ItemData id)
throws SQLException {
PreparedStatement s = con.prepareStatement
("Insert into Items(orderId,quantity,sku) VALUES (?, ?, ?);");
s.setInt(1,id.orderId);
s.setInt(2,id.qty);
s.setString(3, id.sku);
return s;
}
shopping cart code, continued
public static ItemData[] getItemsForOrder(int orderId) throws Exception {
PreparedStatement s = buildItemsForOrderQueryStatement(orderId);
ResultSet rs = s.executeQuery();
ItemData[] id = extractItemDataFromResultSet(rs);
rs.close();
Notice pattern: prepare statement, execute it,
s.close();
extract results, close ResultSet and
return id;
PreparedStatement
}
private static PreparedStatement buildItemsForOrderQueryStatement(int orderId)
throws SQLException {
PreparedStatement s = con.prepareStatement
("SELECT * FROM Items WHERE orderid = ?;");
s.setInt(1, orderId);
return s;
May also query database by orderid
}
shopping cart code, continued
private static ItemData[] extractItemDataFromResultSet(ResultSet rs)
throws SQLException {
LinkedList l = new LinkedList();
for (int row = 0; rs.next(); row++)
{
ItemData id = new ItemData();
id.orderId = rs.getInt("orderid");
id.qty = rs.getInt("quantity");
id.sku = rs.getString("sku");
l.add(id);
}
return (ItemData[]) l.toArray(new ItemData[l.size()]);
}
shopping cart code, continued
private static void executeStatement(PreparedStatement s) throws SQLException
{
s.execute();
s.close();
}
public static void close() throws Exception {
con.close();
}
public static void clear() throws Exception {
Statement s = con.createStatement();
s.execute("delete * from orders;");
s.execute("delete * from items;");
s.execute("delete * from products;");
s.close();
}
} // MORE METHODS IN TEXTBOOK
shopping cart code, continued
public interface Product {
public int getPrice() throws Exception;
public String getName() throws Exception;
public String getSku() throws Exception;
}
public class ProductImp implements Product {
private int itsPrice;
private String itsName;
private String itsSku;
public ProductImp(String sku, String name, int price) {
itsPrice = price;
itsName = name;
itsSku = sku;
}
public int getPrice() {
return itsPrice;
}
// getName and getSku also in textbook
}
shopping cart code, continued
public class ProductProxy implements Product {
private String itsSku;
public ProductProxy(String sku) {
itsSku = sku;
}
public int getPrice() throws Exception {
ProductData pd = DB.getProductData(itsSku);
return pd.price;
}
Are accesses to DB going
to be a performance problem?
Could change so cache info,
but reasonable to wait til you
know if it’s an issue.
public String getName() throws Exception {
ProductData pd = DB.getProductData(itsSku); Remember the database engine
return pd.name;
also does caching.
}
public String getSku() throws Exception {
return itsSku;
}
}
Different from canonical pattern

Pattern would have ProductProx create a
ProductImp in every method. Wasted effort in this
situation.
public int getPrice() throws Exception {
ProductData pd =
DB.getProductData(itsSku);
ProductImp p = new ProductImp(pd.sku,
pd.name,
pd.price);
return p.getPrice();
}
Exercise




With a partner, create a proxy for Order.
Must pass ProxyTest
Turn in your code + comparison of your code to book
solution/analysis of your effort (was it easy, anything you
missed, etc).
Explain what the author did to remove Exceptions.
public void testOrderProxyTotal() throws Exception {
DB.store(new ProductData("Wheaties", 349, "wheaties"));
DB.store(new ProductData("Crest", 258, "crest"));
ProductProxy wheaties = new ProductProxy("wheaties");
ProductProxy crest = new ProductProxy("crest");
OrderData od = DB.newOrder("testOrderProxy");
OrderProxy order = new OrderProxy(od.orderId);
order.addItem(crest, 1);
order.addItem(wheaties, 2);
assertEquals(956, order.total());
}
Summary of PROXY
Application
Problem to be solved: application becomes polluted with
calls to API, SQL statements, etc.
API
Application
Layer
Common to add a layer between application and API,
but this arrangement has an issue: transitive
dependence from application to API. This indirect
independence may be enough to cause problems.
API
Application
Layer
API
The PROXY pattern inverts this. The Application does not
depend on proxies at all. Proxies depend on the
application and the API. Result: proxies are nightmares.
If API changes, proxy must change. If application
changes, proxy must change. BUT at least change is not
spread throughout application code.
Summary of PROXY, continued
Proxies are a heavyweight solution
 Best to avoid if not needed!
 Could be useful in systems with
frequent schema or API thrashing
 Also used in systems that can ride on
top of many different database
engines or middleware engines

STAIRWAY TO HEAVEN


Achieves same dependency inversion as PROXY
Variation on class form of ADAPTER
Product only contains
business rules, no hint
of persistence at all.
PersistentObject
+ write
+ read
Product
Persistent
Product
Assembly
Persistent
Assembly
abstract class, write & read are
abstract, includes methods that can
be used to implement read&write.
uses tools of PersistentObject to
implement read & write for Products
implements read & write for assembly,
inherits read & write for Product fields
Pattern requires multiple inheritance – virtual inheritance to deal with “deadly
diamond” issues. Example code in textbook.
Other patterns used with databases




Extension object – extension object knows how to
write object
Visitor – the visitor hierarchy knows how to write
visited object
Decorator – decorate a business object with read
and write methods OR decorate a data object than
can read and write with business rules
Façade – Good starting point! But it couples
business-rule objects with the database.
Product
Assembly
Database Façade
+ readProduct
+ writeProduct
+ readAssembly
+ writeAssembly