James Coplien: Trygve - Oct 17, 2016
-
Upload
foo-cafe-copenhagen -
Category
Software
-
view
25 -
download
3
Transcript of James Coplien: Trygve - Oct 17, 2016
A taste of trygve
Jim Coplien Former C++ Programmer & Author
Gertrud & Cope Helsingør, Danmark
jcoplien@gmail
1 20161017FooCafé.key - 28 October 2016
Pointers…
• DCI documentation & downloads:
• http://fulloo.info
• trygve on GitHub:
• https://github.com/jcoplien/trygve
2 20161017FooCafé.key - 28 October 2016
My greatest contribution to computing is that I never invented a programming language.
– Jerry Weinberg
3 20161017FooCafé.key - 28 October 2016
In 1972, Kay coined the term: “Object-Oriented Programming”• In his 1972 paper the word “class” appears only once
• Objects: operational models, in the machine, to extend the capabilities of the human mind
• Classes came into Smalltalk ca. 1976 (from Simula 67)
• trygve: conceived to address the largest gaps between current OOP and the benefits of the original vision, through DCI
4 20161017FooCafé.key - 28 October 2016
G&C
We feel that a child is a "verb" rather than a "noun", an actor rather than an object; he is not a scaled-up pigeon or rat; he is trying to acquire a model of his surrounding environment in order to deal with it ... We would like to hook into his current modes of thought in order to influence him rather than just trying to replace his model with one of our own.
5-2 20161017FooCafé.key - 28 October 2016
G&C
We feel that a child is a "verb" rather than a "noun", an actor rather than an object; he is not a scaled-up pigeon or rat; he is trying to acquire a model of his surrounding environment in order to deal with it ... We would like to hook into his current modes of thought in order to influence him rather than just trying to replace his model with one of our own.
Alan Kay
5-3 20161017FooCafé.key - 28 October 2016
But we *do* design
• What kinds of tools / notations / models do you use?
7-2 20161017FooCafé.key - 28 October 2016
But we *do* design
• What kinds of tools / notations / models do you use?
• “… a modeling language is what the language designers omitted to include in the programming languages” — Reenskaug
7-3 20161017FooCafé.key - 28 October 2016
Java Programs as Modelspublic void setParentScope(final StaticScope scope) { parentScope_ = scope;}public List<ObjectDeclaration> objectDeclarations() { final List<ObjectDeclaration> retval = new ArrayList<ObjectDeclaration>(); for (final Map.Entry<String, ObjectDeclaration> objectDecl : objectDeclarationDictionary_.entrySet()) { retval.add(objectDecl.getValue()); } return retval;}public List<ClassDeclaration> classDeclarations() { final List<ClassDeclaration> retval = new ArrayList<ClassDeclaration>(); for (final Map.Entry<String, ClassDeclaration> classDecl : classDeclarationDictionary_.entrySet()) { retval.add(classDecl.getValue()); } return retval;}
Class (domain) structure
Use case structure
Roles (application)
8-1 20161017FooCafé.key - 28 October 2016
Java Programs as Modelspublic void setParentScope(final StaticScope scope) { parentScope_ = scope;}public List<ObjectDeclaration> objectDeclarations() { final List<ObjectDeclaration> retval = new ArrayList<ObjectDeclaration>(); for (final Map.Entry<String, ObjectDeclaration> objectDecl : objectDeclarationDictionary_.entrySet()) { retval.add(objectDecl.getValue()); } return retval;}public List<ClassDeclaration> classDeclarations() { final List<ClassDeclaration> retval = new ArrayList<ClassDeclaration>(); for (final Map.Entry<String, ClassDeclaration> classDecl : classDeclarationDictionary_.entrySet()) { retval.add(classDecl.getValue()); } return retval;}
• I can find only classes in Java code• A simplistic hierarchy• No objects• No networks of collaboration• No dynamics (because I can’t!!!
By design!!!)
Class (domain) structure
Use case structure
Roles (application)
8-2 20161017FooCafé.key - 28 October 2016
Class (domain) structure
trygve Programs as Models
Use case structure
Roles (application)
class Range { public Range(int a, int b) { … } public int start() const { return start_ } public int end() const { return end_ }}
class Scanner { }
context SpellCheck { role [] Words { public void check() { Words[lastIndex].review } public void review() public void reviewAWord() } role Document { public int len() { return length() } private String wordStartingAt(int start) { … } … }}
9-1 20161017FooCafé.key - 28 October 2016
Class (domain) structure
trygve Programs as Models
Use case structure
Roles (application)
DCI Conceptualization:• objects• the roles the objects play• the classes to which they belong, and• the operational models of their interaction
class Range { public Range(int a, int b) { … } public int start() const { return start_ } public int end() const { return end_ }}
class Scanner { }
context SpellCheck { role [] Words { public void check() { Words[lastIndex].review } public void review() public void reviewAWord() } role Document { public int len() { return length() } private String wordStartingAt(int start) { … } … }}
9-2 20161017FooCafé.key - 28 October 2016
Research Results• From Héctor Adrián Valdecantos, RIT
• “to state with high statistical significance that the context element in a DCI system plays an important role in code comprehension.”
• “the correctness analysis shows that in general programmers following the DCI-trygve approach perform better than programmers using the OO-java approach.“
10 20161017FooCafé.key - 28 October 2016
But couldn’t you just…Use multiple dispatch
That’s chooses one algorithm based on multiple types, rather than a sequencing of multiple algorithms based on multiple types
Use aspects The joinpoints are still dictated by the class structure
Use mix-ins Close, but how do they talk to each other?
Just use objects (e.g., self) A good start, but not enough
Use multi-paradigm design Undesired decoupling and lack of cohesion
11 20161017FooCafé.key - 28 October 2016
G&C
Action Between Objects
1st Qtr2nd Qtr3rd Qtr4th Qtr
V0
10
20
30
40
50
60
70
80
90
1st Qtr 2nd Qtr 3rd Qtr 4th Qtr
EastWestNorth
ModelView
Controller
Skip
12-1 20161017FooCafé.key - 28 October 2016
G&C
Action Between Objects
1st Qtr2nd Qtr3rd Qtr4th Qtr
V0
10
20
30
40
50
60
70
80
90
1st Qtr 2nd Qtr 3rd Qtr 4th Qtr
EastWestNorth
Skip
12-2 20161017FooCafé.key - 28 October 2016
G&C
Action Between Objects
1st Qtr2nd Qtr3rd Qtr4th Qtr
V0
10
20
30
40
50
60
70
80
90
1st Qtr 2nd Qtr 3rd Qtr 4th Qtr
EastWestNorth
The programmer must consider the
system:action between
objects
Skip
12-3 20161017FooCafé.key - 28 October 2016
G&C
Action Between Objects
1st Qtr2nd Qtr3rd Qtr4th Qtr
V0
10
20
30
40
50
60
70
80
90
1st Qtr 2nd Qtr 3rd Qtr 4th Qtr
EastWestNorth
13-1 20161017FooCafé.key - 28 October 2016
G&C
1st Qtr2nd Qtr3rd Qtr4th Qtr
V0
10
20
30
40
50
60
70
80
90
1st Qtr 2nd Qtr 3rd Qtr 4th Qtr
EastWestNorth
Programmers’ objects interactwith objects previously conceived by other programmers
13-2 20161017FooCafé.key - 28 October 2016
G&C
System Operations
While the specific interactions are
emergent, the form of the interactions
is designed.
15-2 20161017FooCafé.key - 28 October 2016
G&C
System Operations
This form lives within no single object or class.
15-3 20161017FooCafé.key - 28 October 2016
G&C
System Operations
Objects are an overly small concept,
conceived for the revenge of the nerds,
each owning their individual classes
15-4 20161017FooCafé.key - 28 October 2016
G&C
System Operations
context TripReservation
}
{role JourneyEnd { City city() { … } ….
}role JourneyStart { City city() { … } ….
}role RouteMap { Path pfinder() { … } …. }
16-3 20161017FooCafé.key - 28 October 2016
G&C
System Operations
context TripReservation
}
{role JourneyEnd { City city() { … } ….
}role JourneyStart { City city() { … } ….
}role RouteMap { Path pfinder() { … } …. }
16-4 20161017FooCafé.key - 28 October 2016
Teaching Actors their Scriptscontext TripReservation { role JourneyStart { … } role JourneyEnd { … } public TripReservation(Object jStart, Object jEnd){ JourneyStart = jStart; JourneyEnd = jEnd } }
17-1 20161017FooCafé.key - 28 October 2016
Teaching Actors their Scriptscontext TripReservation { role JourneyStart { … } role JourneyEnd { … } public TripReservation(Object jStart, Object jEnd){ JourneyStart = jStart; JourneyEnd = jEnd } }
17-2 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
foo
foo
foo
foo
bar
bar
bar
bar
bar
?
Skip
18-3 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
foo
foo
foo
foo bar
bar
bar
bar
sna
sna
snasna
sna
?
Skip
18-4 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
foo
foo
foo
foo bar
bar
bar
bar
sna
snasna
sna
Skip
18-5 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
foo
foo
foo
foo bar
bar
bar
bar
sna
snasna
sna
“Just trust the objects to do
the right thing and everything will be fine.” —
Smalltalk
Skip
18-6 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
foo
foo
foo
foo bar
bar
bar
bar
sna
snasna
sna
“You believe in things you don’t understand, you
may suffer.” — Stevie Wonder
Skip
18-7 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
foo
foo
foo
foo bar
bar
bar
bar
sna
snasna
sna
Where is the use case?
Where is the use case?
Skip
18-8 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
go
Context: Another
new concept
19-3 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
bar sna
Data
System Operations
20-4 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
bar sna
Data
Cont
extSystem
Operations
20-5 20161017FooCafé.key - 28 October 2016
Contextualized Polymorphism
bar sna
Data
Cont
ext
Inte
ract
ion
System Operations
20-6 20161017FooCafé.key - 28 October 2016
Hoare’s Insight• “There are two ways of constructing a software
design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.” — Tony Hoare
• The primary goal of trygve is to make system operation code readable
• It is in many ways a language for 7-year-olds
21 20161017FooCafé.key - 28 October 2016
Smalltalk 80
Smalltalk 76
Smalltalk 72
Simula 67
C++
Java
trygve
DCI Squeak
Algol 68
C#
Marvin
self
Javascript
C
Garbage-collected, single-
hierarchy
Syntax (Worse is Better)
Object thinking over classthink
D
Perl
awk
…
22-1 20161017FooCafé.key - 28 October 2016
Smalltalk 80
Smalltalk 76
Smalltalk 72
Simula 67
C++
Java
trygve
DCI Squeak
Algol 68
C#
Marvin
self
Javascript
C
Garbage-collected, single-
hierarchy
Syntax (Worse is Better)
Object thinking over classthink
D
Class-oriented Programming
Perl
awk
…
22-2 20161017FooCafé.key - 28 October 2016
Smalltalk 80
Smalltalk 76
Smalltalk 72
Simula 67
C++
Java
trygve
DCI Squeak
Algol 68
C#
Marvin
self
Javascript
C
Garbage-collected, single-
hierarchy
Syntax (Worse is Better)
Object thinking over classthink
D
Class-oriented Programming
Object-Oriented Programming
Perl
awk
…
22-3 20161017FooCafé.key - 28 October 2016
The trygve language• Contexts: System use cases in code
• The goal: readable code
• System use case steps are demarcated along Role boundaries
• The Context chooses objects — “Role-players” — for each Role (one-time binding)
• Many forms of object can play each Role — duck-typed through a contract specification
• Role-players are dumb, and Role / instance contracts should be simple and primitive
23 20161017FooCafé.key - 28 October 2016
trygve building blocks• Declarations and expressions — and one value
• Parser swallows most naive Java syntax
• Contexts: use cases — mainly a set of Roles
• Classes: the “domain model” (dumb): actor DNA • Classify objects by how they are built
• Roles: scripts for the actors — stateless • Classify objects by how they act
24 20161017FooCafé.key - 28 October 2016
• So this works: int fact(int n) { int retval = 1; if (n > 1) retval = n * fact(n - 1); return retval }
• but this is orthodox: int fact(int n) { return if (n <= 1) 1 else n * fact(n-1) }
25 20161017FooCafé.key - 28 October 2016
Details• Semicolons optional
• Classes, but few class-oriented features (e.g., there is no protected)
• Strongly type-checked at run-time-typed; Roles are duck-typed
• Rudimentary templates
• No exceptions, RTTI, concurrency
26 20161017FooCafé.key - 28 October 2016
More going on here than meets the eye
• The trygve language is a stepping stone to side-effect free programming
• States make it difficult for a Role-player to play several Roles (the MI problem)
• Stateless computation transcends the problem
27 20161017FooCafé.key - 28 October 2016
Both class and Context instances can play Roles
• … in which case, we really don’t need classes any more
• They never were part of the OO vision
• The trygve language supports more or less arbitrary scope nesting (anything inside anything)
• Classes become truly operational models: exactly the Piagetian ideal that Kay strove for
28-1 20161017FooCafé.key - 28 October 2016
Both class and Context instances can play Roles
• … in which case, we really don’t need classes any more
• They never were part of the OO vision
• The trygve language supports more or less arbitrary scope nesting (anything inside anything)
• Classes become truly operational models: exactly the Piagetian ideal that Kay strove for
28-2 20161017FooCafé.key - 28 October 2016
On babies and bathwater• Classes are still part of the business vocabulary
• They are at least part of the learned business mental model
• They are certainly part of the programmer mental model — and programmers are people, too
• So the “classless movement” is really a fad
29 20161017FooCafé.key - 28 October 2016
Reflection• Even common inclusion polymorphism is overly
general — reflection takes it far outside human mental models
• trygve slices reflection with a precisely honed scimitar
• Reflection is the Turing Machine of types
• It shows a lack of design discipline in the articulation of a paradigm
• See “Reflections on Reflection,” SPLASH 2013 keynote
30 20161017FooCafé.key - 28 October 2016
Conclusion: Everybody Wins• Good for humans…
• Both cognitive & volitive models in code
• Organizes, rather than suppresses, complexity
• … and software engineering
• Incremental addition of new use cases
• Reduced discovery cost
• Separates rate-of-change shearing layers
31 20161017FooCafé.key - 28 October 2016
Postlog• trygve is an open-source community research
effort — join it (GitHub jcoplien / trygve)
• Also a binary download and documentation at fulloo.info.
• We’ve been stuck gilding the lily and making much ado about nothing for far too long
• trygve does not aspire to be king: it exists only to combat ignorance and stimulate thinking & dialog
32 20161017FooCafé.key - 28 October 2016
Other assorted goodies
• Templates (very minimal — just enough so people can use familiar Java containers)
• Very basic Frames, Panels, Events, Colors
• Rudimentary InputStream & OutputStream I/O
• No exceptions (goal is readability)
34 20161017FooCafé.key - 28 October 2016
Look, Ma, no semicolonsinterface EventHandler { public void handleEvent(Event e) }
class MyPanel extends Panel { int XSIZE = 1000 int YSIZE = 600 public int xsize() { return XSIZE } public int ysize() { return YSIZE }
public MyPanel() { Panel() eventHandler_ = null frame_ = new Frame("Bouncy") frame_.add("Center", this) frame_.resize(XSIZE, YSIZE) frame_.setVisible(true) drawRect(0, 0, xsize(), ysize()) repaint() }
public boolean handleEvent(Event event) { boolean retval = true if (event.id == Event.MOUSE_MOVE) { if (eventHandler_ != null) { eventHandler_.handleEvent(event) } } return retval }
35 20161017FooCafé.key - 28 October 2016
Everything’s an expression
context SpellCheck { role Utilities { public boolean isDelim(String c) const { return switch (c) { case "ø": case "Ø": case "æ": case "Æ": case "å": case "Å": false; break default: (c < "a" || c > "z") && (c < "A" || c > "Z") } } } . . . .
36 20161017FooCafé.key - 28 October 2016
Duck-typed Role / Object Contracts
role ThePanel { public void drawCircle(int x, int y, int r) { fillOval(x+r, y+r, r, r) } public void drawPaddle(int xs, int ys, int h, int w) { drawRect(xs, ys, h, w) } public int maxX() { return xsize() } public int maxY() { return ysize() } public void setColor(Color c) { setForeground(c) } public void clearObjects() { removeAll() } public void clear() { setColor(new Color(227, 221, 240)); fillRect(0, 0, maxX() - 1, maxY() - 1 ) } } requires { void fillOval(int x, int y, int h, int w); void drawRect(int x, int y, int h, int w); void fillRect(int x, int y, int h, int w); int xsize(); int ysize(); void removeAll(); void setForeground(Color color) }
37 20161017FooCafé.key - 28 October 2016
A Puzzleclass ArrayDupTest { public void test() { int [] intArray = new int[5]; for (int i = 0; i < 5; i++) { intArray[i] = i }
for (int i = 0; i < 5; i++) { System.out.println(intArray[i]) } } }
new ArrayDupTest().test()
38 20161017FooCafé.key - 28 October 2016
Most class-oriented features have been removed
• protected
• super
• Class::
• ABCs
• static (though it exists internally)
39 20161017FooCafé.key - 28 October 2016
Can an object play more than one Role?
• In series, yes: that’s the whole idea • In parallel, no…
• One Context could instantiate another Context • That Context could share Role-players with the
original
• … unless it’s in the same Context so that there is no confusion
40 20161017FooCafé.key - 28 October 2016
The Object Machine• Classes are run-time objects (but not in the
language)
• Two scratch stacks: main, and event
• Two activation record stacks
• Uses Java GC + Context reference counting
• Like Smalltalk in that everything is an object
41 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Exampleclass Currency { public Currency(double amount) { amount_ = amount.clone } public Currency +(Currency amount) { assert(false) return this; } public Currency -(Currency amount) { assert(false) return this; } public String name() const { assert(false); return "" } public String sign() const { assert(false); return "" } public double amountInEuro() const { assert(false); return 0.0 } public double amount() const { return amount_ }
42
43 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example public String toString() const { return amountInEuro().toString() } public int compareTo(Currency other) { if (amount() > other.amount()) return 1 else if (amount() < other.amount()) return -1; return 0 }
private double amount_ }
class Euro extends Currency { public Euro(double amount) { Currency(amount) } public Euro -(Currency amount) { return new Euro(amount() - amount.amountInEuro()) } public Euro +(Currency amount) { return new Euro(amount() + amount.amountInEuro()) } public String name() const { return "Euro"; } public String sign() const { return "€"; }
43
44 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example public double amountInEuro() const { return amount() } public String toString() const { return amount().toString() } }
class Account { public Account() { acct_ = 1234 } public String accountID() const { return acct_.toString() } public Currency availableBalance() const { assert(false); return null } public void increaseBalance(Currency amount) { assert(false) } public void decreaseBalance(Currency amount) { assert(false) } public void updateLog(String message, Date date, Currency amount) { assert(false) } private int acct_ }
44
45 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Exampleclass CheckingAccount extends Account { public CheckingAccount() { availableBalance_ = new Euro(100.00) } public Currency availableBalance() const { return availableBalance_ } public void decreaseBalance(Currency c) { availableBalance_ = availableBalance_ - c } public void updateLog(String message, Date t, Currency c) const { System.out.print("account: ").print(accountID()) .print(" CheckingAccount::updateLog(\"").print(message) .print("\", ").print(t.toString()).print(", ") .print(c.toString()).print(“)") .println() } public void increaseBalance(Currency c) { availableBalance_ = availableBalance_ + c }
private Currency availableBalance_ }
class SavingsAccount extends Account { public SavingsAccount() { availableBalance_ = new Euro(0.00) } public Currency availableBalance() const { return availableBalance_ }
45
46 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example public void decreaseBalance(Currency c) { assert(c > availableBalance_); availableBalance_ = availableBalance_ - c }
public void updateLog(String logMessage, Date timeOfTransaction, Currency amountForTransaction) const { assert(logMessage.length() > 0); assert(logMessage.length() < MAX_BUFFER_SIZE); // assert(new Date() < timeOfTransaction); System.out.print("account: ").print(accountID()) .print(" SavingsAccount::updateLog(\"").print(logMessage) .print("\", ").print(timeOfTransaction.toString()) .print(", ").print(amountForTransaction.toString()) .print(“)") .println() } public void increaseBalance(Currency c) { availableBalance_ = availableBalance_ + c }
private Currency availableBalance_; private int MAX_BUFFER_SIZE = 256 }
46
47 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Exampleclass InvestmentAccount extends Account { public InvestmentAccount() { availableBalance_ = new Euro(0.00) } public Currency availableBalance() const { return availableBalance_ } public void increaseBalance(Currency c) { availableBalance_ = availableBalance_ + c } public void decreaseBalance(Currency c) { availableBalance_ = availableBalance_ - c; } public void updateLog(String s, Date t, Currency c) const { System.out.print("account: ").print(accountID()) .print(" InvestmentAccount::updateLog(\"") .print(s).print("\", ").print(t.toString()) .print(", ").print(c.toString()).print(")") .println() }
private Currency availableBalance_; }
47
48 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Exampleclass Creditor { public Creditor(Account account) { account_ = account } public Account account() { return account_ } public Currency amountOwed() const { return new Currency(0.0) }
private Account account_ }
class ElectricCompany extends Creditor { public ElectricCompany() { Creditor(new CheckingAccount()) } public Currency amountOwed() const { return new Euro(15.0) } }
class GasCompany extends Creditor { public GasCompany() { Creditor( new SavingsAccount()); account().increaseBalance(new Euro(500.00)) // start off with a balance of 500 } public Currency amountOwed() const { return new Euro(18.76) } }
48
49 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Examplecontext TransferMoneyContext { // Roles
role AMOUNT { public Currency(double amount); public Currency +(Currency amount); public Currency -(Currency amount); public String name() const; public String sign() const; public double amountInEuro() const; public double amount() const; public String toString() const; public int compareTo(Currency other) } requires { Currency(double amount); Currency +(Currency amount); Currency -(Currency amount); String name() const; String sign() const; double amountInEuro() const; double amount() const; String toString() const; int compareTo(Currency other) }
role GUI { public void displayScreen(int displayCode) } requires { void displayScreen(int displayCode) }
49
50 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example role SOURCE_ACCOUNT { public void transferTo() { // This code is reviewable and meaningfully testable with stubs! int SUCCESS_DEPOSIT_SCREEN = 10;
beginTransaction(); if (this.availableBalance() < AMOUNT) { endTransaction(); assert(false, "Unavailable balance") } else { this.decreaseBalance(AMOUNT); DESTINATION_ACCOUNT.increaseBalance(AMOUNT); this.updateLog("Transfer Out", new Date(), AMOUNT); DESTINATION_ACCOUNT.updateLog("Transfer In", new Date(), AMOUNT); } GUI.displayScreen(SUCCESS_DEPOSIT_SCREEN); endTransaction() } } requires { void decreaseBalance(Currency amount); Currency availableBalance() const; void updateLog(String msg, Date time, Currency amount) }
50
51 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example role DESTINATION_ACCOUNT { public void transferFrom() { this.increaseBalance(AMOUNT); this.updateLog("Transfer in", new Date(), AMOUNT); } public void increaseBalance(Currency amount); public void updateLog(String msg, Date time, Currency amount) } requires { void increaseBalance(Currency amount); void updateLog(String msg, Date time, Currency amount) }
public TransferMoneyContext(Currency amount, Account source, Account destination) { SOURCE_ACCOUNT = source; DESTINATION_ACCOUNT = destination; AMOUNT = amount } public TransferMoneyContext() { lookupBindings() } public void doit() { SOURCE_ACCOUNT.transferTo() }
51
52 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example private void lookupBindings() { // These are somewhat arbitrary and for illustrative // purposes. The simulate a database lookup InvestmentAccount investmentAccount = new InvestmentAccount(); investmentAccount.increaseBalance(new Euro(100.00)); // prime it with some money SOURCE_ACCOUNT = investmentAccount; DESTINATION_ACCOUNT = new SavingsAccount(); DESTINATION_ACCOUNT.increaseBalance(new Euro(500.00)); // start it off with money AMOUNT = new Euro(30.00) } }
52
53 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Examplecontext PayBillsContext { public PayBillsContext() { lookupBindings } role [] CREDITORS { } requires { Currency amountOwed() } stageprop SOURCE_ACCOUNT { public String accountID() const; public Currency availableBalance() const; public void increaseBalance(Currency amount) unused; public void decreaseBalance(Currency amount) unused; public void updateLog(String message, Date date, Currency amount) unused } requires { String accountID() const; Currency availableBalance() const; void increaseBalance(Currency amount); void decreaseBalance(Currency amount); void updateLog(String message, Date date, Currency amount) }
public void doit() { for (Creditor credit : CREDITORS) { // Note that here we invoke another use case TransferMoneyContext xfer = new TransferMoneyContext( credit.amountOwed(), SOURCE_ACCOUNT, credit.account()); xfer.doit() } }
53
54 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
trygve Account Example private void lookupBindings() { // These are somewhat arbitrary and for illustrative // purposes. The simulate a database lookup InvestmentAccount investmentAccount = new InvestmentAccount(); investmentAccount.increaseBalance(new Euro(100.00)); // prime it with some money SOURCE_ACCOUNT = investmentAccount;
Creditor [] creditors = new Creditor [2];
creditors[0] = new ElectricCompany(); creditors[1] = new GasCompany();
CREDITORS = creditors } }
{ // Main
TransferMoneyContext aNewUseCase = new TransferMoneyContext(); aNewUseCase.doit();
PayBillsContext anotherNewUseCase = new PayBillsContext(); anotherNewUseCase.doit() }
54
55 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
C++ Account ExampleMany thanks to Felix Petriconi for bringing this up-to-date for C++14!
55
56 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currency/* * Currency.h * * Created by James Coplien on 7/31/09. * Copyright 2009 Gertrud & Cope. All rights reserved. */
#ifndef _CURRENCY_H #define _CURRENCY_H
#include <string>
class Currency;
class CurrencyBase { friend class Currency; public: CurrencyBase(); virtual ~CurrencyBase() = default; virtual CurrencyBase &operator+=(const Currency& amount) = 0; virtual CurrencyBase &operator-=(const Currency& amount) = 0; virtual CurrencyBase &operator=(const Currency& amount) = 0; virtual std::string name() const = 0; virtual std::string sign() const = 0; virtual double amountInEuro() const = 0; virtual std::string asString() const = 0; protected: unsigned referenceCount_; };
56
57 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currencyclass Currency { public: Currency(); Currency(const Currency& amount); virtual Currency& operator=(const Currency& amount); explicit Currency(CurrencyBase *derivedClassThis); virtual ~Currency(); virtual Currency& operator+=(const Currency& amount); virtual Currency& operator-=(const Currency& amount); virtual std::string name() const; virtual std::string sign() const; virtual double amountInEuro() const; virtual std::string asString() const; friend bool operator==(const Currency& x, const Currency& y); friend bool operator!=(const Currency& x, const Currency& y); friend bool operator<(const Currency& x, const Currency& y); friend bool operator>(const Currency& x, const Currency& y); friend bool operator<=(const Currency& x, const Currency& y); friend bool operator>=(const Currency& x, const Currency& y);
57
58 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currency friend std::ostream &operator<<(std::ostream& stream, const Currency& currency); private: CurrencyBase *actualCurrency_; };
class Euro: public CurrencyBase { friend class Currency; public: explicit Euro(double amount = 0.0); virtual ~Euro() = default; operator Currency(); CurrencyBase *copy() const; Euro &operator+=(const Currency& amount) override; Euro &operator-=(const Currency& amount) override; Euro &operator=(const Currency& amount) override; std::string name() const override; std::string sign() const override; double amountInEuro() const override; std::string asString() const override; private: double amount_; };
#endif
58
59 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currency/* * Currency.h * * Created by James Coplien on 7/31/09. * Copyright 2009 Gertrud & Cope. All rights reserved. */
#ifndef _CURRENCY_H #define _CURRENCY_H
#include <string>
class Currency;
class CurrencyBase { friend class Currency; public: CurrencyBase(); virtual ~CurrencyBase() = default; virtual CurrencyBase &operator+=(const Currency& amount) = 0; virtual CurrencyBase &operator-=(const Currency& amount) = 0;
59
60 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currency virtual CurrencyBase &operator=(const Currency& amount) = 0; virtual std::string name() const = 0; virtual std::string sign() const = 0; virtual double amountInEuro() const = 0; virtual std::string asString() const = 0; protected: unsigned referenceCount_; };
class Currency { public: Currency(); Currency(const Currency& amount); virtual Currency& operator=(const Currency& amount); explicit Currency(CurrencyBase *derivedClassThis); virtual ~Currency(); virtual Currency& operator+=(const Currency& amount); virtual Currency& operator-=(const Currency& amount); virtual std::string name() const; virtual std::string sign() const;
60
61 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currency virtual double amountInEuro() const; virtual std::string asString() const; friend bool operator==(const Currency& x, const Currency& y); friend bool operator!=(const Currency& x, const Currency& y); friend bool operator<(const Currency& x, const Currency& y); friend bool operator>(const Currency& x, const Currency& y); friend bool operator<=(const Currency& x, const Currency& y); friend bool operator>=(const Currency& x, const Currency& y); friend std::ostream &operator<<(std::ostream& stream, const Currency& currency); private: CurrencyBase *actualCurrency_; };
61
62 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currencyclass Euro: public CurrencyBase { friend class Currency; public: explicit Euro(double amount = 0.0); virtual ~Euro() = default; operator Currency(); CurrencyBase *copy() const; Euro &operator+=(const Currency& amount) override; Euro &operator-=(const Currency& amount) override; Euro &operator=(const Currency& amount) override; std::string name() const override; std::string sign() const override; double amountInEuro() const override; std::string asString() const override; private: double amount_; };
#endif
62
63 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currency/* * Currency.cpp * AgileBook * * Created by James Coplien on 7/31/09. * Copyright 2009 Gertrud & Cope. All rights reserved. * */
#include "Currency.h"
#include <sstream>
CurrencyBase::CurrencyBase() : referenceCount_(1) { }
Currency &Currency::operator+=(const Currency &amount) { *actualCurrency_ += amount; return *this; }
Currency &Currency::operator-=(const Currency &amount) { *actualCurrency_ -= amount; return *this; }
63
64 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: CurrencyCurrency &Currency::operator=(const Currency &amount) { amount.actualCurrency_->referenceCount_++; if (--actualCurrency_->referenceCount_ <= 0) delete actualCurrency_; actualCurrency_ = amount.actualCurrency_; return *this; }
Currency::Currency(const Currency &amount) { (actualCurrency_ = amount.actualCurrency_)->referenceCount_++; }
Currency::Currency() : actualCurrency_(new Euro) {}
Currency::~Currency() { if (--actualCurrency_->referenceCount_ <= 0) delete actualCurrency_; }
std::string Currency::name() const { return actualCurrency_->name(); }
std::string Currency::sign() const { return actualCurrency_->sign(); }
64
65 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currencybool operator<=(const Currency &x, const Currency &y) { return !(y < x); }
bool operator>=(const Currency &x, const Currency &y) { return !(x < y); }
Currency::Currency(CurrencyBase *derivedClassThis) : actualCurrency_(derivedClassThis) {}
Euro::operator Currency() { return Currency(this->copy()); }
CurrencyBase *Euro::copy() const { return new Euro(amount_); }
Euro &Euro::operator+=(const Currency &amount) { amount_ += amount.amountInEuro(); return *this; }
65
66 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: CurrencyEuro &Euro::operator-=(const Currency &amount) { amount_ -= amount.amountInEuro(); return *this; }
Euro &Euro::operator=(const Currency &amount) { amount_ = amount.amountInEuro(); return *this; }
Euro::Euro(double amount) : amount_(amount) {}
std::string Euro::name() const { return "Euro"; }
std::string Euro::sign() const { return "€"; }
double Euro::amountInEuro() const { return amount_; }
66
67 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currencystd::string Euro::asString() const { std::ostringstream stream; stream << amount_; return stream.str(); }
67
68 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Currencybool operator<=(const Currency &x, const Currency &y) { return !(y < x); }
bool operator>=(const Currency &x, const Currency &y) { return !(x < y); }
Currency::Currency(CurrencyBase *derivedClassThis) : actualCurrency_(derivedClassThis) {}
Euro::operator Currency() { return Currency(this->copy()); }
CurrencyBase *Euro::copy() const { return new Euro(amount_); }
Euro &Euro::operator+=(const Currency &amount) { amount_ += amount.amountInEuro(); return *this; }
Euro &Euro::operator-=(const Currency &amount) { amount_ -= amount.amountInEuro(); return *this; }
Euro &Euro::operator=(const Currency &amount) { amount_ = amount.amountInEuro(); return *this; }
68
69 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Account/* * Account.h * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. * */
#ifndef _ACCOUNT_H #define _ACCOUNT_H
#include <string> #include "Currency.h"
class Account { public: Account(); std::string accountID() const; virtual void increaseBalance(const Currency&) = 0; virtual void decreaseBalance(const Currency&) = 0; private: int acct_; };
#endif
69
70 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Account/* * Account.cpp * AgileBook * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. * */
#include "Account.h" #include <sstream>
using namespace std;
namespace { int accountCounter = 0; }
Account::Account() { acct_ = accountCounter++; }
string Account::accountID() const { string retval; std::stringstream s(retval); s << acct_; return retval; }
70
71 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: CheckingAccount/* * CheckingAccount.h * * Created by James Coplien on 9/17/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#ifndef _CHECKINGACCOUNT_H #define _CHECKINGACCOUNT_H
#include "Account.h" #include "Currency.h" #include "TransferMoneyContext.h" #include "MyTime.h" #include <string>
class CheckingAccount: public Account, public TransferMoneyContext::TransferMoneySink<CheckingAccount> { public: CheckingAccount(); virtual ~CheckingAccount() = default;
// These functions can be virtual if there are // specialisations of CheckingAccount Currency availableBalance() const; void decreaseBalance(const Currency&) override; void increaseBalance(const Currency&) override; void updateLog(const std::string&, const MyTime&, const Currency&) const override; private: Currency availableBalance_; };
#endif
71
72 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: CheckingAccount/* * CheckingAccount.cpp * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#include "CheckingAccount.h" #include "PayBillsContext.h" #include "Currency.h" #include "TransferMoneyContext.h" #include <iostream>
CheckingAccount::CheckingAccount() : availableBalance_(Euro(100.00)) { }
Currency CheckingAccount::availableBalance() const { std::cout << "CheckingAccount::availableBalance returns " << availableBalance_ << std::endl; return availableBalance_; }
void CheckingAccount::decreaseBalance(const Currency& c) { std::cout << "CheckingAccount::decreaseBalance(" << c << ")" << std::endl; availableBalance_ -= c; }
72
73 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
void CheckingAccount::updateLog(const string& message, const MyTime& t, const Currency& c) const { std::cout << "account: " << accountID() << " CheckingAccount::updateLog(\"" << message << "\", MyTime, " << c << ")" << std::endl; }
void CheckingAccount::increaseBalance(const Currency& c) { std::cout << "CheckingAccount::increaseBalance(" << c << ")" << std::endl; availableBalance_ += c; }
Account Example: CheckingAccount
73
74 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: SavingsAccount/* * SavingsAccount.h * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#ifndef _SAVINGSACCOUNT_H #define _SAVINGSACCOUNT_H
#include "Account.h" #include "TransferMoneyContext.h" #include "MyTime.h" #include <string>
class SavingsAccount: public Account, public TransferMoneyContext::TransferMoneySink<SavingsAccount> { public: SavingsAccount(); SavingsAccount(Currency initialBalance): availableBalance_(initialBalance) { } private: // These functions can be virtual if there are // specialisations of SavingsAccount Currency availableBalance() const; void decreaseBalance(const Currency&) override; void increaseBalance(const Currency&) override; void updateLog(const std::string&, const MyTime&, const Currency&) const override; private: Currency availableBalance_; };
#endif
74
75 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: SavingsAccount/* * SavingsAccount.cpp * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#include "SavingsAccount.h" #include "PayBillsContext.h" #include "TransferMoneyContext.h"
#include <iostream> #include <cassert>
using namespace std;
SavingsAccount::SavingsAccount() : availableBalance_(Euro(0.00)) { // no application code assert(availableBalance_ == Euro(0.0)); }
Currency SavingsAccount::availableBalance() const { cout << "SavingsAccount::availableBalance returns " << availableBalance_ << std::endl; return availableBalance_; }
75
76 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: SavingsAccountvoid SavingsAccount::decreaseBalance(const Currency& c) { cout << "SavingsAccount::decreaseBalance(" << c << ")" << endl; assert(c > availableBalance_); availableBalance_ -= c; assert(availableBalance_ > Euro(0.0)); }
const unsigned MAX_BUFFER_SIZE = 256;
void SavingsAccount::updateLog(const string& logMessage, const MyTime& timeOfTransaction, const Currency& amountForTransaction) const { assert(logMessage.size() > 0); assert(logMessage.size() < MAX_BUFFER_SIZE); assert(MyTime("00:00:00.00 1970/1/1") < timeOfTransaction); cout << "account: " << accountID() << " SavingsAccount::updateLog(\"" << logMessage << "\", MyTime, " << amountForTransaction << ")" << endl; }
void SavingsAccount::increaseBalance(const Currency& c) { cout << "SavingsAccount::increaseBalance(" << c << ")" << endl; availableBalance_ += c; }
76
77 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: InvestmentAccount/* * InvestmentAccount.h * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#ifndef _INVESTMENTACCOUNT_H #define _INVESTMENTACCOUNT_H
#include "Account.h" #include "Currency.h" #include "TransferMoneyContext.h" #include "MyTime.h"
class InvestmentAccount: public TransferMoneyContext::TransferMoneySource<InvestmentAccount> { public: InvestmentAccount(); Currency availableBalance() const; void increaseBalance(const Currency&) override; void decreaseBalance(const Currency&) override; void updateLog(const std::string&, const MyTime&, const Currency&) const override; private: Currency availableBalance_; };
#endif
77
78 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: InvestmentAccount/* * InvestmentAccount.cpp * * Created by James Coplien on 9/2/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#include "InvestmentAccount.h" #include "PayBillsContext.h" #include "TransferMoneyContext.h" #include <string> #include <iostream>
InvestmentAccount::InvestmentAccount() : availableBalance_(Euro(0.00)) {}
Currency InvestmentAccount::availableBalance() const { std::cout << "InvestmentAccount::availableBalance returns " << availableBalance_ << std::endl; return availableBalance_; }
void InvestmentAccount::increaseBalance(const Currency &c) { std::cout << "InvestmentAccount::increaseBalance(" << c << ")" << std::endl; availableBalance_ += c; }
78
79 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
void InvestmentAccount::decreaseBalance(const Currency &c) { std::cout << "InvestmentAccount::decreaseBalance(" << c << ")" << std::endl; availableBalance_ -= c; }
void InvestmentAccount::updateLog(const std::string &s, const MyTime &, const Currency &c) const { std::cout << "account: " << accountID() << " InvestmentAccount::updateLog(\"" << s << "\", Time, " << c << ")" << std::endl; }
Account Example: InvestmentAccount
79
80 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Creditors/* * Creditor.h * * Created by James Coplien on 9/17/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#ifndef _CREDITOR_H #define _CREDITOR_H
#include "Currency.h" #include "MoneyPort.h" #include "TransferMoneyContext.h"
class Creditor { public: Creditor(TransferMoneyContext::MoneySink *account); virtual ~Creditor(); virtual TransferMoneyContext::MoneySink *account() { return account_; } virtual Currency amountOwed() const = 0; protected: TransferMoneyContext::MoneySink *account_; };
80
81 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Creditors
class ElectricCompany: public Creditor { public: ElectricCompany(); virtual ~ElectricCompany() = default; Currency amountOwed() const override; };
class GasCompany: public Creditor { public: GasCompany(); virtual ~GasCompany() = default; Currency amountOwed() const override; };
#endif
81
82 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Creditors/* * Creditor.cpp * * Created by James Coplien on 9/17/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#include "Creditor.h" #include "CheckingAccount.h" #include "SavingsAccount.h"
Creditor::Creditor(TransferMoneyContext::MoneySink *account) : account_(account) { }
Creditor::~Creditor() { delete account_; }
ElectricCompany::ElectricCompany(): Creditor(new CheckingAccount()) { }
Currency ElectricCompany::amountOwed() const { return Euro(15.0); }
82
83 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: CreditorsGasCompany::GasCompany(): Creditor(new SavingsAccount(Euro(500.00))) // for the demo { }
Currency GasCompany::amountOwed() const { return Euro(18.76); // for the demo }
83
84 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Context/**Context.h**CreatedbyJamesCoplienon1/14/09.UpdatedtoC++147/12/2016.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#ifndef_CONTEXT_H#define_CONTEXT_H
classContext{public:Context();virtual~Context();public:staticContext*currentContext_;private:Context*parentContext_;};
#endif
84
85 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Context/**Context.cpp**CreatedbyJamesCoplienon1/14/09.UpdatedtoC++147/12/2016.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#include"Context.h"
Context*Context::currentContext_=nullptr;
Context::Context(){parentContext_=currentContext_;currentContext_=this;}
Context::~Context(){currentContext_=parentContext_;}
85
86 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Transfer Money/* * TransferMoneyContext.h * * Created by James Coplien on 9/13/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
// TransferMoneyContext knows how to find the objects for a given // use case invocation. It associates those objects with // the roles they play in a use case of this type; and it // publishes those interface bindings for use by the // other Roles that participate in the use case (behind the // scenes).
#ifndef _XFERMONEYCONTEXT_H #define _XFERMONEYCONTEXT_H
#include "Account.h" #include "Context.h" #include "Currency.h" #include "MyExceptions.h" #include "Globals.h" #include "MoneyPort.h" #include <string>
class MyTime;
86
87 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
class TransferMoneyContext: public Context { public: // Roles class MoneySource: public MoneyPort, public Account { public: virtual ~MoneySource() = default; virtual void transferTo() = 0; virtual void decreaseBalance(const Currency&) = 0; virtual void updateLog(const std::string&, const MyTime&, const Currency& amount) const = 0; };
Account Example: Transfer Money
87
88 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
template <class RolePlayer> class TransferMoneySource: public MoneySource { public: TransferMoneySource() = default; virtual ~TransferMoneySource() = default; virtual void transferTo() override { // This code is reviewable and // meaningfully testable with stubs! beginTransaction(); if (SELF<RolePlayer>(this)->availableBalance() < AMOUNT<TransferMoneyContext>()) { endTransaction(); throw InsufficientFunds(); } else { SELF<RolePlayer>(this)->decreaseBalance(AMOUNT<TransferMoneyContext>()); RECIPIENT<TransferMoneyContext>()-> increaseBalance(AMOUNT<TransferMoneyContext>()); SELF<RolePlayer>(this)->updateLog("Transfer Out", DateTime(), AMOUNT<TransferMoneyContext>()); RECIPIENT<TransferMoneyContext>()->updateLog("Transfer In", DateTime(), AMOUNT<TransferMoneyContext>()); } GUI->displayScreen(SUCCESS_DEPOSIT_SCREEN); endTransaction(); } };
Account Example: Transfer Money
88
89 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
class MoneySink: public MoneyPort { public: virtual ~MoneySink() = default; virtual void increaseBalance(const Currency& amount) = 0; virtual void updateLog(const std::string&, const MyTime&, const Currency& amount) const = 0; }; template <class RolePlayer> class TransferMoneySink: public MoneySink { public: TransferMoneySink() = default; virtual ~TransferMoneySink() = default; virtual void transferFrom(const Currency& amount) { SELF<RolePlayer>(this)->increaseBalance(amount); SELF<RolePlayer>(this)->updateLog("Transfer in", DateTime(), amount); } }; public: TransferMoneyContext(); TransferMoneyContext(const Currency& amount, MoneySource *src, MoneySink *destination); void doit(); TransferMoneyContext::MoneySource *sourceAccount() const; TransferMoneyContext::MoneySink *destinationAccount() const; Currency amount() const;
Account Example: Transfer Money
89
90 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
private: void lookupBindings(); MoneySource *sourceAccount_; MoneySink *destinationAccount_; Currency amount_; };
#endif
Account Example: Transfer Money
90
91 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
/* * TransferMoneyContext.cpp * * Created by James Coplien on 9/13/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#include "TransferMoneyContext.h" #include "InvestmentAccount.h" #include "SavingsAccount.h"
TransferMoneyContext::TransferMoneyContext() : Context() { lookupBindings(); }
TransferMoneyContext::~TransferMoneyContext() { }
TransferMoneyContext::TransferMoneyContext(const Currency& amount, MoneySource *source, MoneySink *destination) : Context() { amount_ = amount; sourceAccount_ = source; destinationAccount_ = destination; }
Account Example: Transfer Money
91
92 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
void TransferMoneyContext::doit() { sourceAccount()->transferTo(); }
void TransferMoneyContext::lookupBindings() { // These are somewhat arbitrary and for illustrative // purposes. The simulate a database lookup InvestmentAccount *investmentAccount = new InvestmentAccount; investmentAccount->increaseBalance(Euro(100.00)); // prime it with some money sourceAccount_ = investmentAccount; destinationAccount_ = new SavingsAccount; destinationAccount_->increaseBalance(Euro(500.00)); // start it off with money amount_ = Euro(30.00); }
TransferMoneyContext::MoneySource *TransferMoneyContext::sourceAccount() const { return sourceAccount_; }
TransferMoneyContext::MoneySink *TransferMoneyContext::destinationAccount() const { return destinationAccount_; }
Currency TransferMoneyContext::amount() const { return amount_; }
Account Example: Transfer Money
92
93 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: PayBills/* * PayBillsContext.h * * Created by James Coplien on 9/13/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#ifndef _PAYBILLSCONTEXT_H #define _PAYBILLSCONTEXT_H
#include "TransferMoneyContext.h" #include "Creditor.h" #include <vector>
class Creditor;
class PayBillsContext: public Context { template <typename T> auto CREDITORS() { return static_cast<T*>(Context::currentContext_)->creditors(); } template <typename T> auto SOURCE_ACCOUNT() { return static_cast<T*>(Context::currentContext_)->sourceAccount(); }
93
94 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: PayBillspublic: PayBillsContext(); ~PayBillsContext(); TransferMoneyContext::MoneySource *sourceAccount() const; std::vector<Creditor*> creditors() const; // Role behaviours void doit() { // While object contexts are changing, we don't want to // have an open iterator on an external object. Make a // local copy. for (auto& credit : CREDITORS<PayBillsContext>()) { try { // Note that here we invoke another use case TransferMoneyContext transferTheFunds(credit->amountOwed(), SOURCE_ACCOUNT<PayBillsContext>(), credit->account()); transferTheFunds.doit(); } catch (const InsufficientFunds&) { throw; } } } private: void lookupBindings(); TransferMoneyContext::MoneySource *sourceAccount_; std::vector<Creditor *> creditors_; };
#endif
94
95 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: PayBills/* * PayBillsContext.cpp * * Created by James Coplien on 9/17/08. * Copyright 2008 Gertrud & Cope. All rights reserved. */
#include "PayBillsContext.h" #include "InvestmentAccount.h" #include "SavingsAccount.h" #include "Creditor.h"
PayBillsContext::PayBillsContext() : Context() { lookupBindings(); }
PayBillsContext::~PayBillsContext() { delete sourceAccount_; for (Creditor *creditor : creditors_) delete creditor; }
95
96 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: PayBillsvoid PayBillsContext::lookupBindings() { // These are somewhat arbitrary and for illustrative // purposes. The simulate a database lookup TransferMoneyContext::TransferMoneySource<InvestmentAccount> *investmentAccount = new InvestmentAccount; investmentAccount->increaseBalance(Euro(100.00)); // prime it with some money sourceAccount_ = investmentAccount; creditors_.push_back(new ElectricCompany); creditors_.push_back(new GasCompany); }
TransferMoneyContext::MoneySource *PayBillsContext::sourceAccount() const { return sourceAccount_; }
std::vector<Creditor *> PayBillsContext::creditors() const { return creditors_; }
96
97 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: main// // main.cpp // DCI Money Transfer // // Created by James Coplien on 12/07/16. // Copyright © 2016 Gertrud & Cope. All rights reserved. //
#include <iostream>
#include "TransferMoneyContext.h" #include "PayBillsContext.h" #include <memory>
int main() { auto aNewUseCase = std::unique_ptr<TransferMoneyContext>( new TransferMoneyContext()); aNewUseCase->doit(); auto anotherNewUseCase = std::unique_ptr<PayBillsContext>( new PayBillsContext()); anotherNewUseCase->doit();
return 0; }
97
98 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Exceptions/**MyExceptions.h**CreatedbyJamesCoplienon9/2/08.UpdatedtoC++147/12/16.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#ifndef_MYEXCEPTIONS_H#define_MYEXCEPTIONS_H
#include<exception>
classInsufficientFunds:publicstd::exception{public: InsufficientFunds();};
#endif
98
99 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Exceptions/**MyExceptionsImplementation.cpp**CreatedbyJamesCoplienon9/2/08.UpdatedtoC++147/12/16.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#include"MyExceptions.h"
InsufficientFunds::InsufficientFunds(){}
99
100 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: MyTime/**MyTime.h**CreatedbyJamesCoplienon9/2/08.UpdatedtoC++147/12/16.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#ifndef_MYTIME_H#define_MYTIME_H
#include<string>
classMyTime{public:MyTime()=default;explicitMyTime(longlong);explicitMyTime(conststd::string&timeAsString);~MyTime();MyTime(constMyTime&t);MyTime&operator=(constMyTime&t);
friendbooloperator<(constMyTime&x,constMyTime&y);};
#endif
100
101 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: MyTime/**MyTimeImplementation.cpp**CreatedbyJamesCoplienon9/2/08.UpdatedtoC++147/12/16.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#include"MyTime.h"
MyTime::MyTime(longlong){}
MyTime::MyTime(conststd::string&timeAsString){}
MyTime::~MyTime(){}
MyTime::MyTime(constMyTime&t){}
MyTime&MyTime::operator=(constMyTime&t){return*this;}
booloperator<(constMyTime&x,constMyTime&y){returntrue;}
101
102 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Globals/**Globals.h**CreatedbyJamesCoplienon9/2/08.UpdatedtoC++147/12/16.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#ifndef_GLOBALS_H#define_GLOBALS_H
#include"MyTime.h"
externvoidendTransaction();externvoidbeginTransaction();externMyTimeDateTime();
#endif
102
103 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: Globals/**GlobalsImplementation.cpp**CreatedbyJamesCoplienon9/2/08.UpdatedtoC++147/12/16.*Copyright©2016Gertrud&Cope.Allrightsreserved.*/
#include"Globals.h"#include"MyTime.h"#include<iostream>
voidendTransaction(){std::cout<<"::endTransaction()"<<std::endl;}
voidbeginTransaction(){std::cout<<"::beginTransaction()"<<std::endl;}
MyTimeDateTime(){returnMyTime(0);}
103
104 20161017FooCafé.key - 28 October 2016
G&C Coplien — Lean Architecture
Account Example: MoneyPort////MoneyPort.h//DCIMoneyTransfer////CreatedbyJamesCoplienon12/07/16.//Copyright©2016Gertrud&Cope.Allrightsreserved.//
#ifndefMoneyPort_h#defineMoneyPort_h
#include"Context.h"
classMoneyPort{//Commonprivateutilityfunctions
template<typenameT,typenameU>autoSELF(U*u){returnstatic_cast<T*>(u);}
template<typenameT>autoRECIPIENT(){autoresult=dynamic_cast<T*>(Context::currentContext_);if(result)returnresult->destinationAccount();throwstd::bad_cast();//("dynamiccastfailed");}
104
105 20161017FooCafé.key - 28 October 2016