Singleton Design Pattern
EECS3311 A & E: Software Design Fall 2020
CHEN-WEI WANG
Expanded Class: Modelling
● We may want to have objects which are: ○ Integral parts of some other objects
○ Not shared among objects
e.g., Each workstation has its own CPU, monitor, and keyword. All workstations share the same network.
3 of 23
Learning Objectives
Upon completing this lecture, you are expected to understand: 1. Modeling Concept of Expanded Types (Compositions)
2. Once Routines in Eiffel vs. Static Methods in Java
3. Export Status
4. Sharing via Inheritance (w.r.t. SCP and Cohesion) 5. Singleton Design Pattern
2 of 23
Expanded Class: Programming (2)
Alternatively:
class KEYBOARD … end class CPU … end class MONITOR … end class NETWORK … end class WORKSTATION
k: expanded KEYBOARD c: expanded CPU
m: expanded MONITOR n: NETWORK
end
expanded class KEYBOARD … end expanded class CPU … end expanded class MONITOR … end class NETWORK … end
class WORKSTATION k: KEYBOARD
c: CPU
m: MONITOR
n: NETWORK end
4 of 23
Expanded Class: Programming (3)
1 2
test_expanded
local
eb1, eb2: B do
check eb1.i = 0 and eb2.i = 0 end check eb1 = eb2 end
eb2.change_i (15)
check eb1.i = 0 and eb2.i = 15 end check eb1 /= eb2 end
eb1 := eb2
check eb1.i = 15 and eb2.i = 15 end eb1.change_i (10)
check eb1.i = 10 and eb2.i = 15 end check eb1 /= eb2 end
end
expanded class 3 B4
feature 5
change_i (ni: INTEGER) 6
do 7
i := ni 8
end 9
feature 10
i: INTEGER 11 end 12
13 14 15
● L5: object of expanded type is automatically initialized.
● L10,L12,L13: no sharing among objects of expanded type.
● L6,L9,L14: = compares contents between expanded objects. 5 of 23
Reference vs. Expanded (2)
Problem: Every published book has an author. Every author may publish more than one books. Should the author field of a book reference-typed or expanded-typed?
reference-typed author expanded-typed author
Hyperlinked author page Physical printed copies
7 of 23
Reference vs. Expanded (1)
● Every entity must be declared to be of a certain type (based on a class).
[x ∼ y]
● Every type is either referenced or expanded.
● In reference types:
6 of 23
○ y denotes a reference to some object
○ x := y attaches x to same object as does y ○ x = y compares references
● In expanded types:
○ y denotes some object (of expanded type)
○ x := y copies contents of y into x ○ x = y compares contents
Singleton Pattern: Motivation
Consider two problems:
1. Bank accounts share a set of data.
e.g., interest and exchange rates, minimum and maximum
balance, etc.
2. Processes are regulated to access some shared, limited
resources. e.g., printers
8 of 23
Shared Data via Inheritance
Descendant:
class DEPOSIT inherit SHARED DATA — ‘maximum_balance’ relevant
end
class WITHDRAW inherit SHARED DATA — ‘minimum_balance’ relevant
end
class INT_TRANSFER inherit SHARED DATA — ‘exchange_rate’ relevant
end
class ACCOUNT inherit SHARED DATA feature
— ‘interest_rate’ relevant
deposits: DEPOSIT_LIST
withdraws: WITHDRAW_LIST end
9 of 23
Ancestor:
class
SHARED DATA feature
interest_rate: REAL exchange_rate: REAL minimum_balance: INTEGER maximum_balance: INTEGER …
end
Problems?
Sharing Data via Inheritance: Limitation
● Each descendant instance at runtime owns a separate copy of the shared data.
● This makes inheritance not an appropriate solution for both problems:
○ What if the interest rate changes? Apply the change to all instantiated account objects?
○ An update to the global lock must be observable by all regulated processes.
Solution:
○ Separate notions of data and its shared access in two separate classes.
○ Encapsulate the shared access itself in a separate class. 11 of 23
Sharing Data via Inheritance: Architecture
○ Irreverent features are inherited.
⇒ Descendants’ cohesion is broken.
○ Same set of data is duplicated as instances are created. 10 of 23⇒ Updates on these data may result in inconsistency .
Introducing the Once Routine in Eiffel (1.1)
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
L9 & L10 executed only once for initialization.
L15 & L16 executed whenever the feature is called. 12 of 23
class A
create make
feature — Constructor
make do end feature — Query
new_once_array (s: STRING): ARRAY[STRING] — A once query that returns an array.
once
create {ARRAY[STRING]} Result.make_empty
Result.force (s, Result.count + 1) end
new_array (s: STRING): ARRAY[STRING]
— An ordinary query that returns an array.
do
create {ARRAY[STRING]} Result.make_empty
Result.force (s, Result.count + 1) end
end
Introducing the Once Routine in Eiffel (1.2)
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
test_query: BOOLEAN local
a: A
arr1, arr2: ARRAY[STRING] do
create a.make
arr1 := a.new_array (“Alan”)
Result := arr1.count = 1 and arr1[1] ∼ “Alan” check Result end
arr2 := a.new_array (“Mark”)
Result := arr2.count = 1 and arr2[1] ∼ “Mark” check Result end
Result := not (arr1 = arr2)
check Result end
end
13 of 23
Introducing the Once Routine in Eiffel (2)
● The ordinary do … end is replaced by once … end.
● The first time the once routine r is called by some client, it
executes the body of computations and returns the computed
result.
● From then on, the computed result is “cached”.
● In every subsequent call to r, possibly by different clients, the
body of r is not executed at all; instead, it just returns the
“cached” result, which was computed in the very first call.
● How does this help us?
Cache the reference to the same shared object ! 15 of 23
r (…): T once
…
end
— Some computations on Result
Introducing the Once Routine in Eiffel (1.3)
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
test_once_query: BOOLEAN local
a: A
arr1, arr2: ARRAY[STRING] do
create a.make
arr1 := a.new_once_array (“Alan”)
Result := arr1.count = 1 and arr1[1] ∼ “Alan” check Result end
arr2 := a.new_once_array (“Mark”)
Result := arr2.count = 1 and arr2[1] ∼ “Alan” check Result end
Result := arr1 = arr2
check Result end
end
14 of 23
Approximating Once Routine in Java (1)
We may encode Eiffel once routines in Java:
Problem?
Multiple BankData objects may be created in Account, breaking the singleton!
class BankData { BankData() { }
double interestRate; void setIR(double r); …
}
class Account { BankData data; Account() {
data = BankDataAccess.getData(); }
}
class BankDataAccess {
static boolean initOnce; static BankData data; static BankData getData() {
if(!initOnce) {
data = new BankData(); initOnce = true;
}
return data; }
}
Account() {
data = new BankData();
}
16 of 23
Approximating Once Routine in Java (2)
We may encode Eiffel once routines in Java:
class BankData {
private BankData() { } double interestRate;
void setIR(double r); static boolean initOnce; static BankData data; static BankData getData() {
if(!initOnce) {
data = new BankData(); initOnce = true;
}
return data; }
}
Problem?
Loss of Cohesion: Data and Access to Data are two separate concerns, so should be decoupled into two different classes!
17 of 23
Singleton Pattern in Eiffel (2)
Supplier:
Client:
class
ACCOUNT
feature
data: BANK DATA make (…)
— Init. access to bank data.
local
data_access: BANK DATA ACCESS do
data := data_access.data
…
end end
class BANK DATA
create {BANK DATA ACCESS} make feature {BANK DATA ACCESS}
make do … end
feature — Data Attributes
interest_rate: REAL set_interest_rate (r: REAL) …
end
expanded class
BANK DATA ACCESS feature
data: BANK DATA
— The one and only access
once create Result.make end invariant data = data
Writing in client’s make feature does not compile. Why?
create data.make
19 of 23
Singleton Pattern in Eiffel (1)
Supplier: Client:
test: BOOLEAN local
access: DATA ACCESS
d1, d2: DATA do
d1 := access.data d2 := access.data Result := d1 = d2
and d1.v = 10 and d2.v = 10 check Result end
d1.change_v (15)
Result := d1 = d2
and d1.v = 15 and d2.v = 15 end
end
class DATA
create {DATA ACCESS} make feature {DATA ACCESS}
make do v := 10 end feature — Data Attributes
v: INTEGER
change_v (nv: INTEGER)
do v := nv end end
expanded class
DATA ACCESS feature
data: DATA
— The one and only access
once create Result.make end invariant data = data
create d1.make
18 of 23
Writing in test feature does not compile. Why?
Testing Singleton Pattern in Eiffel
20 of 23
test_bank_shared_data: BOOLEAN
— Test that a single data object is manipulated
local acc1, acc2: ACCOUNT do
comment(“t1: test that a single data object is shared”) create acc1.make (“Bill”)
create acc2.make (“Steve”)
Result := acc1.data = acc2.data
check Result end
Result := acc1.data ∼ acc2.data check Result end acc1.data.set_interest_rate (3.11) Result :=
acc1.data.interest_rate = acc2.data.interest_rate and acc1.data.interest_rate = 3.11
check Result end
acc2.data.set_interest_rate (2.98) Result :=
acc1.data.interest_rate = acc2.data.interest_rate and acc1.data.interest_rate = 2.98
end
Singleton Pattern: Architecture
clien_1
clien_2
clien_3
daa_acce+
daa_acce+
daa_acce+
pplier_of_hared_daa
DATA_ACCESS+
— Daa daa+:
— Refeece a haed daa bec
R.
haed_ace: =
daa+
DATA+
DATA_ACCESS — Cea Rec ae
DATA_ACCESS — Udae Rec ae+
— Iae a daa bec
— Daa
:
— A eae e c
— A eae cad
+ 1
+ 2
+ 3
Important Exercises: Instantiate this architecture to the problem of shared bank data.
Draw it in draw.io. 21 of 23
Index (1)
Learning Objectives
Expanded Class: Modelling
Expanded Class: Programming (2)
Expanded Class: Programming (3)
Reference vs. Expanded (1)
Reference vs. Expanded (2)
Singleton Pattern: Motivation
Shared Data via Inheritance
Sharing Data via Inheritance: Architecture
Sharing Data via Inheritance: Limitation
Introducing the Once Routine in Eiffel (1.1)
23 of 23
Beyond this lecture
The singleton pattern is instantiated in the ETF framework:
● ETF MODEL (shared data) ● ETF MODEL ACCESS (exclusive once access) ● ETF COMMAND and its effective descendants:
deferred class
ETF_COMMAND
feature — Attributes model: ETF_MODEL
feature {NONE} make(. . .)
local
ma: ETF_MODEL_ACCESS do
…
model := ma.m end
end
class
ETF_MOVE
inherit
ETF_MOVE_INTERFACE
— which inherits ETF_COMMAND feature — command
move(. . .) do
…
model.some_routine (…) …
end end
22 of 23
Index (2)
Introducing the Once Routine in Eiffel (1.2)
Introducing the Once Routine in Eiffel (1.3)
Introducing the Once Routine in Eiffel (2)
Approximating Once Routines in Java (1)
Approximating Once Routines in Java (2)
Singleton Pattern in Eiffel (1)
Singleton Pattern in Eiffel (2)
Testing Singleton Pattern in Eiffel
Singleton Pattern: Architecture
Beyond this lecture
24 of 23