|
[
bamboo
]
15:46, Friday, 27 February 2009
After a long wait boo is finally available as a programming language category on Source Forge. Now go and improve the categorization of your boo projects.
[
bamboo
]
13:25, Monday, 16 February 2009
Motivations:
Environment Based Programming* is a design pattern founded on a very simple principle:
This principle can be completely captured in C# with the following API:
namespace EnvironmentBasedProgramming
{
public delegate void Code();
public interface IEnvironment
{
Need Provide<Need>();
}
public static class Environments
{
/// <summary>
/// Executes code in a given environment.
/// </summary>
public static void With(IEnvironment environment, Code code);
}
/// <summary>
/// Used by code to fulfill its needs.
/// </summary>
public static class My<Need>
{
public static Need Instance { get; }
}
}
To make it all concrete I'll use Martin Fowler's naive example specially for the contrast with the dependency management approaches he documents in his article. The MovieLister component provides a list of movies directed by a particular director. In order to fulfill its contract it needs the list of all known movies, something that a MovieFinder service would provide:
interface IMovieFinder
{
IEnumerable<Movie> FindAll();
}
class MovieLister
{
public IEnumerable<Movie> MoviesDirectedBy(string directorName)
{
var movies = My<IMovieFinder>.Instance.FindAll();
foreach (var movie in movies)
if (movie.Director == directorName)
yield return movie;
}
}
Notice how the code express its needs using the My idiom. MovieLister can now be executed in a suitable environment using the With primitive:
Environments.With(environment, delegate
{
foreach (var movie in new MovieLister().MoviesDirectedBy("Terry Jones"))
Console.WriteLine(movie.Title);
});
A suitable environment in this case would have to deliver a valid IMoveFinder instance upon request. The following implementation should suffice:
class DummyMovieFinder : IMovieFinder
{
public IEnumerable<Movie> FindAll()
{
yield return new Movie {Director = "Terry Jones", Title = "Erik The Viking"};
yield return new Movie {Director = "Terry Gilliam", Title = "Fear and Loathing in Las Vegas"};
}
}
The missing piece in the puzzle is the final EBP building block - ClosedEnvironment:
public class ClosedEnvironment : IEnvironment
{
private readonly object[] _bindings;
public ClosedEnvironment(params object[] bindings)
{
_bindings = bindings;
}
public T Provide<T>()
{
foreach (var binding in _bindings)
if (binding is T)
return (T) binding;
return default(T);
}
}
Which allows the environment for the example to be defined as:
var environment = new ClosedEnvironment(new DummyMovieFinder());
Component activation and lifetime are not aspects dealt directly with by EBP and are better treated as different environment strategies (one can easily imagine an environment that automatically instantiates components based on naming conventions or metadata). The complete listings follow. The example:
namespace EnvironmentBasedProgramming.NaiveExample
{
using System;
using System.Collections.Generic;
class Movie
{
public string Title { get; set; }
public string Director { get; set; }
}
interface IMovieFinder
{
IEnumerable<Movie> FindAll();
}
class MovieLister
{
public IEnumerable<Movie> MoviesDirectedBy(string directorName)
{
var movies = My<IMovieFinder>.Instance.FindAll();
foreach (var movie in movies)
if (movie.Director == directorName)
yield return movie;
}
}
class Program
{
static void Main(string[] args)
{
var environment = new ClosedEnvironment(new DummyMovieFinder());
Environments.With(environment, delegate
{
PrintMoviesDirectedBy("Terry Jones");
});
}
private static void PrintMoviesDirectedBy(string directorName)
{
foreach (var movie in new MovieLister().MoviesDirectedBy(directorName))
Console.WriteLine(movie.Title);
}
class DummyMovieFinder : IMovieFinder
{
public IEnumerable<Movie> FindAll()
{
yield return new Movie {Director = "Terry Jones", Title = "Erik The Viking"};
yield return new Movie {Director = "Terry Gilliam", Title = "Fear and Loathing in Las Vegas"};
}
}
}
}
The minimalist EBP framework written for this article:
namespace EnvironmentBasedProgramming
{
using System;
public delegate void Code();
public interface IEnvironment
{
Need Provide<Need>();
}
public static class Environments
{
/// <summary>
/// Executes code in a given environment.
/// </summary>
public static void With(IEnvironment environment, Code code)
{
IEnvironment previous = _environment;
_environment = environment;
try
{
code();
}
finally
{
_environment = previous;
}
}
private static IEnvironment _environment;
internal static IEnvironment Current
{
get { return _environment; }
}
}
/// <summary>
/// Used by code to fulfill its needs.
/// </summary>
public static class My<Need>
{
public static Need Instance
{
get
{
var current = Environments.Current;
if (current == null)
throw new InvalidOperationException("No environment to provide '" + typeof(Need) + "'.");
return current.Provide<Need>();
}
}
}
public class ClosedEnvironment : IEnvironment
{
private readonly object[] _bindings;
public ClosedEnvironment(params object[] bindings)
{
_bindings = bindings;
}
public T Provide<T>()
{
foreach (var binding in _bindings)
if (binding is T)
return (T) binding;
return default(T);
}
}
}
In a future article I'll explore environment chaining and convention based service discovery. Thoughts? * or to use a name more to the style of Martin Fowler: Dynamically Scoped Service Locator (not to be confused with Dynamic Service Locator)
[
bamboo
]
08:37, Thursday, 12 February 2009
... What do I need to do to get it to work on Windows? I've got mono + eclipse + rcp + monolipse installed. Eclipse's DMONO_HOME variable points the right place. I've got Boo 0.9 in a folder, but I'm not sure if it's the right place or if I need to do anything else. ... What's missing? --Søren, February 12, 2009 06:21 AM Thanks for the question. The eclipse plugins expects the boo assemblies to be in the mono GAC and it also expects to find the boo compiler under $MONO_HOME/lib/boo, something that can be easily arranged by running nant in the boo source folder (yes, windows users need a source distro or a svn checkout for now): $ nant install -D:mono.prefix=c:/dotnet/mono-2.2 Enjoy!
[
bamboo
]
08:04, Saturday, 7 February 2009
Thanks, Andrés. The song is Take Five. I love it.
Thanks, Cedric. I prefer /usr/local for my mono version and /usr for the system's mono version. Configuration via user interface is not yet implemented and I couldn't find the patch here, I hope that now that it's out there more people will contribute and this project might even realize its full potential. For now you need to start eclipse passing the MONO_HOME system property like this:
eclipse -data /path/to/workspace -vmargs -DMONO_HOME=/usr
The same will work on windows. Enjoy!
[
bamboo
]
16:12, Friday, 6 February 2009
Summing up:
Enjoy! |