A week ago I spotted very inspiring video: http://vimeo.com/34522837 "Some Thoughts on Classes After 18 Months of Clojure" from Brian Marick [BM] and found mentions about DCI in the comments.
Brian Marick treats objects as a set of features (traits) which could be used as Roles in UseCase (DCI context) implementation.
In real life even simplest object has millions of properties and features but every time I need only few and at this time I don't care about other features. I can touch and feel (getters) this object and use it (setters and methods). Kind of interface with real implementation and real properties.
Next time I may touch/use same object in different context, the interface may be the same or new, but properties with same name are indeed same properties as I am using exactly same object. Methods should be different (or same? same is easier in case of JavaSctipt), that's I am not sure yet.
So object becomes a container of all such sets of features (traits).
Technically it is pretty easy to implement:
1) define HUGE class and many interfaces with getters/setters and functions. This way is not interesting.
2) Use dynamic features of language and treat each instance as a Hash of properties and methods. Pretty easy in JavaScript.
In C# it may look like this http://houseofbilz.com/archives/2010/05/08/adventures-in-mvvm-my-viewmodel-base/
// Trait here is ViewModelBase from above article: class with Hash and few helper methods Get(), Set(), With(), As()
public class Account: Trait
{
// strongly types property
public double Balance
{
get { return Get(() => Balance, 0.0 /* This is the default value */); }
set { Set(() => Balance, value);}
}
public void Withdraw(double amount) { Balance -= amount; }
public void Deposit(double amount) { Balance += amount; }
}
public class BalanceChangesTrace: Trait
{
// this Balance is mapped to Account via Hash
private double Balance
{
get { return Get(() => Balance); } // it will take value from Hash
}
[DependsUpon("Balance")]
public void LogWhenBalanceChanges()
{
Console.WriteLine("New Balance is {0}", Balance);
}
}
public class OverdraftProtection: Trait
{
// I may repeat private double Balance { get { return Get(() => Balance); } }
// but there is another way to get Balance
[DependsUpon("Balance")] // this method will be called each time Account.Balance is set.
public void LogWhenBalanceChanges()
{
var balance = this.As
if ( balance < 0 ) throw new ApplicationException("insufficient funds");
// or even untyped, bad practice in general but may be handy sometimes
// double balance = (double)this.Get("Balance")
}
}
public class AccountTransferUseCase: Context
{
public void Transfer(Account from, Account to, double amount)
{
var _from = from
.With
.With
var _to = to
.With
Console.WriteLine("Withdrawing " + amount);
_from.Withdraw(amount);
Console.WriteLine("Depositing " + amount);
_to.Deposit(amount);
}
}
var acc1 = new Account();
acc1.Balance = 100.0;
var acc2 = new Account();
(new AccountTransferUseCase()).Transfer(from: acc1, to: acc2: amount: 20.0);
With all these dynamics it may be slow but for most software it is fast enough.
[end]
No comments:
Post a Comment