Tuesday, May 1, 2012

DCI #2



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().Balance;

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: