I will talk about some of the things I learned from Jean-Paul S. Boodhoo's training application, NothinButDotNetStore.
One thing to notice is that he did not use any framework for the common patterns that are ussualy found in enterprise applications, instead he implemented simple but efficient solutions for each of them. Most of the communication is done through interfaces, which is a great object oriented programming lesson.
This post will cover only a small percent of what this code has to offer, so I suggest you get the source using a SVN client and take your time to review in detail.
DataAccess: He did an implementation of a data access pattern (subdirectories Core and Mapping).
Domain:
Great examples of the visitor pattern:
public interface IValueReturningVisitor<ValueToReturn, T> : IVisitor<T>
{
ValueToReturn GetResult();
}
public class AllItemsInCartVisitor : IValueReturningVisitor<IEnumerable<ICartItem>, ICartItem>
{
private IRichList<ICartItem> aggregated;
public AllItemsInCartVisitor()
{
aggregated = new RichList<ICartItem>();
}
public IEnumerable<ICartItem> GetResult()
{
return aggregated;
}
public void Visit(ICartItem item)
{
aggregated.Add(item);
}
}
private IRichList<ICartItem> items;
public IEnumerable<ICartItem> AllItems()
{
return items.GetResultOfVisitingAllItemsWith(new AllItemsInCartVisitor());
}
Instead of returning a null when a CartItem is not found, a NonExistantCartItem object is returned (which also implements ICartItem):
private ICartItem found;
...
return found ?? NonExistantCartItem.Instance;
Infrastructure:
You can find a simple and elegant implementation of a dependency injection mechanism.
Also, check out these cool extensions for IEnumerable which go hand in hand with the visitor classes:
public static class EnumerableExtensionGateway
{
public static void VisitAllItemsUsing<T>(this IEnumerable<T> items,IVisitor<T> visitor)
{
new EnumerableActions<T>(items).VisitAllItemsUsing(visitor);
}
public static Result GetResultOfVisitingAllItemsWith<T,Result>(this IEnumerable<T> items,IValueReturningVisitor<Result,T> visitor)
{
return new EnumerableActions<T>(items).GetResultOfVisitingAllItemsWith(visitor);
}
public static IEnumerable<T> AllSatisfying<T>(this IEnumerable<T> items,ISpecification<T> specification)
{
return new EnumerableActions<T>(items).AllMatching(specification);
}
public static IEnumerable<Output> MapAllUsing<T,Output>(this IEnumerable<T> itemsToMap,IMapper<T,Output> mapper)
{
return new EnumerableActions<T>(itemsToMap).MapAllUsing(mapper);
}
}
public interface IEnumerableActions<T>
{
void VisitAllItemsUsing(IVisitor<T> visitor);
Result GetResultOfVisitingAllItemsWith<Result>(IValueReturningVisitor<Result, T> visitor);
IEnumerable<T> AllMatching(ISpecification<T> specification);
IEnumerable<Output> MapAllUsing<Output>(IMapper<T, Output> mapper);
}
public class EnumerableActions<T> : IEnumerableActions<T>
{
private IEnumerable<T> itemsToActOn;
public EnumerableActions(IEnumerable<T> itemsToActOn)
{
this.itemsToActOn = itemsToActOn;
}
public void VisitAllItemsUsing(IVisitor<T> visitor)
{
foreach (T t in itemsToActOn)
{
visitor.Visit(t);
}
}
public Result GetResultOfVisitingAllItemsWith<Result>(IValueReturningVisitor<Result,T> visitor)
{
VisitAllItemsUsing(visitor);
return visitor.GetResult();
}
public IEnumerable<T> AllMatching(ISpecification<T> specification)
{
foreach (T t in itemsToActOn)
{
if (specification.IsSatisfiedBy(t)) yield return t;
}
}
public IEnumerable<Output> MapAllUsing<Output>(IMapper<T,Output> mapper)
{
foreach (T t in itemsToActOn)
{
yield return mapper.MapFrom(t);
}
}
}
And add the RichList, which is integrated nicely with the rest of the patterns:
public class RichList<T> : List<T>, IRichList<T>
{
public RichList(IEnumerable<T> collection) : base(collection)
{
}
public RichList(int capacity) : base(capacity)
{
}
public RichList()
{
}
public IEnumerable<Output> MapAllUsing<Output>(IMapper<T, Output> mapper)
{
return ConvertAll<Output>(mapper.MapFrom);
}
public new IRichList<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
{
return new RichList<TOutput>(base.ConvertAll(converter));
}
public new IRichList<T> FindAll(Predicate<T> match)
{
return new RichList<T>(base.FindAll(match));
}
public new IRichList<T> GetRange(int index, int count)
{
return new RichList<T>(base.GetRange(index, count));
}
public void VisitAllItemWith(IVisitor<T> visitor)
{
this.foreach(visitor.Visit);
}
public Result GetResultOfVisitingAllItemsWith<Result>(IValueReturningVisitor<Result, T> visitor)
{
VisitAllItemWith(visitor);
return visitor.GetResult();
}
}