Section 3 (13-18)

Download Report

Transcript Section 3 (13-18)

Section 3
Payroll Case Study
The Payroll System – Initial Requirement
Employee
Database
correct amount
(minus deductions)
Payroll System
on time
method specified
Payroll Initial Spec




Pay Type:
 Hourly. Some employees work by the hour. Paid hourly rate. Submit
time cards. Paid overtime @ 1.5 times rate. Paid every Friday.
 Salary. Some employees salaried. Paid last working day of month.
 Commission. Some employees also paid commission based on
sales. Submit sales amounts. Paid every Friday.
 Commission rate, salary, hourly rate are part of employee record.
Pay Method.
 Employees can choose for paychecks to be mailed, held for pickup,
or directly deposited to bank account.
Deductions.
 Some employees belong to the union. Employee record has field for
weekly dues rate. Must be deducted from pay. Union may assess
service charges from time to time. These are submitted by union
on weekly basis and must be deducted from appropriate
employee’s next pay amount.
Other requirements.
 Payroll application will run once each working day and pay the
appropriate employees on that day. The system will be told to
what date the employees are to be paid, so it will calculate
payments from the last time the employee was paid up to the
specified date.
Chapter 18
Payroll Case Study
How to begin?
Obviously a database is involved. Should we generate
a database schema and some queries?




Common – but this generates an application for
which the database is the central concern.
Databases are implementation details!
Considering the database should be deferred as
long as possible.
Abstraction: the amplification of the essential and
the elimination of the irrelevant. The database is
irrelevant at this stage; it is merely a technique
used for storing and accessing data, nothing more.
Analysis by Use Cases



Start by considering the behavior of the system.
Use case: similar to a story, but elaborated with
more detail
Stories for this iteration:
1. Add a new employee
2. Delete an employee
3. Post a time card
4. Post a sales receipt
5. Post a union service charge
6. Change employee details (e.g., hourly rate,
dues rate)
7. Run the payroll for today
Use Case 1: Add New Employee
A new employee is added by the receipt of an AddEmp transaction.
This transaction contains the employee’s name, address, and
assigned employee number. The transaction has 3 forms:
AddEmp <EmpId> “<name>” “<address>” H <hourly-rate>
AddEmp <EmpId> “<name>” “<address>” S <monthly-salary>
AddEmp <EmpId> “<name>” “<address>” C <monthly-salary> <comm-rate>
The employee record is created with fields assigned appropriately.
Alternative 1:
An error in the transaction structure.
If the transaction structure is inappropriate, it is printed out in an
error message and no action is taken.
Thinking about the design…

Use case 1 hints at an abstraction:
AddEmployee
Transaction
- Name
- EmployeeId
- Address
AddHourly
Employee
Transaction
AddSalaried
Employee
Transaction
We’ll look at the COMMAND
pattern for this abstract base
class…
AddCommissioned
Employee
Transaction
SRP adhered to: each responsibility in its own class
Don’t get sucked into the database yet!!
OK to think about objects…
Employee
Hourly
Employee
Salaried
Employee
Commissioned
Employee
Possible Employee class hierarchy
Use Case 2: Delete an Employee
Employees are deleted when a DelEmp transaction is received.
The form of this transaction is as follows:
DelEmp <EmpId>
When this transaction is received, the appropriate employee
record is deleted.
Alternative 1:
Invalid or unknown EmpID
If the <EmpID> field is not structured correctly, or it does not
refer to a valid employee record, then the transaction is
printed with an error message and no other action is taken.
No design insights from this use case, so let’s move on…
Use Case 3: Post Time Card
On receiving a TimeCard transaction, the system will create a
time-card record and associate it with the appropriate
employee record.
TimeCard <EmpId> <date> <hours>
Alternative 1:
The selected employee is not hourly
The system will print an appropriate error message and take no
further action
Alternative 2:
An error in the transaction structure
The system will print an appropriate error message and take no
further action
Design insights…
Some transactions apply only to
certain kinds of employees –
strengthen case for different classes.
 Indicates association between time
cards and hourly employees.

Hourly
Employee
0..*
TimeCard
remember aggregation?
Could also be composition, if TimeCard objects
are destroyed when Employee is destroyed
Use Case 4: Posting a Sales Receipt
Upon receiving the SalesReceipt transaction, the system will
create a new sales-receipt record and associate it with the
appropriate commissioned employee.
SalesReceipt <EmpId> <date> <amount>
Alternative 1:
The selected employee is not commissioned.
The system will print an appropriate error message and take no
further action.
Alternative 2:
An error in the transaction structure.
The system will print an appropriate error message and take no
further action.
Design insights…

This is very similar to use case 3. It
implies the following structure:
Commissioned
Employee
0..*
Sales Receipt
Use Case 5: Post Union Service Charge
Upon receiving this transaction, the system will create
a service-charge record and associate it with the
appropriate union member.
ServiceCharge <memberID> <amount>
Alternative 1:
Poorly formed transaction
If the transaction is not well formed or if the <memberID>
does not refer to an existing union member, then the
transaction is printed with an appropriate error message.
Design Insights…

Union members are not accessed through
employee IDs. The union maintains its own
numbering scheme. The system must be able to
associate union members and employees. Could
be done various ways, so postpone decision until
later. Just show association for now.
UnionMember
0..*
ServiceCharge
Example of how
this differs from
waterfall…
Use Case 6: Change Employee Details
Upon receiving this transaction the system will alter one of the details of the
appropriate employee record. Several possible variations:
ChgEmp
ChgEmp
ChgEmp
ChgEmp
ChgEmp
ChgEmp
ChgEmp
ChgEmp
ChgEmp
ChgEmp
<EmpID>
<EmpID>
<EmpID>
<EmpID>
<EmpID>
<EmpID>
<EmpID>
<EmpID>
<EmpID>
<EmpID>
Name <name>
Address <address>
Hourly <hourlyRate> // Change to hourly
Salaried <salary> // Change to salary
Commissioned <salary> <rate>
Hold
Direct <bank> <account>
Mail <address>
Member <memberID> Dues <rate>
NoMember
Alternative 1:
An error in the transaction structure.
If the transaction structure is inappropriate, or <EmpID> does not refer to a real
employee, or <memberID> already refers to a member, then print a suitable
error and take no further action
Design insights…
Use case has shown all aspects of
employee that are changeable.
 Note that employees can change from
hourly to salaried and vice versa.
Initial class design may not be
appropriate. Use STRATEGY pattern
instead (see class diagram next slide)
 Use the NULL OBJECT pattern for union
membership.

Updated class design
<<interface>>
Payment
Method
Employee
<<interface>>
Affiliation
Payment
Classification*
NoAffiliation**
HoldMethod
MailMethod
- Address
DirectMethod
- Bank
- Account
UnionAffiliation
- Dues
Salaried
Classification
- Salary
Hourly
Classification
- HourlyRate
0..*
ServiceCharge
Commissioned
Classification
- CommissionRate
- Salary
0..*
TimeCard
0..*
SalesReceipt
*strategy class
** NULL object pattern
Use Case 7: Run Payroll for Today
Upon receiving the Payday transaction, the system
finds all those employees that should be paid on
the specified date. The system then determines
how much they are owed and pays them according
to their selected payment method.
Payday <date>
Finding Abstractions


Must hunt for abstractions, because often
not stated or even alluded to by application
requirements.
Example:
 Some
 Some
 Some


employees work by the hour
employees are paid a flat salary
employees are paid a commission
Generalization: All employees are paid, but
they are paid by different schemes.
Abstraction: All employees are paid. Lead
to PaymentClassification.
Another abstraction

Scheduling the payroll:







They are paid every Friday
They are paid on last working day of month
They are paid every other Friday
Generality: All employees are paid according to
some schedule.
Abstraction: schedule.
Use cases link schedule to payment classification.
BUT, it seems possible that this could change (e.g.,
employees in different departments might be paid
on different days). Also, if linked then a change in
payment classification would require testing the
schedule as well.
Schedule abstraction not included in original design
Chapter 13
COMMAND and
ACTIVE OBJECT
COMMAND: simple + elegant
<<interface>>
Command
+ do()
public interface Command
{
public void do();
}
Elevates the role of a function to the level of a class. Blasphemy!
But interesting designs can unfold…
Simple Commands
do()
<<interface>>
Command
+ do()
do()
RelayOn
Command
MotorOn
Command
RelayOff
Command
ClutchOn
Command
MotorOff
Command
init
ClutchOnCommand
MotorOnCommand
ClutchOff
Command
Embedded real-time software for a photocopier. Pass Command() objects around
system and call do() without knowing which type of Command they represent.
(next slide…)
Copier example, continued
Sensor
Command
Example: sensor needs to engage clutch at point in paper path. Bind
appropriate ClutchOnCommand to object.
Sensor detects event, calls do().
Complexity of determining which relays to connect to which sensors is now
handled by an initialization function.
Transactions: a type of do()


Maintain database of employees. Number of operations that users can
apply (add, delete, modify).
Command object collects unvalidated data, implements validation
methods, executes transactions.
<<interface>>
Transaction
+ validate()
+ execute()
Employee
- name
- address
<<interface>>
PayClassification
+ CalculatePay()
Commissioned
Classification
- basePay
- commissionRate
0..*
SalesReceipt
- date
- amount
Salaried
Classification
- monthlyPay
Hourly
Classification
- hourlyRate
0..*
TimeCard
- date
- amount
AddEmployee
Transaction
- name
- address
+ validate()
+ execute()
<<interface>>
Pay
Classification
+ CalculatePay()
Command Benefits

Physical and Temporal Decoupling
 Decouple code that procures data from user (probably a
dialog in some GUI) from code that validates and from
business objects. If coupled, validation code couldn’t be
used other places. Code that manipulates database
should also be separate.
 Temporal decoupling. Transaction data can be collected
and processed later. For example, maybe changes to
database should only occur in the evening.

Undo

Easy to add an undo() method to the COMMAND pattern.
Can reverse the effects of a do().
ACTIVE OBJECT
Old technique for implementing
multiple threads of control.
 Not covered – threads covered in OS
and PL

Chapter 14
TEMPLATE METHOD &
STRATEGY
Inheritance vs. Delegation
Inheritance





Brave new world in early 90s
Could program by difference – find class
that did something almost useful to us,
create subclass and change only what didn’t
work in new domain
BUT, easy to overuse inheritance
New advice: “Favor object composition over
class inheritance”
Need to carefully consider, use inheritance,
composition and delegation appropriately
TEMPLATE METHOD - inheritance
Common main-loop structure:
Initialize();
while (!done()) // main loop
{
Idle(); // do something useful
}
Cleanup();
Use the pattern to create abstract base classes that
can work on various types of objects.
Example: Bubble Sorter

First, we have a sort that knows how to work with
integers:
public class BubbleSorter
{
static int operations = 0;
public static int sort(int [] array)
{
operations = 0;
if (array.length <= 1)
return operations;
for (int nextToLast = array.length-2;
nextToLast >= 0; nextToLast--)
for (int index = 0;
index <= nextToLast; index++)
compareAndSwap(array, index);
return operations;
}
private static void swap
(int[] array, int index)
{
int temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
}
private static void compareAndSwap
(int[] array, int index)
{
if (array[index]> array[index+1])
swap(array, index);
operations++;
}
}
Bubble Sorter, continued

Can create abstract class instead:
public abstract class BubbleSorter
{
private int operations = 0;
protected int length = 0;
protected int doSort()
operations = 0;
if (length <= 1)
return operations;
{
for (int nextToLast = length-2;
nextToLast >= 0;
nextToLast--)
for (int index = 0;
index <= nextToLast; index++)
{
if (outOfOrder(index))
swap(index);
operations++;
}
return operations;
}
protected abstract void swap(int index);
protected abstract boolean outOfOrder
(int index);
}
BubbleSorter
{abstract}
# outOfOrder
# swap
IntBubble
Sorter
DoubleBubble
Sorter
Bubble Sorter, continued

Implement methods for Int and Double:
public class IntBubbleSorter
extends BubbleSorter
{
private int[] array = null;
public int sort(int [] theArray)
{
array = theArray;
length = array.length;
return doSort();
}
protected void swap(int index)
{
int temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
}
protected boolean outOfOrder
(int idx)
{
return (array[idx]> array[idx+1]);
}
}
public class DoubleBubbleSorter
extends BubbleSorter
{
private double[] array = null;
public int sort(double [] theArray)
{
array = theArray;
length = array.length;
return doSort();
}
protected void swap(int index)
{
double temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
}
protected boolean outOfOrder
(int idx)
{
return (array[idx]> array[idx+1]);
}
}
STRATEGY pattern




Another way to solve the problem of inverting the
dependencies
Instead of putting generic application algorithm in an abstract
class, place it in a concrete class named ApplicationRunner
Abstract methods that must be called are defined in the
Application interface
ftocStrategy is derived from this interface. An object of this
class can be passed to ApplicationRunner
Application
Runner
+ run
concrete class
<<interface>>
Application
+ init
+ idle
+ cleanup
+ done: boolean
ftoc = fahrenheit to celcius
ftocStrategy
concrete class
Strategy code
public class ApplicationRunner
{
private Application itsApplication = null;
can pass in ftocStrategy, etc.
public ApplicationRunner(Application app)
{
itsApplication = app;
}
public void run()
{
itsApplication.init();
while (!itsApplication.done())
itsApplication.idle();
public interface Application
itsApplication.cleanup();
{
}
public void init();
}
public void idle();
calls methods
public void cleanup();
from interface
public boolean done();
}
Strategy code, continued
import java.io.*;
public class ftocStrategy implements Application
{
private InputStreamReader isr;
private BufferedReader br;
private boolean isDone = false;
public static void main(String[] args) throws Exception
{
(new ApplicationRunner(new ftocStrategy())).run();
}
public void init()
{
isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
}
// More code in textbook
Strategy pros and cons


Strategy involves more classes
Delegation pointer in ApplicationRunner
incurs some overhead
BUT

Easy to reuse ApplicationRunner by passing
in different implementations of Application
Another example – sorting again
public class BubbleSorter
{
private int operations = 0;
private int length = 0;
private SortHandle itsSortHandle = null;
public BubbleSorter(SortHandle handle) {
itsSortHandle = handle;
}
public int sort(Object array) {
itsSortHandle.setArray(array);
length = itsSortHandle.length();
operations = 0;
if (length <= 1)
return operations;
for (int nextToLast = length-2;
nextToLast >= 0; nextToLast--)
for (int index = 0;
index <= nextToLast; index++)
{
if (itsSortHandle.outOfOrder(index))
itsSortHandle.swap(index);
operations++;
}
return operations;
}}
public interface SortHandle
{
public void swap(int index);
public boolean outOfOrder(int index);
public int length();
public void setArray(Object array);
}
Sorting continued
public class IntSortHandle implements SortHandle
{
private int[] array = null;
public void swap(int index) {
int temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
}
public void setArray(Object array) {
this.array = (int[])array;
}
public int length() {
return array.length;
}
public boolean outOfOrder(int index) {
return (array[index] > array[index+1]);
}
}
Notice:
• IntSortHandle knows nothing of
BubbleSorter
• IntSortHandle could therefore be
used with other sorts – e.g.,
QuickBubbleSorter that stops
when the array is in order (code
in text)
• Strategy allows the details to be
reused independently of the
high-level algorithm!
More comparison sorts
covered in CSCI406
Chapter 15
FAÇADE and
MEDIATOR
Policy patterns
Both Façade and Mediator impose
some kind of policy on another group
of objects
 Façade imposes policy from above
 Mediator imposes policy from below
 Façade is visible and constraining
 Mediator is invisible and enabling

Facade

Used to provide a simple, specific interface for a group of
objects that has a complex, general interface
Product-specific sql interface; complex but
public class DB {
shields users from knowing sql details
private static Connection con;
public static void init() throws Exception {…}
public static void store(ProductData pd) throws Exception {…} // Products
private static PreparedStatement buildProductInsertionStatement
ProductData pd) throws SQLException
public static ProductData getProductData(String sku) throws Exception
private static PreparedStatement buildProductQueryStatement(String sku)
private static ProductData extractProductDataFromResultSet(ResultSet rs)
public static void store(ItemData id) throws Exception {…} // Items
private static PreparedStatement buildItemInsersionStatement(ItemData id)
public static ItemData[] getItemsForOrder(int orderId) throws Exception
private static PreparedStatement buildItemsForOrderQueryStatement
(int orderId) throws SQLException
private static ItemData[] extractItemDataFromResultSet(ResultSet rs)
public static OrderData newOrder(String customerId) throws Exception
private static int getMaxOrderId() throws SQLException // Orders
public static OrderData getOrderData(int orderId) throws SQLException
private static void executeStatement(PreparedStatement s)
public static void close() throws Exception {…}
public static void clear() throws Exception {…}
}
DB Facade
ProductData
DB
+ store(ProductData)
+ getProductData(sku)
+ deleteProductData(sku)
…
Application
java.sql
Connection
ResultSet
Statement
Prepared
Statement
DriverManager
SQLException
All database calls must go through DB. Direct calls to java.sql violate convention.
Policy is visible – application sees interface it provides. Valid operations are all
provided. Simplifies – application programmers don’t all need to learn sql
interface, just use DB.
Mediator

Imposes policy in hidden, constrained way.
QuickEntryMediator selects the first entry in a list
that matches the current prefix in a JTextField.
State
JList
JTextField
QuickEnty
Mediator
<<anonymous>>
Document
Listener
CO
ALABAMA
ALASKA
ARIZONA
ARKANSAS
CALIFORNIA
COLORADO
CONNECTICUT
DELAWARE
Hidden: Users of JList and JTextField have no idea that this Mediator exists.
Mediator QuickEntry Example
List of states
public class QuickEntryMediator { State
public QuickEntryMediator(JTextField t, JList l) {
itsTextField = t;
itsList = l;
responds to JTextField events, shown
on next slide
itsTextField.getDocument().addDocumentListener(
new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
textFieldChanged();
}
public void insertUpdate(DocumentEvent e) {
textFieldChanged();
}
public void removeUpdate(DocumentEvent e) {
textFieldChanged();
}
} // new DocumentListener
); // addDocumentListener
} // QuickEntryMediator()
Mediator QuickEntry continued
private void textFieldChanged() {
String prefix = itsTextField.getText();
if (prefix.length() == 0) { // clear selection if user clears field
itsList.clearSelection();
return;
}
ListModel m = itsList.getModel(); // finds first item that matches
boolean found = false;
// text typed so far
for (int i = 0; found == false && i < m.getSize(); i++) {
Object o = m.getElementAt(i);
String s = o.toString();
if (s.startsWith(prefix)) {
itsList.setSelectedValue(o, true);
found = true; // found is true if any items in list match prefix
}
}
if (!found) { // clear if nothing matches text entry
itsList.clearSelection();
}
} // textFieldChanged
private JTextField itsTextField;
private JList itsList;
} // class QuickEntryMediator
Chapter 16
SINGLETON and
MONOSTATE
Sometimes you only need one…


Normally there is a one-to-many relationship
between classes and objects
Some classes should only have one instance







factories
managers (e.g., print, other resources)
root of application
If more than one, logic errors may occur
May be able to just create one when application
starts and be done with it application root
Sometimes it is important to enforce singularity
Two patterns to do that: singleton & monostate
SINGLETON pattern




Simple to implement
Access through public method, e.g., Instance
Instance always returns same object
No public constructors, so can’t create a second
object of same type
must be public class Singleton{
private static Singleton theInstance = null;
private -private Singleton() {}
why?
public static Singleton getInstance()
{
if (theInstance == null)
theInstance = new Singleton();
return theInstance;
}
must be
static -why?
}
Testing the SINGLETON
@Test
public void testCreateSingleton()
{
Singleton s = Singleton.Instance();
Singleton s2 = Singleton.Instance();
assertSame(s, s2);
}
@Test (expected=Exception.class)
public void testNoPublicConstructors()
{
Class singleton = Class.forName("Singleton");
Constructor[] constructors = singleton.getConstructors();
assertEquals("Singleton has public constructors.",
0, constructors.length);
}
Pros and Cons
Benefits of SINGLETON:
 Cross platform. Can extend to
work across many JVMs and
computers.
 Applicable to any class. Just
make constructors private and
add static functions and
variable.
 Can derive a singleton from
some other class. Given a
class, can create a subclass
that is a SINGLETON
 Lazy evaluation. If SINGLETON
is never used, it is never
created
Costs of SINGLETON:
 Destruction undefined. If NULL
out theInstance, other modules
may still hold reference. So??
 Not inherited. Class derived
from SINGLETON is not a
SINGLETON. Would need to add
function to subclass.
 Efficiency. Each call to
Instance invokes an if important?
statement, most often useless.
 Nontransparent. Users of
SINGLETON must know they
are using SINGLETON because
they must invoke Instance
method.
Example related to Pros/Cons
public class MyClass { // CAN MAKE ANY CLASS A SINGLETON
private static MyClass instance;
private MyClass() { // WHATEVER INITIALIZATION NEEDED }
public static MyClass getInstance() {
if (instance == null) // LAZY EVALUATION
instance = new MyClass();
return instance;
} }
public class AnotherClass extends MyClass {
public AnotherClass() { … } // SINGLETON NOT INHERITED
}
… // in some other file
MyClass myClass = MyClass.getInstance(); // NON-TRANSPARENT
SINGLETON Example
No IF statement,
using Java initialization
private static UserDatabase theInstance =
new UserDatabaseSource();
public static UserDatabase instance() {
return theInstance;
}
private UserDatabaseSource(){}
public User readUser(String userName){
// Some Implementation
return null; // just to make it compile.
}
public void writeUser(User user){
// Some Implementation
}
Common usage, ensures all database access via single instance of Façade.
Two possible approaches


MONOSTATE enforces the behavior of
singularity
SINGLETON enforces the structure
MONOSTATE

Have multiple instances of Monostate, all act as one
import junit.framework.*;
public class TestMonostate extends TestCase
{
public TestMonostate(String name) {
Two instances of Monostate
super(name);
behave as though they were one
}
public void testInstancesBehaveAsOne() {
Monostate m1 = new Monostate();
public void testInstance() {
Monostate m2 = new Monostate();
Monostate m = new Monostate();
for (int x = 0; x<10; x++) {
for (int x = 0; x<10; x++) {
m.setX(x);
m1.setX(x);
assertEquals(x, m.getX());
assertEquals(x, m2.getX());
}
}
}
}
}
MONOSTATE continued

How can that be?
 Objects
share same variables – because they are
static
 BUT, none of the methods are static so they are
accessed via actual instances of class objects
public class Monostate
{
private static int itsX = 0;
public Monostate() {}
not static
}
public void setX(int x) {
itsX = x;
}
public int getX() {
return itsX;
}
Monostate Turnstile Example




Turnstile begins in locked state. Returns to locked
state after people pass.
If coin is deposited, transition to Unlocked state,
unlock gate, reset alarm, deposit coins in its bin
Abnormal condition 1: user deposits two coins
before passing through. Coins are refunded, gate
remains unlocked.
Abnormal condition 2: user passes through without
paying. Alarm sounds, gate locked.
Pass /
Alarm
Coin / Unlock, AlarmOff, Deposit
Turnstile finite
state machine
Locked
Pass / Lock
Unlocked
Coin / Refund
Turnstile Code
public class Turnstyle
{
private static boolean isLocked;
private static boolean isAlarming;
private static int itsCoins;
private static int itsRefunds;
protected final static Turnstyle LOCKED = new Locked();
protected final static Turnstyle UNLOCKED = new Unlocked();
protected static Turnstyle itsState = LOCKED;
public void reset() {
lock(true);
alarm(false);
itsCoins = 0;
itsRefunds = 0;
itsState = LOCKED;
}
Turnstile code, continued
class Locked extends Turnstyle
{
public void coin() {
itsState = UNLOCKED;
lock(false);
alarm(false);
deposit();
}
public void pass() {
alarm(true);
}
class Unlocked extends Turnstyle
{
public void coin() {
refund();
}
public void pass() {
lock(true);
itsState = LOCKED;
}
}
}
The event functions (coin and pass) are delegated to two derivatives of Turnstyle
(Locked and Unlocked) that represent the states of the finite-state machine.
Derivatives are automatically MONOSTATE. Since these are all MONOSTATE,
Locked and Unlocked are not really separate objects, so no violation of OO
principles.
NOTE: Controlling more than one turnstile would require significant refactoring.
Turnstile code, continued

See rest of code + JUnit tests in
textbook
Benefits of MONOSTATE




Transparency. Users do not need to know
that object is MONOSTATE, because usage
is same as for a regular object
Derivability. Derivatives of MONOSTATE are
MONOSTATE – they share the same
variables
Polymorphism. Since methods are not
static, they can be overridden. Different
behavior, same set of static variables.
Well-defined creation and destruction.
Static variables in MONOSTATE have welldefined creation and destruction times.
Costs of MONOSTATE




No conversion. Normal class cannot be
converted to MONOSTATE through
derivation.
Efficiency. May have many creations and
destructions because MONOSTATE is a real
object. These are costly operations.
Presence. Variables of MONOSTATE take up
space, even if MONOSTATE never used.
Platform local. You can’t make a
MONOSTATE work across several JVM
instances or across several platforms.
Which option to use?
SINGLETON is best when you have an
existing class that you want to
constrain through derivation, and you
don’t mind requiring users to call
instance()
 MONOSTATE is best when you want
singular nature of class to be
transparent or when you want to
employ polymorphic derivatives

Chapter 17
NULL OBJECT
Common “null” code technique
Rely on short-circuit evaluation
 Error-prone (may forget)

Employee e = DB.getEmployee(“Bob”);
if (e != null && e.isTimeToPay(today))
e.pay();

Try/catch can be even worse
An alternative: NULL OBJECT



Employee interface has two implementations
Normal implementation works as usual
NULL implementation methods do “nothing”
DB
<<creates>>
<<interface>>
Employee
NullEmployee
Employee
Implementation
<<creates>>
Employee e = DB.getEmployee(“Bob”);
if (e.isTimeToPay(today))
e.pay();
NullEmployee is returned
if “Bob” object doesn’t exist
isTimeToPay will return false
NULL Object Code
public class DB{
public static Employee getEmployee(String name){
return Employee.NULL;
}
}
public interface Employee{
public boolean isTimeToPay(Date payDate);
public void pay();
// NULL EMPLOYEE METHODS
public static final Employee NULL = new Employee() {
public boolean isTimeToPay(Date payDate) {
return false;
// NOTE: class definition
}
public void pay()
{
}
Anonymous inner class ensures
};
single instance
}
NULL Object Code continued
public void testNull() throws Exception
{
Employee e = DB.getEmployee("Bob");
if (e.isTimeToPay(new Date()))
fail();
assertEquals(Employee.NULL, e);
}