Title: M'Sc Computing Science Software Engineering Lecture 8
1M.Sc Computing Science Software Engineering
Lecture 8
- Niki Trigoni
- Department of Computer Science
- and Information Systems
- Birkbeck College, University of London
- Email niki_at_dcs.bbk.ac.uk
- Web Page http//www.dcs.bbk.ac.uk/niki
2Review of lecture 7
- We identified new requirements in Iteration 2 of
elaboration - Support for variations in third-party external
services - Complex pricing rules
- Pluggable business rules
- GUI window updates when information changes
- We focused on first requirement and introduced a
set of patterns to help its design - GRASP Polymorphism, Pure Fabrication,
Indirection, Protected Variations - GoF Adapter, Factory and Singleton
3Overview of lecture 8
- Examples of applying the following patterns
- GRASP Polymorphism, Pure Fabrication,
Indirection, Protected Variations - GoF Adapter, Factory and Singleton
- More GoF patterns
- Strategy and Composite
- Facade
- applicable to requirements
- Complex pricing rules
- Pluggable business rules
4Orientation within the Unified Process
quick overview
focus of this lecture
5Polymorphism example
- When related alternatives or behaviors vary by
type (class), assign the same name to services
(methods) in different classes. The different
classes usually implement a common interface or
have a common superclass. - Example
- Consider a bank application with two types of
accounts CheckingAccount and SavingsAccount. - Suppose that you want to evaluate for each
account the interest accumulated over a period. - The implementation of evaluating interest is
different for each account type. It uses a
different set of interest rates.
6Polymorphism example (cont.)
- Solution 1 Class SavingsAccount with method
getSavAccInterest(startDate, endDate), and class
CheckingAccount with method getChAccInterest(start
Date, endDate). - First go through objects of class SavingsAccount
and invoke their method getSavAccInterest() - Then go through objects of class CheckingAccount
and invoke their method getChAccInterest() - Solution 2 Same as Solution 1, but
SavingsAccount and CheckingAccount have the same
superclass Account. - Go through all objects of class Account. If an
object is of subclass SavingsAccount, invoke
getSavAccInterest(), else invoke
getChAccInterest()
7Polymorphism example (cont.)
- Solution 3 Class SavingsAccount with method
getInterest(startDate, endDate), and class
CheckingAccount with method getInterest(startDate,
endDate) inherit from a common superclass with
method getInterest(startDate, endDate). - Go through all objects of class Account. Invoke
their method getInterest() - What are the advantages of solution 3 over the
previous two solutions? - In terms of code
- In terms of maintenance
8Pure Fabrication example
- Assign a highly cohesive set of responsibilities
to an artificial or convenience class that does
not represent a problem domain concept
something made up to support high cohesion, low
coupling, and reuse. - Example Consider a bank application with objects
of class Account, Customer, Transaction etc.
that need to be inserted into, updated, or
deleted from the database. - Solution 1 By Information Expert, assign methods
insertIn(OODB db), update(OODB db),
deleteFrom(OODB db) to classes Account, Customer,
Transaction etc. - Solution 2 Use polymorphism and assign methods
insertIn(OODB db), update(OODB db),
deleteFrom(OODB db) to superclass Object.
9Pure Fabrication example (cont.)
- Solution 3 Since these functions are highly
correlated, and they do not concern domain logic,
group them together into class OODB with methods
insert(Object obj), delete(Object obj),
update(Object obj). - What are the advantages of solution 2 over
solution 1? - What are the advantages of solution 3 over
solution 2?
10Indirection example
- Avoid creating multiple couplings to an unstable
class C1?UnstClass, C2 ?UnstClass,, Cm
?UnstClass. Instead, devise a mediator stable
class StableClass, and use it to connect
indirectly C1, , Cm with UnstClass. - Example Suppose that our company initially
decides to use an object-oriented database
system. If performance issues arise, the company
will switch to a relational db. How should we
perform database-related operations for storing
objects of classes Transaction, Account,
Customer, etc.? - Solution 1 Wherever db operations are needed in
the code interact directly with an object of
class OODB to perform them.
11Indirection example
- Solution 2 Wherever db operations are needed in
the code interact with an object of a new class
PersistentStorage to perform them. The methods of
PersistentStorage (insert, update, delete) invoke
the corresponding methods of OODB. - Which solution do you prefer?
- What is the impact of change (to relational DB)
for each one of the solutions?
12Protected Variations example
- Design objects, systems and subsystems so that
the variations and instability in these elements
does not have an undesirable impact on other
elements. - All previous examples are instances of applying
this general principle. - Another example is the Dont Talk to Strangers
principle avoid sending messages to distant,
indirect objects, because this leads to fragile
code fragments. - Example Suppose that within a method of Sale, we
want to access the holder of the account of the
payment of this sale. - Sale has an attribute paymentPayment
- Payment has an attribute accountAccount
- Account has an attribute holderPerson.
13Protected Variations example (cont.)
- Solution 1
- public void methodOfSale()
-
- Person holder this.getPayment().getAccount
().getHolder() -
- Solution 2
- public void methodOfSale()
-
- Person holder this.getAccountHolderOfPayme
nt() -
- Which solution do you prefer and why?
14Adapter (GoF) example
- Use adapter classes when in need of common stable
interface to similar classes with different
interfaces - Example Consider the problem of processing
payments in a store using two external credit
services - Visa.sendPayment(cardID, sale.amount)
- Mastercard.makePayment(cardID, sale.amount,
sale.date) - Solution 1 In method Sale.makePayment method, we
check whether the cardID corresponds to a Visa or
a Mastercard and we invoke either of the methods
accordingly. - Visa.sendPayment(cardID, sale.amount)
- Mastercard.makePayment(cardID, sale.amount,
sale.date) -
15Adapter (GoF) example (cont.)
- Solution 2 We create two adapter classes,
VisaAdapter and MastercardAdapter, with the same
method signature arrangePayment(cardIDCardID,
saleSale). Continue - Which solution do you prefer and why?
- Can you think of other scenarios where adapters
are useful?
16Factory (GoF) example
- Use Factory objects to create objects when
creation logic is complex, or efficient memory
management is needed, or many related creation
responsibilities should be separated for better
cohesion. - Example Suppose that a store adjusts the prices
of its products each day. Based on the profits of
the previous 10 days and the recommendations of
managers, it comes up with a different pricing
policy. How do we apply the daily pricing policy
to sales?
17Factory (GoF) example
- Solution 1 Each Sale object constructs a new
PricingPolicy object by passing as arguments the
date, profits, etc. - Solution 2 Like Solution 1, except that the
Register object constructs the PricingPolicy
object once and makes it visible to each Sale. - Solution 3 A new PricingPolicyFactory is defined
that is responsible for instantiating the
PricingPolicy object at the beginning of each day
(or when the first Sale occurs). - Which solution do you prefer and why?
- In solution 3, who creates the PricingPolicyFactor
y object and how does it become visible to Sale
objects.
18Singleton (GoF) example
- How to get global visibility to a single instance
of a class? - Example How will Sale objects obtain visibility
to the PricingPolicy instance created at the
beginning of the day? - Solution 1 Create a PricingPolicyFactory
instance in Register, call its method
getPricingPolicy and store the PricingPolicy
object as an attribute of Register. Register will
then pass it as parameter to all invocations of
Sale methods. - Solution 2 Create a PricingPolicyFactory
instance in Register, call its method
getPricingPolicy (like solution 1). Get global
visibility to the policy instance, by invoking a
static method PricingPolicy.getInstance().
19Singleton (GoF) example (cont.)
- Solution 3 Get global visibility to a
PricingPolicyFactory instance by calling
PricingPolicyFactory.getInstance(). Let factory
be the returned object. Then get the actual
pricingPolicy object by calling
factory.getPricingPolicy(). Think of an efficient
implementation of the last method. - Which solutions apply the Singleton pattern and
at which point?
20Overview of lecture 8
- Examples of applying the following patterns
- GRASP Polymorphism, Pure Fabrication,
Indirection, Protected Variations - GoF Adapter, Factory and Singleton
- More GoF patterns
- Strategy and Composite
- Facade
- applicable to requirements
- Complex pricing rules
- Pluggable business rules
21Complex pricing rules
- There may be more than one pricing
policies/strategies for a sale - 10 off for one period
- 10 pounds off if the sale is greater than 100
pounds - senior person discount of 5
- Sunday sale for specific items
- a combination of simple policies
22The Strategy (GoF) pattern
Problem How to design for varying, but related
algorithms or policies? How to design for the
ability to change these algorithms or
policies? Solution Define each
algorithm/policy/strategy in a separate class
with a common interface. Which GRASP pattern
does this remind you of? How can it be used to
address the need for complex pricing rules?
23Strategy (GoF) example
ltltinterfacegtgt ISalePriceStrategy getTotal(Sale)M
oney
PercDiscountSaleStrategy percentage getTotal(Sal
e)Money
AbsoluteDiscountPriceStrategy discountMoney
thresholdMoney getTotal(Sale)Money
24The Composite (GoF) pattern
Problem How to treat a group or composition
structure of objects the same way
(polymorphically) as a non-composite atomic
object? Solution Define classes for composite
and atomic objects so that they implement the
same interface. How can this pattern be used to
address the need for complex pricing rules?
25Composite (GoF) pattern example
ltltinterfacegtgt ISalePriceStrategy getTotal(Sale)M
oney
pricingStrategies
1..
CompositePriceStrategy add(ISalePriceStrategy) g
etTotal(Sale)Money
PercDiscSaleStrategy percentage getTotal(Sale)M
oney
AbsoluteDiscount PriceStrategy discountMoney
thresholdMoney getTotal(Sale)Money
Composite1Strategy getTotal(Sale)Money
Composite2Strategy getTotal(Sale)Money
26Composite (GoF) pattern example (cont.)
public abstract class CompositePriceStrategy
implements ISalePriceStrategy protected List
pricingStrategies new ArrayList() public add
(ISalePricingStrategy s) pricingStrategies.add
(s). public abstract Money
getTotal ( Sale sale) //end of class public
class Composite1Strategy extends
CompositePriceStrategy public Money
getTotal(Sale sale) Money lowestTotal new
Money (Integer.MAX_VALUE) for (Iterator i
pricingStrategies.iterator() i.hasNext()) I
SalePricingStrategy strategy(ISalePricingStrategy
)i.next() Money total strategy.getTotal(sal
e) lowestTotal total.min(lowestTotal)
return lowestTotal
27Pluggable business rules
- We would like to customize the behavior of the
system based on a set of business rules - If a sale is paid by a gift certificate, the sale
must contain only one item. Subsequent items must
be invalidated. - If the sale is paid by a gift certificate, change
is given only by means of another gift
certificate, but not in cash. - A new sale may be created for charitable
donation, only if the currently logged in
cashier is a manager. - Goal
- Implement these rules with as low impact as
possible on the existing code - Keep coupling low
28The Facade (GoF) pattern
Problem A common, unified interface to a
disparate set of implementations or interfaces is
required. There may be undesirable coupling to
many things in the subsystem, or the
implementation of the subsystem may change. What
to do? Solution Define a single point of
contact to the subsystem a facade object that
wraps the subsystem. This facade object presents
a single unified interface and is responsible for
collaborating with the subsystem
components. How can this pattern be used to
address the need for pluggable business rules?
29Facade (GoF) pattern example
public class Sale public void makeLineItem
(ProductSpec spec, int quantity) //call to
the Facade if ( RuleEngineFacade.getInstance().i
sInvalid ( sli,this ) ) return lineItems.add
(sli)
1
RuleEngineFacade InstanceRuleEngineFacade get
Instance()RuleEngineFacade isInvalid(SalesLineIte
m, Sale) isInvalid(Payment, Sale)
30Summary of lecture 8
- We elaborated on the principles of polymorphism,
indirection, pure fabrication, adapter, factory
etc. used during software design. - We used examples to discover benefits of design
patterns - reusable code
- programs easy to understand and maintain
- low impact of requirements changes
- We introduced three new GoF design patterns i)
strategy, ii) composite and iii) facade - We applied these patterns in order to provide
good designs for the following requirements - complex pricing rules (strategy and composite)
- pluggable business rules (facade)