Title: COMPOSITE
1Chapter 23
2COMPOSITE Shape Example
ltltinterfacegtgt Shape draw()
0,,
ltltdelegatesgtgt
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
3COMPOSITE 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 lt
itsShapes.size() i) Shape shape
(Shape) itsShapes.elementAt(i)
shape.draw()
4COMPOSITE Shape Example Test Code
public void testCompositeWithNShapes() throws
Exception CompositeShape s new
CompositeShape() for (int i 0 i lt 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
5COMPOSITE 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?
Alternative 1 Sensor maintains a list of Command
objects. Observation Always just iterated over
list and called do()
0..
Command
Sensor
0..
Alternative 2 No modifications to Sensor or
Command classes, just create a CompositeCommand
using COMPOSITE pattern.
Sensor
Command
Composite Command
One-to-one relationship is easier to code,
understand and maintain than a one-to-many
relationship. NOTE Only use COMPOSITE when
every object in list is treated identically
(e.g., just call do(), just call draw(), etc.)
6Chapter 24
- Observer Backing into a Pattern
7To 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
8Chapter 25
- ABSTRACT SERVER and Adapter
- Bridge not covered
9Motivating Example
- Design software that runs inside a simple table
lamp. Table lamp has a 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
- Dependency-Inversion Principle (DIP) because
Switch is dependent on concrete class, prefer to
depend on abstract class - Open-Closed Principle (OCP) because requires
Light everywhere we need a Switch. Not easy to
extend Switch to control other types of objects.
Light turnOn turnOff
Switch
10ABSTRACT SERVER
Cant just create a subclass of Switch because
FanSwitch still inherits dependency upon Light.
Light turnOn turnOff
Switch
Fan turnOn turnOff
Fan Switch
Simple design pattern ABSTRACT SERVER Introduce
an interface in between Switch and Light. Now
Switch can control anything that implements that
interface. Notice the name is Switchable not
ILight. Interfaces belong to the client, not
the derivative. Stronger logical bonding
ltltinterfacegtgt Switchable turnOn turnOff
Switch
Light turnOn turnOff
Fan turnOn turnOff
AbstractServer is a pattern, DIP and OCP are
principles
11ADAPTER
- No access to source code
- Assume have Light class which is not Switchable
- Can add adapter class which delegates to Light
- Potential violation of Single-Responsibility
Principle (SRP) - Light and Switchable may not change for the same
reason.
ltltinterfacegtgt Switchable turnOn turnOff
Extra overhead instantiating the adapter Time and
space for delegation. Use this if needed, prefer
ABSTRACT SERVER
Switch
Fan turnOn turnOff
Light turnOn turnOff
Light Adapter turnOn turnOff
ltltdelegatesgtgt
12Chapter 26
- PROXY and
- STAIRWAY TO HEAVEN
- Managing Third Party APIs
13Barriers 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
14Example problem
0..
Simple shopping cart object model
0..
0..
Shopping cart relational data model
cusid
orderID
0..
sku
15Code 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!
16PROXY 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.
17PROXY static model
ltltinterfacegtgt Product
Product DB Proxy
Product Implementation
ltltdelegatesgtgt
DB
- 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.
18PROXY dynamic model
DB
Product DB Proxy
created by DB
getPrice()
retrieveProduct(sku)
Product Implementation
price
Product
getPrice()
price
Using a proxy is nontrivial.
19PROXY shopping cart
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)
- 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
20PROXY 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)
pricepd.price public String toString()
return ("ProductData("sku","name","pric
e")")
21shopping cart code, continued
public class DB private static Connection
con public static void init() throws
Exception Class.forName("sun.jdbc.odbc.JdbcO
dbcDriver") // load driver con
DriverManager.getConnection("jdbcodbcPPP
Shopping Cart") public static void
store(ProductData pd) throws Exception
PreparedStatement s buildProductInsertionStateme
nt(pd) executeStatement(s) private
static PreparedStatement buildProductInsertionStat
ement(ProductData pd) throws SQLException
PreparedStatement s con.prepareStatement ("INS
ERT into Products VALUES (?, ?, ?)")
s.setString(1, pd.sku) s.setString(2,
pd.name) s.setInt(3, pd.price) return
s
22shopping 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 extractProductDataFr
omResultSet(rs) rs.close()
s.close() return pd private static
PreparedStatement buildProductQueryStatement(Strin
g sku) throws SQLException
PreparedStatement s con.prepareStatement ("SELE
CT FROM Products WHERE sku ?")
s.setString(1, sku) return s
This is the method that returns the ProductData
Query by sku, probably reuse
23shopping cart code, continued
private static ProductData extractProductDataFrom
ResultSet(ResultSet rs) throws SQLException
ProductData pd new ProductData() pd.sku
rs.getString(1) pd.name
rs.getString(2) pd.price rs.getInt(3)
return pd public static void store(ItemData
id) throws Exception PreparedStatement s
buildItemInsersionStatement(id)
executeStatement(s) private static
PreparedStatement buildItemInsersionStatement(Item
Data id) throws SQLException
PreparedStatement s con.prepareStatement ("Inse
rt into Items(orderId,quantity,sku) VALUES (?, ?,
?)") s.setInt(1,id.orderId)
s.setInt(2,id.qty) s.setString(3, id.sku)
return s
Use database result to create ProductData
24shopping 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() s.close() return id
private static PreparedStatement
buildItemsForOrderQueryStatement(int orderId)
throws SQLException PreparedStatement s
con.prepareStatement ("SELECT FROM Items WHERE
orderid ?") s.setInt(1, orderId)
return s
Notice pattern prepare statement, execute it,
extract results, close ResultSet
and PreparedStatement
May also query database by orderid
25shopping cart code, continued
private static ItemData extractItemDataFromResu
ltSet(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
ItemDatal.size())
26shopping cart code, continued
private static void executeStatement(PreparedStat
ement 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
27shopping 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
28shopping 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 public String getName()
throws Exception ProductData pd
DB.getProductData(itsSku) return pd.name
public String getSku() throws Exception
return itsSku
Are accesses to DB going to be a performance
problem? Could change so cache info, but
reasonable to wait til you know if its an issue.
Remember the database engine also does caching.
29Different 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()
30Exercise
- 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(wheatie
s, 2) assertEquals(956, order.total())
31Summary of PROXY
Application
Problem to be solved application becomes
polluted with calls to API, SQL statements, etc.
API
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.
Application
Layer
API
Application
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.
Layer
API
32Summary 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
33STAIRWAY TO HEAVEN
- Achieves same dependency inversion as PROXY
- Variation on class form of ADAPTER
abstract class, write read are abstract,
includes methods that can be used to implement
readwrite.
Product only contains business rules, no hint of
persistence at all.
uses tools of PersistentObject to implement read
write for Products
Persistent Product
Product
implements read write for assembly, inherits
read write for Product fields
Persistent Assembly
Assembly
Pattern requires multiple inheritance virtual
inheritance to deal with deadly diamond issues.
Example code in textbook.
34Other 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.
Database Façade readProduct writeProduct
readAssembly writeAssembly
Product
Assembly