One of the coolest things about powershell is being able to customize the shell. Here’s what my shell looks like now.
When I’m working on a project using git, my prompt looks like this.
It now tells me what branch i am on. Whoa… All I had to do was drop a modified version of profile.ps1 into “c:\users\mo\documents\WindowsPowerShell”. If the “WindowsPowerShell” folder doesn’t exist, then create it. That’s what I did. This is also using posh-git. If you checkout the source you’ll find an example of the profile.ps1 that you can use.
Leveraging this file you can load other scripts every time you pop open a powershell. Like if you wanted to load a sweet twitter script. Here’s my current script…
Import-Module d:/scripts/posh-git/posh-git
d:\scripts\twitter-on-powershell\twitter-on-powershell.ps1
d:\scripts\vsvars2010.ps1
function prompt {
$user_location = $env:username + '@' + [System.Environment]::MachineName + ' /' + ([string]$pwd).replace('\', '/').replace(':', '').tolower() + ' ~'
$host.UI.RawUi.WindowTitle = $pwd
Write-Host($user_location) -foregroundcolor green
# Git Prompt
$Global:GitStatus = Get-GitStatus
Write-GitStatus $GitStatus
return "> "
}
if(-not (Test-Path Function:\DefaultTabExpansion)) {
Rename-Item Function:\TabExpansion DefaultTabExpansion
}
function TabExpansion($line, $lastWord) {
$lastBlock = [regex]::Split($line, '[|;]')[-1]
switch -regex ($lastBlock) {
# Execute git tab completion for all git-related commands
'git (.*)' { GitTabExpansion $lastBlock }
# Fall back on existing tab expansion
default { DefaultTabExpansion $line $lastWord }
}
}
Enable-GitColors
The cool part is that everything you write in a powershell console can be dropped right in to a .ps1 file and run as a script. I’m actively learning…
At ARC I recently got to work on a multi touch screen application for a Smart Board. The application is for when guests come to visit the office, they use the touch screen to lookup the person they are here to see, then create a visitor pass for their visit. The application was built in Flash, which is a technology I have almost no experience with. However, they Flash guys were having some trouble getting the multi-touch piece working on the board. That’s when I came in.
I ended up building a TUIO bridge that is an overlay on top of their application. When a touch is recorded I am building up TUIO packets and flushing it to all connected clients on TCP port 3000. We received a version of a TUIO overlay from the guys at SMART but it didn’t work and the code was a procedural mess. I was committed to writing an OO friendly version of the overlay, and so far so good. There were some tiny things that we had to change to get this working. For instance our target machine was a 64 bit copy of Windows 7. Because of this we had to change some registry settings for the SMART board software. This was a pain to figure out but we got some decent help from a developer at SMART Technologies. Here’s a snippet of an email he from the SMART guy.
We replicated the problem on this end with my Windows 7 machine, so I have a fix for you. What was happening is that when you did a second contact, the software was feeding both contacts to the Windows 7 Gesture recognition engine. To bypass this.
FIRST:
Regedit:
HKEY_CURRENT_USER\Software\Classes\Virtual Store\MACHINE\SOFTWARE\Wow6432Node\SMART Technologies\SMART Board Drivers\Board1\IsDoGesture to 0
The key should already exist.
SECOND:
Run SMART Board Control Panel
- Under SMART Hardware Settings\Mouse and Gesture TURN OFF Enable Multitouch Gestures and Enable Single Touch Gestures.
THIRD:
- Restart SMART Board Service so it picks up the new settings. Under SMART Board Control Panel -> About Software and.. -> Tools -> Diagnostics -> Service -> Stop.
And then start it again.
The registry keys for other versions of Windows are:
- Windows Vista/7 (32 bit, UAC on) HKCU\Software\Classes\VirtualStore\Machine\Software\SMART Technologies\SMART Board Drivers\BoardX\ IsDoGesture
- Windows Vista/7 (64 bit, UAC on) HKCU\Software\Classes\VirtualStore\Machine\Software\Wow6432Node\SMART Technologies\SMART Board Drivers\BoardX\ IsDoGesture
- Windows Vista/7 (32 bit, UAC off) HKLM\Software\SMART Technologies\SMART Board Drivers\BoardX\ IsDoGesture
- Windows Vista/7 (64 bit, UAC off) HKLM\Software\Wow6432Node\SMART Technologies\SMART Board Drivers\BoardX\ IsDoGesture
Where X is a board number >= 1.
In order to make sure the overlay was functioning properly, I built a test tool that listens for all the xml that got flushed to TCP port 3000 and displays it in a console application. When building applications like this, it’s much more important to have useful logging rather then depending on a debugger.
The SMART Board API required me to listen to messages on the windows message pump then funnel those message up in to the SMART board sdk, which then gets processed and pumped back to me via event handlers. When a touch is received it gets a unique id assigned to it. The Smart board is only capable of handling two touches at a time, so only 2 id’s ever appear. The recorded touches are flushed down a TCP socket 30 frames per second. This means that as a unique touch is moving across the board, the last known x and y coordinates for that touch is what should be flushed down.
The key to capturing touches and drags was wiring up event handlers for the OnXYDown, OnXYUp, and OnXYMove events.
public partial class Shell
{
[DllImport("user32.dll")]
static public extern int RegisterWindowMessageA([MarshalAs(UnmanagedType.LPStr)] string lpString);
int SBSDKMessageID = RegisterWindowMessageA("SBSDK_NEW_MESSAGE");
ISBSDKBaseClass2 Sbsdk;
public Shell()
{
InitializeComponent();
Loaded += (o, e) =>
{
Sbsdk = new SBSDKBaseClass2();
((_ISBSDKBaseClass2Events_Event) Sbsdk).OnXYDown += (x, y, z, pointer_id) =>
{
TouchTrigger.fire(new Down(pointer_id, x, y));
};
((_ISBSDKBaseClass2Events_Event) Sbsdk).OnXYMove += (x, y, z, pointer_id) =>
{
TouchTrigger.fire(new Down(pointer_id, x, y));
};
((_ISBSDKBaseClass2Events_Event) Sbsdk).OnXYUp += (x, y, z, pointer_id) =>
{
TouchTrigger.fire(new Up(pointer_id));
};
var handle = new WindowInteropHelper(this).Handle;
var int_handle = handle.ToInt32();
Sbsdk.SBSDKAttachWithMsgWnd(int_handle, false, int_handle);
Sbsdk.SBSDKSetSendMouseEvents(int_handle, _SBCSDK_MOUSE_EVENT_FLAG.SBCME_NEVER, -1);
HwndSource.FromHwnd(handle).AddHook(new_message);
};
}
IntPtr new_message(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
{
if (Msg == SBSDKMessageID && Sbsdk != null) Sbsdk.SBSDKProcessData();
return IntPtr.Zero;
}
}
The TouchTrigger will fire off the touch to any listening observers.
public class TUIOProtocol : Protocol
{
public TUIOProtocol(double screen_width, double screen_height)
{
this.screen_width = screen_width;
this.screen_height = screen_height;
}
public void record(Touch touch)
{
touches[touch.id] = touch;
}
public void publish_to(Connection connection)
{
connection.send(build_for(connection));
}
Serializable build_for(Connection connection)
{
var xml = new Xml();
xml.add("<OSCPACKET ADDRESS='{0}' PORT='{1}' TIME='{2}'>", connection.ip, connection.port, create_time_stamp());
foreach (var touch in touches.Values)
{
touch.append_header(xml, screen_width, screen_height);
}
xml.add("<MESSAGE NAME='/tuio/2Dcur'>");
xml.add("<ARGUMENT TYPE='s' VALUE='alive' />");
foreach (var touch in touches.Values)
{
touch.append_footer(xml);
}
xml.add("</MESSAGE>");
xml.add("<MESSAGE NAME='/tuio/2DCur'>");
xml.add("<ARGUMENT TYPE='s' VALUE='fseq'/>");
xml.add("<ARGUMENT TYPE='i' VALUE='{0}'/>, sequence.next());
xml.add("</MESSAGE>");
xml.add("</OSCPACKET>");
return xml;
}
double create_time_stamp()
{
return DateTime.Now.Subtract(reference_time).TotalMilliseconds/1000000000;
}
}
The TUIOProtocol stores updates the recorded touch for each unique touch id. When the timer elapses it tells the TUIOProtocol to publish the changes to a TCP Connection. As the xml is built it tells each touch to append it’s own header and footer to the xml. If it’s Down touch then something gets appended. If it’s an UP touch then nothing gets added.
public class Down : Touch
{
public Down(long id, double x, double y)
{
this.id = id;
this.x = x;
this.y = y;
}
public long id { get; private set; }
public void append_header(Xml xml, double screen_width, double screen_height)
{
xml.add("<MESSAGE NAME='/tuio/2Dcur\'>");
xml.add("<ARGUMENT Type='s' VALUE='set' />");
xml.add("<ARGUMENT Type='i' VALUE='{0}' />", id);
xml.add("<ARGUMENT Type='f' VALUE='{0}' />", plot_x(screen_width));
xml.add("<ARGUMENT Type='f' VALUE='{0}' />", plot_y(screen_height));
xml.add("<ARGUMENT Type='f' VALUE='0.0000000' />");
xml.add("<ARGUMENT Type='f' VALUE='0.0000000' />");
xml.add("<ARGUMENT Type='f' VALUE='0.0000000' />");
xml.add("</MESSAGE>");
}
public void append_footer(Xml xml)
{
xml.add("<ARGUMENT TYPE='i' VALUE='{0}' />", id);
}
double plot_x(double screen_width)
{
return x/screen_width;
}
double plot_y(double screen_height)
{
return x/screen_height;
}
double x;
double y;
}
The UP touch does not append anything. It just symbolizes a gesture where the user has lifted their finger off of the touch surface.
public class Up : Touch
{
public Up(long id)
{
this.id = id;
}
public long id { get; private set; }
public void append_header(Xml xml, double screen_width, double screen_height){}
public void append_footer(Xml xml){}
}
And that’s all folks. Developing an application for the SMART board has been fun. I would love to get an opportunity to build a full blown WPF app on either the SMART board or the SMART table. For more info on the SMART Developer Network.
Download Source
In this post I am going to discuss multi-legged transactions.
Multi legged transactions occurs when items are transferred to and from multiple accounts.
For example, If I withdraw $100.00 from my chequing account and put $50.00 in to a retirement
savings account and the other $50.00 in to a utility payment account.
If you would rather just read the source code, I have provided a
download.
In order to successfully complete a multi legged transactions, the total of all exchanges must balance to 0.
It’s important to remember that Accounts don’t just apply to monetary values.
You can have Gas Volume account with a unit of measure in MCF, or an Oil Volume account measured in BOED.
When transferring money from a money account to a gas account,
you are essentially buying gas which means you must convert money in to it’s equivalent amount of gas at current price of gas.
We’ll talk about different strategies of how you can accomplish this.
There are also times when you want to group accounts together to create a hierarchy of accounts.
For example, I could have an expenses account which aggregates entries from a utility payment account,
a tax account, and a food expenses account.
This is a type of Summary Account which can aggregate one or more Accounts.
Accounts at their lowest level are called Detail Accounts.
Detail accounts track a the entries for a single account.
Let’s start with an example.
When transferring funds from one account to another it should increase the balance of the destination account and decrease the balance of the source account.
For this example both of our accounts will use the same currency.
[Concern(typeof(Transaction))]
public class when_transferring_funds_from_one_account_to_another : concern
{
context c = () =>
{
source_account = DetailAccount.New(Currency.CAD);
destination_account = DetailAccount.New(Currency.CAD);
source_account.add(Entry.New<Deposit>(100, Currency.CAD));
};
because of = () =>
{
sut.deposit(destination_account, new Quantity(100, Currency.CAD));
sut.withdraw(source_account, new Quantity(100, Currency.CAD));
sut.post();
};
it should_increase_the_balance_of_the_destination_account = () =>
{
destination_account.balance().should_be_equal_to(new Quantity(100, Currency.CAD));
};
it should_decrease_the_balance_of_the_source_account = () =>
{
source_account.balance().should_be_equal_to(new Quantity(0, Currency.CAD));
};
static DetailAccount source_account;
static DetailAccount destination_account;
}
Take a look at the “because” block in the above code.
We are depositing a quantity of 100 CAD in to one account,
and withdrawing a quantity of 100 CAD from another account.
This transaction balances to zero, so when we post the transaction it shouldn’t have any problems.
Pretty straight forward so far. We’ve made some key design decisions in these tests.
Instead of modeling an account just for money, we are using a Quantity object.
This allows to potentially withdraw 80 CAD from one account and deposit 1 BOED of oil in to another account.
Before jumping in to the Transaction class let’s take a quick peek at Quantity.
Quantity
public class Quantity : IEquatable<Quantity>
{
double amount;
UnitOfMeasure units;
public Quantity(double amount, UnitOfMeasure units)
{
this.units = units;
this.amount = amount;
}
public Quantity plus(Quantity other)
{
return Quantity(amount + other.convert_to(units).amount, units);
}
public Quantity subtract(Quantity other)
{
return Quantity(amount - other.convert_to(units).amount, units);
}
public Quantity convert_to(UnitOfMeasure unit_of_measure)
{
return new Quantity(unit_of_measure.convert(amount, units), unit_of_measure);
}
}
In our current implementation Quantity’s can be added to one another,
and they can be subtracted from one another.
Each quantity represents a single amount of something.
That something is represented as a Unit Of Measure.
For example, 100 Canadian dollars can be represented as a quantity of 100 with a unit of measure of CAD.
1000 BOED of oil can be modeled as a quantity of 1000 with a unit of measure of BOED.
6 MCF of gas can be represented as a quantity of 6 with a unit of measure of MCF.
Each unit of measure can be converted to another unit of measure.
When we add 100 CAD to 1000 BOED we may want the result to be measured in CAD or BOED.
This requires a conversion using the price of oil at the time of conversion.
Let’s talk about one strategy to do this using a exchange rate table.
Unit of Measure
We need a way to sneak in a rate table lookup during runtime,
usually the way we would do this is by pushing in a domain service to use to lookup the current days rate.
What we want to avoid is having our Domain model reaching out to an external third party directly to look up the rates.
But we want to be able to provide a rate table lookup strategy at run time.
public delegate ConversionRation RateTable(UnitOfMeasure unitCurrency, UnitOfMeasure referenceCurrency);
public abstract class SimpleUnitOfMeasure : UnitOfMeasure
{
public double convert(double amount, UnitOfMeasure other)
{
return rate_table(this, other).applied_to(amount);
}
public abstract string pretty_print(double amount);
static RateTable rate_table = (x, y) => ConversionRatio.Default;
static public void provide_rate(RateTable current_rates)
{
rate_table = current_rates;
}
}
The “provide_rate" method allows us to push in a rate lookup,
in a manner that doesn’t couple us to the implementation.
The actual implementation might open up a connection to a remote host and pull down the current rates,
or it might cache the rates and serve them.
Either way this becomes completely open for extension.
We can now drop in different units of measure like BOED, Currency, MCF etc.
public class Currency : SimpleUnitOfMeasure
{
static public readonly Currency USD = new Currency("USD");
static public readonly Currency CAD = new Currency("CAD");
...
Currency(string pneumonic)
{
this.pneumonic = pneumonic;
}
public override string pretty_print(double amount)
{
return "{0:C} {1}".format(amount, this);
}
}
Transaction
Ok now let’s get back on track, we were talking about multi legged transactions.
When we are building a transaction we need a way to record the potential entries before actually posting them to each respective account.
When we deposit or withdraw anything we record Potential transactions.
When we post the transaction we ensure that the balance is zero.
If it’s all good then, we commit each potential transaction to the respective accounts.
public class Transaction
{
Transaction(UnitOfMeasure reference)
{
reference_units = reference;
}
public void deposit(DetailAccount destination, Quantity amount)
{
deposits.Add(Potential<Deposit>.New(destination, amount));
}
public void withdraw(DetailAccount source, Quantity amount)
{
withdrawals.Add(Potential<Withdrawal>.New(source, amount));
}
public void post()
{
ensure_zero_balance();
deposits.Union(withdrawals).each(x => x.commit());
}
void ensure_zero_balance()
{
var balance = calculate_total(deposits.Union(withdrawals));
if(balance == 0) return;
throw new TransactionDoesNotBalance();
}
Quantity calculate_total(IEnumerable<PotentialEntry> potential_transactions)
{
var result = new Quantity(0, reference_units);
potential_transactions.each(x => result = x.combined_with(result));
return result;
}
List<PotentialEntry> deposits = new List<PotentialEntry>();
List<PotentialEntry> withdrawals = new List<PotentialEntry>();
UnitOfMeasure reference_units;
}
Detail Account
Now I skipped a bunch of tests, but you can download the source to check out the rest.
Each potential transaction records the account that is the target of the entry,
and whether it was a deposit or a withdrawal.
public class DetailAccount : Account
{
DetailAccount(UnitOfMeasure unit_of_measure)
{
this.unit_of_measure = unit_of_measure;
}
public void add(Entry new_entry)
{
entries.Add(new_entry);
}
public Quantity balance()
{
return balance(Calendar.now());
}
public Quantity balance(Date date)
{
return balance(DateRange.up_to(date));
}
public Quantity balance(Range<Date> period)
{
var result = new Quantity(0, unit_of_measure);
foreach(var entry in entries.Where(x => x.booked_in(period))
{
result = entry.adjust(result);
}
return result;
}
IList<Entry> entries = new List<Entry>();
UnitOfMeasure unit_of_measure;
}
When the potential transaction is committed it simple adds the equivalent entry to the target account.
When the account calculates the balance it sums up each entry.
Withdrawal entries decrement the amount, and deposits increase the amount.
The balance is returned in the unit of measure that the account manages.
If it’s a monetary account, then a monetary quantity is returned.
Download
I am constantly working towards becoming a better OO practitioner.
To do so I like to practice by solving problems by trying to stay true to the design principles of OO.
My current job is a great source of real world business domains, so this helps with my practicing.
In this post I am going to focus on a specific problem on employee compensation.
If you prefer to just download the source code, I have included it as a
download.
Last year we released a system to the Human Resources department of our company to help them manage the compensation for each employee in the company.
As part of our compensation we are all issued a base salary for the year, a target bonus, and a target LTIP (long term incentive plan.)
The bonus is split in half, and issued to employees in January, and June of each year.
This is called the H1 and H2 bonuses.
The LTIP is also split in half and issued in the spring and fall of each year.
This is known as the spring and fall LTIP.
Bonus are issued in cash, but LTIP’s are issued as grants.
We offer two types of LTIP’s,
one called RTU (restricted trust units) and another called PTU (performance trust units).
For this article I am going to focus on our RTU grants.
When a grant is issued to an employee,
1/3 of the grant will vest on each anniversary of the date that the grant was issued.
For instance, if I was issued a fall LTIP grant of $4500.00 at a unit price of $10.00,
then the following year I would be issued 1/3 of the grants value.
If the price doubles from $10.00/unit to $20.00/unit then I would receive a payout of $3000.00.
When an employee has been working at ARC for 3 years,
then they are considered “fully loaded”,
which means that during either the spring or fall compensation events,
that employee would receive 1/3 of 3 different grants.
So let’s model this.
If an employee can have any where between 0 and 3 grants with unvested units available at any time,
how can we calculate the current value of that employees LTIP.
Let’s start by writing a unit test, and let test driven development guide us.
[Concern(typeof(Compensation))]
public class when_calculating_the_total_unvested_dollars_awarded : concern
{
context c = ()=>
{
grant_date = new DateTime(2009, 09, 15);
value_of_grant = 4500.00;
unit_price = 10.00;
portion_to_issue_at_each_vest = new One<Third>();
frequency = new Annually();
};
because of = () =>
{
Calendar.stop(() => grant_date);
sut.issue_grant(value_of_grant, unit_price, portion_to_issue_at_each_vest, frequency);
Calendar.start();
sut.grant_for(grant_date).change_unit_price_to(20.00);
};
it should_indicate_that_nothing_has_vested_before_the_first_anniversary = () =>
{
sut.unvested_balance(new DateTime(2010, 09, 14)).should_be_equal_to(9000);
};
it should_indicate_that_one_third_has_vested_after_the_first_anniversary = () =>
{
sut.unvested_balance(new DateTime(2010, 09, 15)).should_be_equal_to(6000);
};
it should_indicate_that_two_thirds_has_vested_after_the_second_anniversary = () =>
{
sut.unvested_balance(new DateTime(2011, 09, 15)).should_be_equal_to(3000);
};
it should_indicate_that_the_complete_grant_has_vested_after_the_third_anniversary = () =>
{
sut.unvested_balance(new DateTime(2012, 09, 15)).should_be_equal_to(0);
};
static DateTime grant_date;
static double value_of_grant;
static double unit_price;
static One<Third> portion_to_issue_at_each_vest;
static Annually frequency;
}
In the above set of unit tests, I am focusing on a single employees compensation.
I’ve awarded that compensation a single grant valued at $4500.00 at the time of grant at a
price of $10.00/unit.
In each test I am checking to see that the unvested amount is correct at different times in the future.
With this design I can now see what an employees compensation looks like in the future
and at any point in the past.
This is a form of black box testing, I am testing the expected behavior of a single class.
I don’t really care what the underlying implementation is.
I just want to know that in the end it produces the value that I expect.
This type of testing is my preferred style when working in a domain model, it allows for much easier refactoring, less test maintenance and still preserves the expected behavior.
Compensation
Let’s take a look at the Compensation class to see how we can get these tests passing
and stick to some fundamental object oriented programming principles.
public class Compensation : Visitable<Grant>
{
IList<Grant> grants = new List<Grant>();
public void issue_grant(Money grant_value, UnitPrice price, Fraction portion_to_issue_at_each_vest, Frequency frequency)
{
grants.Add(Grant.New(grant_value, price, portion_to_issue_at_each_vest, frequency));
}
public Grant grant_for(Date date)
{
return grants.Single(x => x.was_issued_on(date));
}
public Money unvested_balance(Date date)
{
var total = Money.Zero;
accept(new AnonymousVisitor<Grant>(grant => total = total.plus(grant.balance(date))));
return total;
}
public void accept(Visitor<Grant> visitor)
{
grants.each(x => visitor.visit(x));
}
}
Compensation is our aggregate root.
Within it’s boundary it creates an instance of Grant via a static factory method.
It implements a Visitable<T> interface to adhere to the interface segregation principle
as well as the open closed principle.
By allowing the Compensation to accept visitors it leaves this class closed for modification
but still for extension.
We can create new implementation of the visitors and pass them to collect the information necessary.
In our calculation we are visiting each Grant and telling it to calculate the balance remaining
as of a particular date.
Notice the message passing, and information hiding.
Compensation doesn’t need to “know” about any of Grants “data” it is invoke specific behaviors
on Grant instead of picking off values from getters.
I prefer not to use getters and setters, not only are they an anti-patterns in object oriented design,
but they help produce brittle software.
Every time you add a getter or worse, setter you are adding a future maintenance cost to your software.
Focus on behavior rather than on data.
Grant
static public Grant New(Money purchase_amount, UnitPrice price, Fraction portion, Frequency frequency)
{
var grant = new Grant
{
issued_on = Calendar.now(),
};
grant.change_unit_price_to(price);
grant.purchase(purchase_amount);
grant.apply_vesting_frequency(portion, frequency);
return grant;
}
There’s a couple of things that happen when we create an instance of Grant.
First we record that date that the grant was issued on, second we record the unit price, then we purchase units, and finally we apply a vesting frequency.
When we record the unit price we are actually tracking the each price change, which allows us to move forward and backwards in time.
History<UnitPrice> price_history = new History<UnitPrice>();
public virtual void change_unit_price_to(UnitPrice new_price)
{
price_history.record(new_price);
}
The history of each price change is record in the generic History.
This will record the date that the change occurred and keeps a stack of these changes.
Again, we are pushing message forward.
We have also wrapped the primitive double type in a UnitPrice class that allows us to extend double
with additional behavior as well as allows to quickly glean the intention rather than the implementation.
If we were to store dollars and units in primitive types, there’s little that blocks us from accidentally adding these two values together.
This is just now how Money and UnitPrice behave with one another.
This relationship now become explicit when we use actual classes.
void purchase(Money amount)
{
units = units.combined_with(current_unit_price().purchase_units(amount));
}
UnitPrice current_unit_price()
{
return unit_price(Calendar.now());
}
UnitPrice unit_price(Date on_date)
{
return price_history.recorded(on_date);
}
When we purchase a certain amount of units, we look up the current unit price, and purchase as many units as we can for the dollars given.
Notice how it’s the UnitPrice class that is calculating the number of Units that can be awarded for a certain amount of Money.
We then combine those Units with the existing number of Units already awarded to this grant.
The Unit Price History allows us to look up the most relevant Unit Price for any given date.
public virtual Money balance()
{
return balance(Calendar.now());
}
public virtual Money balance(Date on_date)
{
return unit_price(on_date).total_value_of(units_remaining(on_date));
}
Units units_remaining(Date on_date)
{
var remaining = Units.Empty;
foreach( var expiration in expirations)
{
remaining = remaining.combined_with(expiration.unvested_units(units, on_date));
}
return remaining;
}
The final balance calculation looks up the unit price for the given date and
calculates the total monetary value for the unit that have not expired.
We iterate through each expiration and accumulate the units that have not vested.
Let’s take a look at how that is done.
Vest
public class Vest
{
Fraction portion;
Date vesting_date;
public Vest(Fraction portion, Date vesting_date)
{
this.portion = portion;
this.vesting_date = vesting_date;
}
public Units unvested_units(Units total_units, Date date)
{
return expires_before(date) ? Units.Empty : total_units.reduced_by(portion);
}
bool expires_before(Date date)
{
return vesting_date.is_before(date);
}
}
Each Vest has a date that the vest occurs.
In our example this happens on each anniversary of the original grant date until each 1/3 has completely vested.
To calculate the unit remaining we check to see if the vest expired before the given date.
If so then 1/3 has expired.
If not then we take the total units available and divide that by 1/3.
We have a Fraction interface so that if in the future the rules need to changes from
1/3 to 1/12 we can accommodate that.
In this post I hope that I have given you an opportunity to see the benefit of object oriented modeling.
By modeling real world business processes as closely to the real thing, we allow for change, in fact we embrace it.
We make the code easy to read and hopefully easy to understand.
The small pieces are easier to digest and get new team members up to speed on the core domain much faster.
The way we name our classes and methods should be intention revealing and mimic the language used in the core business domain.
Focusing on behavior rather than data, allows us to achieve things in a model that a data model simply cannot easily do.
I have done my best to illustrate some of the principles of object oriented design such as “Tell don’t ask”, “Single Responsibility Principle”, “Open/Closed Principle”, “Interface Segregation Principle”.
Download