in

 

Jimmy Bogard

Assistant to the assistant to the regional manager

August 2008 - Posts

  • One of those days

    I had one of those days the other day.  Not quite a case of the Mondays, but one where I thought maybe I should just go home and start over.  If you're trying to get this test to pass:

    [Test]
    public void Good_times()
    {
        Regex.IsMatch(@"^\d*$", "123456").ShouldBeTrue();
    }
    

    Then maybe it's time to call it a day.  You really don't want to know how many regex tutorial and expression evaluator sites I went to on this one.  Only to find out my method arguments were backwards.

    Posted Aug 30 2008, 10:35 AM by bogardj with 3 comment(s)
    Filed under:
  • Strategies and discriminators in NHibernate

    I recently posted about enumeration classes, and how I like to use them as a sort of "Enumerations with behavior".  Not every enumeration should be replaced with a class, but that pattern helps quite a bit when I find a lot of switch statements concerning my enumeration.  Often, these strategies come from data.  For example, I recently had a situation where individual Product instances had different PricingStrategies.  Each PricingStrategy depended on specific rules around Product data, so the Product data owners would decide what PricingStrategy each Product was eligible for.

    This was nice, as it seemed like they pretty much flipped a coin on how they wanted to do pricing.  In any case, when the PricingStrategy is data-driven, it leaves a lot of flexibility on the business side to change pricing as they need to, with full confidence that each PricingStrategy type could handle its own pricing rules.

    To handle this, we went with something very close to the enumeration classes I described earlier.  First, here's our Product class:

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal UnitPrice { get; set; }
        public int UnitsInStock { get; set; }
    
        public Category Category { get; set; }
        public PricingStrategy PricingStrategy { get; set; }
    
        public decimal GetPrice()
        {
            decimal discountAmount = (UnitPrice * PricingStrategy.GetDiscountPercentage(this) / 100m);
    
            return UnitPrice - discountAmount;
        }
    }
    

    Note that the GetPrice uses the PricingStrategy to calculate the final price.  The PricingStrategy (really, a discount strategy in this example) had various means of determining what he discount percentage would be.  It used something of a double-dispatch to calculate this, passing the Product into the PricingStrategy.  Our PricingStrategy class is our old enumeration class:

    public abstract class PricingStrategy
    {
        private int _id;
        private string _name;
        private string _displayName;
    
        public static readonly PricingStrategy FullPrice = new FullPriceStrategy(1);
        public static readonly PricingStrategy LowStock = new LowStockDiscountPriceStrategy(2);
    
        private PricingStrategy() {}
    
        protected PricingStrategy(int id)
        {
            _id = id;
        }
    
        public int Id
        {
            get { return _id; }
        }
    
        public string Name
        {
            get { return _name; }
        }
    
        public string DisplayName
        {
            get { return _displayName; }
        }
    
        public override bool Equals(object obj)
        {
            var otherValue = obj as PricingStrategy;
    
            if (otherValue == null)
            {
                return false;
            }
    
            return otherValue.Id.Equals(Id);
        }
    
        public override int GetHashCode()
        {
            return _id.GetHashCode();
        }
    
        public abstract decimal GetDiscountPercentage(Product product);
    
        private class FullPriceStrategy : PricingStrategy
        {
            private FullPriceStrategy() { }
    
            public FullPriceStrategy(int id)
                : base(id)
            {
            }
    
            public override decimal GetDiscountPercentage(Product product)
            {
                return 0.0m;
            }
        }
    
        private class LowStockDiscountPriceStrategy : PricingStrategy
        {
            private LowStockDiscountPriceStrategy() { }
    
            public LowStockDiscountPriceStrategy(int id)
                : base(id)
            {
            }
    
            public override decimal GetDiscountPercentage(Product product)
            {
                if (product.UnitsInStock < 10)
                    return 10m;
    
                return 0m;
            }
        }
    }
    

    It's rather long, but the basic idea is that the only public type is PricingStrategy.  The implementation types are completely private to anyone outside the abstract PricingStrategy.  Anyone wanting to use a specific PricingStrategy can use the static readonly fields, which again hide the private types.  No one refers to anything but the base PricingStrategy class.

    The PricingStrategy values are stored in the database, here is what the tables look like:

    In the database, each Product has a foreign key reference to the PricingStrategies table, which has only two entries right now.  Each PricingStrategyID corresponds to the ID used in the static fields defined in the PricingStrategy class.  That way, no matter if a user choose to pull from the database or from the static fields, each Value Object is equal to the other.  The corresponding NHibernate mapping for PricingStrategy would be:

    <class name="PricingStrategy" table="PricingStrategies">
        <id name="Id" column="PricingStrategyID" type="int">
            <generator class="assigned" />
        </id>
        <discriminator column="PricingStrategyID" />
        <property name="Name" />
    
        <subclass discriminator-value="1" extends="PricingStrategy" name="PricingStrategy+FullPriceStrategy" />
        <subclass discriminator-value="2" extends="PricingStrategy" name="PricingStrategy+LowStockDiscountPriceStrategy" />
    </class>
    

    Note that each subclass is called out with the <subclass> element, with the corresponding discriminator value.  The specific strategy is specified with the "name" attribute.  At load time, NHibernate will check the discriminator column value and pull up the corresponding subclass that matches that found value.  When the Product table has a "1" for the PricingStrategyID, NHibernate pulls up the FullPriceStrategy, and so on.

    So how might this look in actual client code?  Here are a couple of tests that pull out two different Product entries from the database, each with a different PricingStrategy:

    [Test]
    public void Products_with_full_price_strategy_should_charge_full_price()
    {
        ISession session = GetSession();
        var product = session.Get<Product>(22);
    
        product.PricingStrategy.ShouldEqual(PricingStrategy.FullPrice);
        product.GetPrice().ShouldEqual(product.UnitPrice);
    }
    
    [Test]
    public void Products_with_low_stock_price_strategy_should_charge_discount_when_stock_is_low()
    {
        ISession session = GetSession();
        var product = session.Get<Product>(8);
    
        product.UnitsInStock.ShouldBeLessThan(10);
        product.PricingStrategy.ShouldEqual(PricingStrategy.LowStock);
        product.GetPrice().ShouldBeLessThan(product.UnitPrice);
    }
    

    The first Product uses the FullPrice strategy (proven by the first assertion).  Its price should simply equal the UnitPrice.  In the second test, that Product uses the LowStock pricing strategy.  That strategy charges a discount when the stock gets low (maybe it's a blue-light special?).  Its final price should be lower than the original UnitPrice, and this is verified by our unit test above.

    When strategies or enumeration classes aren't completely code-driven, and are data driven as was in this case, NHibernate discriminators provide a nice means of a "strategy factory".  Each individual strategy instance is supplied with a distinct discriminator value, allowing me to add new strategy/enumeration class implementations as needed.  The specific implementation is data-driven, which makes our product data owners happy.

  • Parameter lists in NHibernate

    Occasionally I need to return a set of entites that match a collection of parameters.  In SQL, I would use the "IN" clause, then manually create each parameter in ADO.NET.  With NHibernate, that's not necessary anymore.  NHibernate has built-in capabilities for a collection parameter, creating all the necessary ADO.NET parameters behind the scenes.

    For example, suppose my database has the following structure (from Northwind):

    What I'm trying to do is to load up all Products for a certain set of Categories.  My Product and Category class are nothing interesting:

    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Category Category { get; set; }
    }
    

    I didn't bother mapping all of the properties, just the ones for the classes defined above.  Here are the mapping files:

    <class name="Category" table="Categories">
        <id name="Id" column="CategoryID" type="int">
            <generator class="assigned" />
        </id>
        <property name="Name" not-null="true" column="CategoryName" />
    </class>
    
    <class name="Product" table="Products">
        <id name="Id" column="ProductId" type="int">
            <generator class="assigned" />
        </id>
        <property name="Name" not-null="true" column="ProductName" />
        <many-to-one name="Category" column="CategoryID">
        </many-to-one>
    </class>
    

    When using plain HQL, I need to use the SetParameterList method on the IQuery object:

    [Test]
    public void Should_get_products_by_categories_correctly_using_HQL()
    {
        ISession session = GetSession();
        string hql = @"from Product p
                       where p.Category in (:categories)";
    
        var categoriesToSearch = new[] {new Category {Id = 1}, new Category {Id = 2}};
    
        var query = session.CreateQuery(hql);
        query.SetParameterList("categories", categoriesToSearch);
    
        var products = query.List<Product>();
    
        products.Count.ShouldEqual(24);
    }
    

    When using the Criteria API, I'll need to use the InExpression:

    [Test]
    public void Should_get_products_by_categories_correctly_using_criteria()
    {
        ISession session = GetSession();
        var criteria = session.CreateCriteria(typeof(Product));
    
        var categoriesToSearch = new[] {new Category {Id = 1}, new Category {Id = 2}};
        criteria.Add(new InExpression("Category", categoriesToSearch));
        var products = criteria.List<Product>();
    
        products.Count.ShouldEqual(24);
    }
    

    When I execute both of these, NHibernate produces the correct IN expression for each.  Unfortunately, I am ashamed to say I initially tried to create the parameter list myself.  Next time, I'll know better.

  • Services in Domain-Driven Design

    Services are first-class citizens of the domain model.  When concepts of the model would distort any Entity or Value Object, a Service is appropriate.  From Evans' DDD, a good Service has these characteristics:

    • The operation relates to a domain concept that is not a natural part of an Entity or Value Object
    • The interface is defined in terms of other elements in the domain model
    • The operation is stateless

    Services are always exposed as an interface, not for "swappability", testability or the like, but to expose a set of cohesive operations in the form of a contract.  On a sidenote, it always bothered me when people say that an interface with one implementation is a design smell.  No, an interface is used to expose a contract.  Interfaces communicate design intent, far better than a class might.

    But most examples I see of Services are something trivial, such as IEmailSender.  But Services exist in most layers of the DDD layered architecture:

    • Application
    • Domain
    • Infrastructure

    An Infrastructure Service would be something like our IEmailSender, that communicates directly with external resources, such as the file system, registry, SMTP, database, etc.  Something like NHibernate would show up in the Infrastructure.

    Domain services are the coordinators, allowing higher level functionality between many different smaller parts.  These would include things like OrderProcessor, ProductFinder, FundsTransferService, and so on.  Since Domain Services are first-class citizens of our domain model, their names and usages should be part of the Ubiquitous Language.  Meanings and responsibilities should make sense to the stakeholders or domain experts.

    In many cases, the software we write is replacing or supplementing a human's job, such as Order Processor, so it's often we find inspiration in the existing business process for names and responsibilities.  Where an existing name doesn't fit, we dive into the domain to try and surface a hidden concept with the domain expert, which might have existed but didn't have a name.

    Finally, we have Application Services.  In many cases, Application Services are the interface used by the outside world, where the outside world can't communicate via our Entity objects, but may have other representations of them.  Application Services could map outside messages to internal operations and processes, communicating with services in the Domain and Infrastructure layers to provide cohesive operations for outside clients.  Messaging patterns tend to rule Application Services, as the other service layers don't have a reference back out to the Application Services.  Business rules are not allowed in an Application Service, those belong in the Domain layer.

    In top-down design, we typically start from the Application or Domain Service, defining the actual interface clients use, then use TDD to drive out the implementation.  As we're always starting from the client perspective with actual client scenarios, we get a high degree of confidence that what we're building will create success and add value.  When stories are vertical slices of functionality, this is fairly straightforward, at least mechanically so.

    I used to make the mistake of dismissing Services as a necessary evil, confined to the Infrastructure layer.  Through more reading and conversation through our recent Austin DDD Book Club, I've started to realize the potential the Application and Domain services have in creating a well-designed model.

  • DDD, Repositories and ORMs

    One of the confusing aspects of those new to DDD is the concept of a Repository.  From Fowler's Patterns of Enterprise Application Architecture, a Repository:

    Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

    Paraphrasing the various DDD sources, a Repository provides the ability to obtain a reference to an Aggregate root.  Not Entity, Value Object, but Aggregate root.  Each Save operation encapsulates the entire operation for saving a single Root and all of its child entities.  For example, given the model of a Root Person entity with child Address entities, a Save operation will save Person and Address, all in one operation, from the perspective of the client of the Repository.

    From the client perspective, how an object is persisted is unimportant.  From the developer perspective, persistence is very important!  Many who follow DDD choose to use various ORMs to provide the persistence logic inside the Repository.

    Because DDD does not prescribe a persistence technology, nor even a storage medium, using an ORM like NHibernate does not indicate you doing DDD.  Conversely, doing DDD does not predestine you into an ORM technology like NHibernate.  I could use the Cargo Cult metaphor, were it not for the Cargo Cult of folks using the Cargo Cult metaphor.

    You can do DDD with stored procedures.  You can create Repositories for in-memory databases, all the LINQ implementations (including EF).  A Repository is a pattern, not a technology prescription.  It's far more important to learn the concepts than jump to a technology, that's the short bath to a bad experience.

  • On good design and defining success

    This is for the most part a reaction to conversations on design:

    Believe it or not, I largely agree with all of these posts.  The conversation originally started around TypeMock (and IoC before that).

    Missing from all these discussions, and hidden in the intent of comments, is the real discussion: defining and creating success.

    Defining success

    The traditional definition for success in the software space is:

    Completed on time, on budget, with all features and functions as originally specified

    Variations of this definition exist, mostly around "on time and under budget".  But anyone in the software in any length of time has seen far different definitions of success.  An on-time project never used is not very successful.  A late project that users love is very successful.  Defining success strictly in terms time-to-market and functionality delivered as promised still won't lead to happy users.  Sounds like our definition of success is a little off.

    I personally like James Shore's definition of success, with three facets:

    • Personal success
    • Technical success
    • Organizational success

    Total success is achieved by delivering value in these three areas.  If we achieve organizational success but lose our minds in the process in an arcane system, we've missed complete success.  Not all projects are capable of complete success, but Agile methodologies focus on striving for these three facets of success.  Namely:

    • Delivering what the customer wants and needs
    • Creating software that's both fun to write and fun to work with
    • Creating an inclusive, collaborative, and exciting atmosphere where individuals feel free to question and contribute

    That isn't to say that these aspects don't come without Agile methodologies, but that Agile is intended to deliver this type of success.

    On an old team of mine, a team member got an email that some error count exceeded its limit.  It was some throwaway application that they wrote years ago, but was still providing value.  As no one maintained it, no one asked to change it, but it still did its job and created its little pocket of success (that not many would have predicted).

    Organizational value is defined by the business, and includes increased revenue, decrease costs, and many other items that can't be measured as directly as dollars and cents.  Sometimes the value needed is a small application, but sometimes value and success requires something else.

    So where does good design come into play into all of this?

    Enabling success

    Good design such as those espoused in the SOLID principles and reinforced through TDD, as well as the other XP practices, can enable success on certain projects.  In other projects, as Jacob pointed out, these principles don't matter as much.  The problem is where to know when to make the transition to "good design" from "good enough" design.

    In our projects, prototyping and "spiking" is common.  This is where we throw out any design principles and try to prove a concept.  Once we're done from a time-boxed episode, we'll take what we learn (throw it away Pragmatic Programmer-style), and incorporate the concepts into our project, using the principles and values we normally follow.  What we do the second time around invariably improves on the original throwaway concept, but gave us a more complete knowledge.

    As we design, we're always mindful of creating success.  I create success by trying to create a sustainable software ecosystem, where the rate of change stays high and level, ready to move in the face of changing business conditions and new information.  However, I'm on projects where I don't know the end state, ideal for Agile, and I optimize for change.  Software ecosystems that aren't optimized for change resist it at every turn.  Such projects are easy the first release, but every release after delivers less and less.

    This isn't "ivory tower", but a recognition of a simple fact: change is inevitable.  Unless it isn't.  But as software professionals, we have to recognize when the application transitions from simple to complex, and change our tactics as necessary.

    In the end, users don't care about design directly.  They want value, they want success.  Good design can enable success, as it allows us to change software more quickly and easily than otherwise.  For complex or changing systems, users care about design, they just might not know it.

    Posted Aug 19 2008, 08:27 PM by bogardj with 5 comment(s)
    Filed under: ,
  • Convention, configuration and WCF

    Convention is something that's fairly lacking in WCF.  Here's what I'd like to do:

    [ServiceContract]
    public interface ICustomerSearch
    {
        Customer FindCustomerByName(string name);
    }
    
    public class Customer
    {
        public string Name { get; set; }
        public CustomerType Type { get; set; }
    }
    
    public enum CustomerType
    {
        Good,
        Canadian
    }
    

    I understand maybe just that one attribute.  Here's what I'm forced to do:

    [ServiceContract]
    public interface ICustomerSearch
    {
        [OperationContract]
        Customer FindCustomerByName(string name);
    }
    
    [DataContract]
    public class Customer
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public CustomerType Type { get; set; }
    }
    
    public enum CustomerType
    {
        Good,
        Canadian
    }
    

    All those noisy attributes, none adding any information nor functionality.  What's consistently bothered me the most is how much configuration is necessary to get straightforward scenarios working.  I'm tired of opting-in to every single piece of information on the contract side.  I want to point to an interface, and just let the framework figure out the rest.

    On the plus side, lots of vendors recognized this and created products on top of WCF to make it usable.  Good for them.

    Posted Aug 18 2008, 07:56 PM by bogardj with 8 comment(s)
    Filed under:
  • Austin DDD Book Club wrapped up

    The last Austin DDD Book Club meeting came and went today, finishing up with the last couple of chapters in Evans' excellent Domain-Driven Design book (covering Chapter 17, Bringing the Strategy Together and the Conclusion).  I want to thank our hosts for the meetings, both Drilling Info and Dovetail Software.  They both provided great meeting spaces that allowed some great discussions to take place.  Although I think we really confused the office assistants with a bunch of geeks showing up every couple of weeks, each with a big blue book in hand.

    Talks about creating an Austin DDD Book Club started back in November, and we finally had our first meeting a couple months later in January.  Seven months later, we're finally done, meeting every two weeks with one or two breaks.  Although I've owned the book for three years or so, this was the third time I've read it, but only the first I've made it all the way through.

    I'm very glad I did, as the last few chapters concerning large-scale structure and distillation provided some key insights for applying DDD over the long run.  Most DDD discussions revolve around Entities, Value Objects, Repositories and such.  That's only the first third of the book!

    It was great to finally dive deeply into the later chapters, and even more valuable, discuss these concepts with others, each with varying experience with DDD.  I've only begun to grasp some of the later concepts, specifically in the Strategic Design and Deeper Insight sections.  These sections talk about DDD beyond one team, and beyond applying it for localized domains.  Now that I'm starting to see projects that are beyond forms-over-data apps (where DDD still shines), these later concepts become much more relevant, and critical.

    It was also refreshing in the conclusion to hear Evans' follow up with the examples he used in the book.  All the examples he used were based on real-world experiences, and he went back and followed up to see how the final picture wound up.  Not surprisingly, all examples wound up in many different places for a variety of reasons.

    It's always strange to me when talking with some pro-EF folks, that think DDD people are fixated on NHibernate.  Nowhere that I can find in Evans' book do I find any mention of any kind of ORM solution, excluding things like EJBHome.  Which makes the conversations about tools rather pointless, as folks doing DDD just find tools that match the principles and patterns laid out in Evans' book (as well as other earlier books).  When I hear things like "NHibernate Mafia", it's both puzzling and insulting.  I just don't care about the tool, it doesn't matter, I just want something with the least total friction.

    So where do we go from now?  The Austin DDD Book Club was a lot of fun, but a lot of work, and I feel that its format left some folks out.  I've been asked a few times if we're doing it again.  Right now, we have no plans for another DDD book club, but almost surely something in the similar vein of continuous improvement through community events.  That's not to stop someone else from starting up another one!

    Thanks again to our hosts and all the attendees, for the great discussions and learning that took place.

  • Deferred execution gotchas

    I was trying to be clever the other day and try to chain assertions on an array:

    [Test]
    public void This_does_not_work()
    {
        var items = new[] {1, 2, 3, 4, 5};
        items
            .Each(x => x.ShouldBeLessThan(0))
            .Each(x => x.ShouldBeGreaterThan(10));
    }
    

    I did this by writing an Each extension method that I've seen in about a hundred different variations.  However, mine was a special implementation, and special as in it didn't quite work as intended:

    public static IEnumerable<T> Each<T>(this IEnumerable<T> items, Action<T> action)
    {
        foreach (var item in items)
        {
            action(item);
            yield return item;
        }
    }
    

    Clever, clever, I iterate over the items, performing the action for each item, and finally yielding the item back again (for further processing).  To my surprise, the above test passed (when it should have failed).

    After putting in some Debug.WriteLines, I found that none of the actions were ever executed.  This is because of deferred execution.  With deferred execution, the operations aren't executed until you try and iterate over the results.

    You can try and iterate over the results by doing a foreach, or calling another method that does something similar like ToArray().  Here's the modified test that fails as expected:

    [Test]
    public void This_fails_as_expected_but_quite_ugly()
    {
        var items = new[] {1, 2, 3, 4, 5};
        items
            .Each(x => x.ShouldBeLessThan(0))
            .Each(x => x.ShouldBeGreaterThan(10))
            .ToArray();
    }
    

    It works, but now I have to call a ToArray method to force an iteration over the collection.  Ugly, as it obscures the intent of the test with technical details of deferred execution.

    I could bypass deferred execution by not using custom iterators:

    public static IEnumerable<T> Each<T>(this IEnumerable<T> items, Action<T> action)
    {
        foreach (var item in items)
        {
            action(item);
        }
    
        return items;
    }
    

    This works just fine, with no need to get clever with a custom iterator (the yield return business).  If I decided to get even more clever, and I wanted to keep the deferred execution for whatever reason, I can add yet another extension method:

    public static void Iterate<T>(this IEnumerable<T> items)
    {
        foreach (var item in items)
        {
        }
    }
    

    And now I can be more explicit in my tests that I need to iterate the results:

    [Test]
    public void This_does_work_and_is_not_so_ugly()
    {
        var items = new[] {1, 2, 3, 4, 5};
        items
            .Each(x => x.ShouldBeLessThan(0))
            .Each(x => x.ShouldBeGreaterThan(10))
            .Iterate();
    }
    

    This test fails as expected, without needing to do a ToArray call that obscures intent.

    It turns out that I'm far more likely to do something like a map or reduce than just performing an action on a collection.  I'm doing a lot more Func than Action in real-world code.  Another example that clever is not always simple.

    Posted Aug 13 2008, 08:08 AM by bogardj with 3 comment(s)
    Filed under: ,
  • Enumeration classes

    A question came up on the ALT.NET message board asking whether Value Objects should be used across service boundaries.  Of course, the conversation took several detours, eventually coming to the question, "what do you do about enumerations crossing service boundaries."  I'm still ignoring that question, but I've found different ways to represent enums in my model.  Now, enums are just fine in lots of scenarios, but quite poor in others.  There are other options I like to use when enumerations break down, and many times in the domain model, I go straight to the other option.

    For example, I've seen quite a few models like this:

    public class Employee
    {
        public EmployeeType Type { get; set; }
    }
    
    public enum EmployeeType
    {
        Manager,
        Servant,
        AssistantToTheRegionalManager
    }
    

    The problem with a model like this is it tends to create lots of switch statements:

    public void ProcessBonus(Employee employee)
    {
        switch(employee.Type)
        {
            case EmployeeType.Manager:
                employee.Bonus = 1000m;
                break;
            case EmployeeType.Servant:
                employee.Bonus = 0.01m;
                break;
            case EmployeeType.AssistantToTheRegionalManager:
                employee.Bonus = 1.0m;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
    

    There are a few problems with enumerations like this:

    • Behavior related to the enumeration gets scattered around the application
    • New enumeration values require shotgun surgery
    • Enumerations don't follow the Open-Closed Principle

    Adding a new enumeration value is quite a large pain, as there are lots of these switch statements around to go back and modify.  In the case above, we want the "default" behavior for defensive coding purposes, but a new enumeration value will cause an exception to be thrown.

    With enumeration behavior scattered around, we can never bring it back to the source type, because enumeration types can't have any behavior (or state for that matter).

    Instead of an enumeration, I like to use an enumeration class.

    Creating the enumeration class

    To move away from an enumeration to an enumeration class, I'll first use the Enumeration layer supertype from our "default architecture" we use at Headspring, from Tarantino:

    public abstract class Enumeration : IComparable
    {
        private readonly int _value;
        private readonly string _displayName;
    
        protected Enumeration()
        {
        }
    
        protected Enumeration(int value, string displayName)
        {
            _value = value;
            _displayName = displayName;
        }
    
        public int Value
        {
            get { return _value; }
        }
    
        public string DisplayName
        {
            get { return _displayName; }
        }
    
        public override string ToString()
        {
            return DisplayName;
        }
    
        public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
        {
            var type = typeof(T);
            var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
    
            foreach (var info in fields)
            {
                var instance = new T();
                var locatedValue = info.GetValue(instance) as T;
    
                if (locatedValue != null)
                {
                    yield return locatedValue;
                }
            }
        }
    
        public override bool Equals(object obj)
        {
            var otherValue = obj as Enumeration;
    
            if (otherValue == null)
            {
                return false;
            }
    
            var typeMatches = GetType().Equals(obj.GetType());
            var valueMatches = _value.Equals(otherValue.Value);
    
            return typeMatches && valueMatches;
        }
    
        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }
    
        public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
        {
            var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
            return absoluteDifference;
        }
    
        public static T FromValue<T>(int value) where T : Enumeration, new()
        {
            var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
            return matchingItem;
        }
    
        public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
        {
            var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
            return matchingItem;
        }
    
        private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
        {
            var matchingItem = GetAll<T>().FirstOrDefault(predicate);
    
            if (matchingItem == null)
            {
                var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
                throw new ApplicationException(message);
            }
    
            return matchingItem;
        }
    
        public int CompareTo(object other)
        {
            return Value.CompareTo(((Enumeration)other).Value);
        }
    }
    

    It's a large class, but it gives us some nice functionality out of the box, such as equality operations and such.  Next, I'll create the subtype that will house all of my different enumeration values:

    public class EmployeeType : Enumeration
    {
    }
    

    I'd still like individual employee types, such as Manager and Servant, and I can do this by exposing static readonly fields representing these employee types:

    public class EmployeeType : Enumeration
    {
        public static readonly EmployeeType Manager 
            = new EmployeeType(0, "Manager");
        public static readonly EmployeeType Servant 
            = new EmployeeType(1, "Servant");
        public static readonly EmployeeType AssistantToTheRegionalManager 
            = new EmployeeType(2, "Assistant to the Regional Manager");
    
        private EmployeeType() { }
        private EmployeeType(int value, string displayName) : base(value, displayName) { }
    }
    

    Notice I also get a much nicer display name with spaces.  In the past, I always had to do a lot of finagling to put spaces in the names when I displayed them. When someone wants to assign the Employee's type, it doesn't look any different than before:

    dwightSchrute.Type = EmployeeType.AssistantToTheRegionalManager;
    

    Now that I have a real class that acts like a Value Object, I have a destination for behavior.  For example, I can tack on a "BonusSize" property:

    public void ProcessBonus(Employee employee)
    {
        employee.Bonus = employee.Type.BonusSize;
    }
    

    Yes, this is a rather silly example, but it illustrates that most, if not all, of the switch statements concerning the previous enum type go away.  The behavior can be pushed down into the enumeration class, with each specific enumeration type supplying specific behavior.

    This pattern can even be taken further, where I have subtypes for each individual EmployeeType.  I'll never need to expose them outside to anyone:

    public abstract class EmployeeType : Enumeration
    {
        public static readonly EmployeeType Manager 
            = new ManagerType();
    
        protected EmployeeType() { }
        protected EmployeeType(int value, string displayName) : base(value, displayName) { }
    
        public abstract decimal BonusSize { get; }
    
        private class ManagerType : EmployeeType
        {
            public ManagerType() : base(0, "Manager") { }
    
            public override decimal BonusSize
            {
                get { return 1000m; }
            }
        }
    }
    

    All of the variations of each enumeration type can be pushed down not only to the enumeration class, but to each specific subtype.  The BonusSize now becomes an implementation detail of individual EmployeeType.

    Enumerations work well in a variety of scenarios, but can break down quickly inside your domain model.  Enumeration classes provide much of the same usability, with the added benefit of becoming a destination for behavior.

    Switch statements are no longer necessary, as I can push that variability and knowledge where it belongs, back inside the model.  If for some reason I need to check specific enumeration class values, the option is still open for me.  This pattern shouldn't replace all enumerations, but it's nice to have an alternative.

  • Rails notes, from the .NET side

    I'm starting a small experiment to try out Rails, and I'm keeping notes as I go.  From the first day, here's what I have so far:

    • Rails easy to install
      • First MySQL (easy)
      • Ruby, another MSI and go
      • Rails - from gem
      • All easy to install, basically a one-click installer for all
    • gem installer is crazy nuts
    • Don't forget to actually start the MySQL service (duh)
    • rake is also nuts
      • rake db:migrate is my friend
    • The generators trump ridiculous wizards any day of the week
      • We as .NET developers seem to be rather infatuated with projects and wizards
    • Ridiculous how little code is needed
    • The Rails console script is nice to try things out (it's an interactive Ruby session)
    • I can't tell when I need to restart the console to pick up changes

    On that last point, it's unbelievably ridiculous how little code is needed.  When using Active Record, I don't have to create properties and business as I would with any other ORM.  This comes from the ingenuity of Rails, combined with the power of Ruby.  Literally, an Active Record class could be just two lines of code, with as many properties as you wanted.

    • Convention over configuration, or "the Rails way", took exactly 5 minutes to accept
    • e is a nice text editor, which I need to remember to pay for someday
    • I need to find a good color template for e
    • Remember when using e, to set the tab size to 2, and use soft tabs
    • I'm surprised how little I care that I'm not in VS when developing in Rails

    I'm pretty much primarily using VS right now for a handful of features:

    • Compile, run, debug
    • Host ReSharper, TD.NET and VisualSVN (thanks Jeremy for reminding me on the last two)
    • An explorer-ish window to see my files

    I got rid of all the other items quite a while back.  I don't use the Toolbox window, the Server explorer, or the designer (the biggest waste).

    I'm not trading my career for a Rails path, not by a long shot, but I'm really curious to see what other communities have come up with.  More soon...

    Posted Aug 04 2008, 10:09 PM by bogardj with 14 comment(s)
    Filed under:
Copyright Los Techies 2007. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems