in

 

Jimmy Bogard

Assistant to the assistant to the regional manager
  • Three simple Rhino Mocks rules

    In previous versions of Rhino Mocks, the Record/Replay model was the only way to create and verify mocks.  You would have an area that set up expectations in a record mode, then the replay mode would verify your expectations.  What was really strange about the Record/Replay model was that it didn’t vibe with the “Setup, Exercise, Verify” unit test pattern.  It looked like you had verifications in the middle of your setup, and your verification was just one line of code, “VerifyAll”.  In addition, you had to decide what kind of test double you wanted when you created it.  Last time I checked, there were at least four choices, and I’ll never get any of them straight on what exactly they did differently.

    With the new release of Rhino Mocks 3.5, which RTM’d today, the new Arrange, Act, Assert syntax makes it dirt simple to create test doubles for our unit tests.  In fact, I only really abide by three rules for 99.99% of the mocking cases I run into.  All I need to do is abide by three rules:

    1. Use the static MockRepository.GenerateMock method.  Don’t use an instance of a MockRepository, and don’t use the GenerateStub method.
    2. If the mocked instance method returns a value, use the Stub method to set up a result for that value.  This is an indirect input.
    3. If the mocked instance method does not return a value (void), use the AssertWasCalled/AssertWasNotCalled methods.  This is an indirect output.

    That’s it.  I rarely, almost never, assert a method was called on a method that returns a value.  The assumption is that indirect inputs should be used to alter the control flow in the application, or to be observed later in indirect or direct outputs.  When I started using Rhino Mocks, I set expectations on every single method call.  This led to brittle tests, where the body of the test matched almost exactly the implementation.  It was ridiculous.

    Here’s an example of snippet of code we want to test (yes it was test-driven the first time I wrote it):

    public class OrderProcessor
    {
        private readonly ISmtpClient _client;
        private readonly IOrderSpec _spec;
    
        public OrderProcessor(ISmtpClient client, IOrderSpec spec)
        {
            _client = client;
            _spec = spec;
        }
    
        public void PlaceOrder(Order order)
        {
            if (_spec.IsMatch(order))
            {
                string body = "Huge frickin' order alert!!!\r\nOrder #:" + order.OrderNumber;
    
                MailMessage message =
                    new MailMessage("sender@email.com", "salesdude@email.com",
                                    "Order processed", body);
    
                _client.Send(message);
            }
    
            order.Status = OrderStatus.Submitted;
        }
    }

    Let’s create a test that ensures that when the order specification is a match, we send a message to the SmtpClient:

    [Test]
    public void Should_send_an_email_when_the_order_spec_matches()
    {
        // Arrange
        var client = MockRepository.GenerateMock<ISmtpClient>();
        var spec = MockRepository.GenerateMock<IOrderSpec>();
        var order = new Order {Status = OrderStatus.New, Total = 500m};
    
        spec.Stub(x => x.IsMatch(order)).Return(true);
    
        var orderProcessor = new OrderProcessor(client, spec);
    
        // Act
        orderProcessor.PlaceOrder(order);
    
        // Assert
        client.AssertWasCalled(x => x.Send(null), opt => opt.IgnoreArguments());
        order.Status.ShouldEqual(OrderStatus.Submitted);
    }

    I set up the IOrderSpec to return true for IsMatch.  After exercising the OrderProcessor, I ensure that the Send method was called, ignoring any arguments set.  I could have asserted individual parameters, but that’s beyond the scope of this discussion.  What I don’t assert is that the IsMatch method was called.  I don’t care.  If the IsMatch method isn’t called, the Send method won’t be called.  I’ll have more tests to cover the other situations, which will cover all my behavioral specifications I want on the OrderProcessor.

    I should also note that I could care less what the GenerateMock method was named.  It could be called GenerateFloogle for all I care, all I’m interested in is how I use the Floogle.  Or Test Double, it doesn’t matter.  I just want a test double, I’ll decide how to use it.  The nice thing about the Rhino Mocks AAA syntax is that I can explicitly setup and verify whatever I want, and the test will fail if I don’t.  I don’t stub properties any more, as I don’t really have any interfaces with properties at this point.  Interfaces are for the most part stateless services, so I don’t need any fancy auto-property behavior for stubs.

    Sticking with these three rules for the 99% cases ensures I have good dependency interfaces that conform well to the command/query separation, that method should either perform an operation and return void, or query an object and not change anything.  With the new AAA syntax and these rules, I’ve found my tests to be far less brittle, and much more expressive in describing the behavior I’m specifying.

  • Transitioning between consulting projects

    Starting at Headspring gave me the first opportunity in my career to do consulting work.  I had previously worked in a startup, a product team and a large IT department.  In each of those cases, I didn’t leave a job until I gave my two weeks.  It was more than a little strange to leave a place after six months, and not from quitting.  Transitioning between projects is an interesting spot, having to both forget the old domain and learn a new one, all in a small amount of time.  Before I forget, I wanted to look back both at my first project and my first transition.

    Project observations

    It’s easy during a project to get caught up in delivery and never stop to look at how processes and events can affect delivery.  When I used to work in the test and manufacturing industry, both measurements and events were combined to try and glean the source of success and failures.  For example, if the manufacturing line switched to a higher quality assembler, you should expect to see an immediate decrease of assembly defects.

    During a software project, no one will notice the correlation between events, processes and quality/throughput, unless they’re specifically paying attention.  I didn’t pay attention too closely, but these are some of the things I remember.

    New team members have an initial net negative effect on velocity

    We brought on some great people in our project as the work started ramping up.  It didn’t matter how great the person was, it takes time to learn not only the codebase, but the domain, the team and the process.  While we practice fairly strict XP/Scrum practices here at Headspring, no two working environments are the same, and some variables need to change.  New people will lower velocity, so it’s better to set expectations early before the new team members come on.

    It’s also important not to blame the new team members for a lowered velocity.  They have enough on their hands to be reminded, “hey thanks for slowing us down, jerk.”  After one sprint/iteration, our velocity went higher than before, just as we expected.

    New team members will affect team dynamics

    I know it was wrong, but for some reason, I felt slightly resentful every time a new team member came on board.  I don’t know anything about psychology, but conversations with other team members confirmed everyone felt about the same.  An existing team gets comfortable not only with their predictable delivery, but also the personalities and dynamics of each team member.  Adding a new team member rocks the boat, so to speak, and it can be difficult for the existing team to transition to a new dynamic that includes the new team members.  It takes time to figure out where the new people fit, how the work will break down, how the new people interact with the existing team, the new team members likes/dislikes etc.

    For the new team member, it’s also a stressful transition, as they have to be injected into an existing dynamic, where the existing team might work together very smoothly and you’re introducing an unknown.  On top of that, you have a new domain, new codebase and new project to learn.

    Organizations that treat people as resources/head count completely miss this issue, and never address the side effects of adding new team members.  Teams are rarely static over the life of a project, but it’s important to understand how much new people have the potential for affecting velocity and delivery.

    Complex domains require simple models

    The last project had by far the most complex domain I’ve ever been involved with.  Maybe it’s because I’m so familiar with the e-commerce space, that going outside that safety zone added complexity, but the last domain really trumped all in terms of complexity.

    The early indications that it was complex was the amount of time it would take to get answers from the domain experts.  Custom configurable pricing rules, different billing options, even product lists that changed per customer.  It was still a system where a user would buy things, but without going into specifics, it wasn’t selling some simple widget.  There were at least major product types, with prices coming from three different places.  Prices could be affected in dozens of ways, and all had to match an existing legacy system.

    In all of this, our team spent a great deal of time getting the model “right”.  We didn’t try to design it perfectly up front, and we tried to design only as far as our current knowledge allowed.  Guessing was not an option.

    In the end, I think we worked on one of the best situations for domain-driven design, as our concepts and models needed to be representative of the complexity that was inherent in our domain.  Our domain model was large, but simple.  We worked diligently to ensure that our classes were highly cohesive, and concerns were clearly separated, each with a discernable business or domain concern.  Product selection/entitlement was completely separate from pricing.  Pricing complexity did not bleed into product-level concerns.  Billing options (prorate, annual bills, prorate and annual bills) did not bleed in to pricing nor product entitlement concerns.

    We wound up with a very rich domain model, flexible in the ways it needed to change, rigid in the core concerns of each class.

    Not everything is modeled equally well

    There were areas of our system that weren’t modeled as well as others.  That was fine, our most important areas, where we spent the most time and had the most important domain logic, were modeled very carefully and evolved over time.  This worked out naturally, as we only addressed modeling in an area that needed to change or grow.  Places that changed more were modeled better, and places that didn’t change either were correct (according to our assumptions at the time) or didn’t matter as much.

    When passing off to another team, it’s important to realize that all they see is the current iteration, not any previous iterations of the domain.  They weren’t involved in the deep design and domain conversations with the domain experts, nor do they have any insight on where most of the work occurred.  When explaining the model to new people, and something looked strange, we always had the excuse “well, we just don’t look at that part often.”

    It’s not possible to spend equal amounts of time on the entire model.  By spending time in places where we were actually working or interacting with, those areas naturally got better.  Other areas we didn’t touch or look at much, weren’t changed that often, simply because they didn’t need to.

    Transition observations

    Along with the first big project ending, is the first transition to a new project with an existing team.  Joining a team that’s already in progress gives me sympathy for those that joined the last project in the middle of development.

    Don’t expect to be productive immediately

    Along with a new project comes a new codebase, new domain, and in my case, a new laptop.  We have fairly large list of items that need to be installed for a development machine to be ready to develop, including all of the other small add-ons like Fiddler, Firebug, etc.  All of this takes about one working day, more if you need to install an operating system.  There were still things I forgot to write down and transition from my old laptop, including my AutoHotKey scripts, R# live templates, and other transient files.  Some folks I know use thumbdrives to keep all of these types of files, extensions and addons completely portable, and I think I’ll need to go down that route as well.

    Expect some initial friction

    Joining an existing team dynamic can be frustrating for both the existing team members and new team members.  New people will change how the team works, and that small pang of resentment I felt on my last team is surely being felt on the new team.  I don’t believe it’s wrong to feel that, as it’s probably a natural reaction in a team environment.  I will decrease velocity, or at least average velocity per team member.  I’m comfortable with that, as it’s only the initial transition period where this problem occurs.  Once we all get comfortable again, it won’t be a problem.

    One of my own personal problems of joining a new team/project is that I’ve noticed I start out by pointing out all of the differences and perceived inefficiencies.  It’s something I can tell definitely annoys people, as it’s probably the last thing anyone wants to hear from someone new to the team.  Every person adds new knowledge, and everyone appreciates that new knowledge, but probably not all at once.

    No two environments will be the same

    In my last project, we were housed in a bullpen and delivered in two week sprints.  This is what the environment and team members allowed.  In the new environment, developers and product owners are in one large room, delivering in one week iterations.  Not all environments are exactly what we want, but eventually, we have to deliver software.  We’ll create as an ideal environment as we can, but the time spent on environment change needs to be balanced against actually delivering value.  We’re always watchful of areas of improvement and inefficiencies, but it can take a lot of work to create a completely new environment.  As a consultant, I have to create the best environment I can and ensure that the environment is ready when we start the project.  After the project starts, we can’t spend nearly as much time and money crafting a new environment.

    I like good ergonomics

    The new project has great chairs.  I’ve never sat in a great chair, but mostly decrepit hand-me-downs.  If good chairs in your office are being fought over, it’s an organization smell.  I don’t think I could ever go back to bad chair, especially if I have to sit in it eight hours a day.

    Also, desk size is important.  Bumping elbows a hundred times a day increases frustration and can ultimately cause friction and affect velocity.  If I’m thinking about how close the person next to me has to sit, and not about the problem at hand, I’m wasting time.

    Thinking I like this whole consulting thing

    One of the most enjoying experiences I had at my last project was diving in to the business and domain problems.  It was technically challenging, but learning about a completely new domain was the most interesting for me.  Trying to learn the existing business processes, and how the technical processes were intended to support it were a lot of fun.

    Posted Oct 04 2008, 01:42 PM by bogardj with 3 comment(s)
    Filed under:
  • Windows 2008 Workstation and Live Writer

    After quite a bit of work, I’ve finally been able to post a blog entry with my new Windows 2008 Workstation machine.  I recently switched laptops, and have been forced to go silent because of the issues of installing Windows Live Writer on Server 2008.

    There isn’t really a Windows 2008 Workstation product you can buy.  It’s really taking a Win 2008 Server installation and adding some of the Vista features back in.  I really couldn’t stand to use Vista as either a home desktop or development machine OS, with all of the UAC and other garbage built in.

    Instead of trying to turn things off until I got to a usable point, I started from a stable, clean OS and added what I needed.  The best site I’ve found to create a Win 2008 Workstation machine is win2008workstation.com.

    Unfortunately, not everything is wine and roses on Server 2008, let alone the 64-bit version of Server 2008.  Most applications and drivers that support Vista 64-bit will work, as will most applications (but not drivers) that support 32-bit Server 2008/2003.

    At the very bottom of the barrel are applications that don’t support Server 2008 or 64-bit, and prevent their installers from running.  Google Desktop is one that provides the installer, but no support.  Windows Live Writer, one of the especially heinous installers, has several very annoying aspects:

    • The downloaded installer is not an installer.  It’s only there to download the real installer (often blocked in corporate environments).
    • The downloaded installer prevents downloading of the WLW installer if the OS isn’t supported.
    • Beta versions of WLW, if you can still find them, expire and prevent you from installing.

    All of this for a simple blog posting application.  For now, one of the only places I could find direct links to installers is here:

    http://dotnetwizard.net/live/direct-links-for-windows-live-wave-3/

    I’m sure Microsoft will find a way to self-destruct these applications for Workstation 2008, but until then, I’ll be happily posting away.

    Posted Oct 04 2008, 09:50 AM by bogardj with 2 comment(s)
    Filed under:
  • JavaScript block scoping

    I'm going through BLOCKED SCRIPT The Good Parts (which I highly, highly recommend), and I'm finding I knew next to nothing about JavaScript.  For example, this does not compile in C#:

    public void TestScope()
    {
        if (true) 
        {
            int i = 5;
        }
    
        i.ShouldEqual(5); // does not compile
    }
    

    This is because C# has block scope.  Any variable declared inside a block is scoped to that block, but not to any parent blocks.  JavaScript, however, does not have block scope:

    var foo = function()
    {
        if (true)
        {
            var a = 5;
        }
        
        alert(a);
    }
    
    foo();

    That will display "5".  Since there is no block scope, the book recommends declaring all variables at the top of the function.  JavaScript does have function scope (as well as closures), so this is the safer bet:

    var foo = function()
    {
        var a;
        
        if (true)
        {
            a = 5;
        }
        
        alert(a);
    }
    
    foo();

    This includes any variables declared inside for loops and any other kind of block.  Declaring them at the top of the function will eliminate pesky scoping bugs, when you might accidentally revert back to C-style block scope rules.

    Did I also mention you should pick up BLOCKED SCRIPT The Good Parts?  It turns out learning about the language may dispel ill feelings towards it.

  • A TDD investment addendum

    I completely left out one very important tip in my top 10 tips to get a return on your TDD investment:

    Take advantage of pair-programming.

    Pair programming is a great teaching device, as it lets two people go back and forth, working on a problem.  By pairing someone new to TDD with someone experienced, the knowledge transfer happens much faster.  Not only that, but questions get answered immediately, and barriers get knocked down faster with two eyes on the code.

    I don't pair all the time in day-to-day work, but I try to recognize difficult or design-heavy work, which is the real sweet spot for pair programming.  Pairing on visual design, HTML or CSS is an absolute bore for everyone involved.

    But especially with learning TDD, pair programming is the grease that keeps the machine running.  As with any new skill, beginners will get tripped up quite a bit at first.  With the real-time code review of pair programming, the more experienced developer can guide and assist a beginner, ensuring they don't fall completely flat on their face.
    Posted Sep 22 2008, 11:38 PM by bogardj with 2 comment(s)
    Filed under:
  • Ten tips to maximize the return on your TDD investment

    Paul Cowen presented an interesting personal observation of using TDD on the ALT.NET mailing list, under the title "TDD + effort != return".  The implication being that doing TDD requires extra work during development, extra work in training, extra work in hiring qualified people, even extra coding.  TDD is an investment in your team, your product and your craft.  But there are a few things you can do to get the most out of your TDD experience.  Here are ten tips to maximize your TDD investment:

    1. Invest in a professional refactoring tool.  ReSharper is my current favorite, though I've been fond of Refactor! Pro in the past.  Visual Studio, out of the box, has some fairly paltry (and slow) refactoring support.  A professional refactoring tool will pay for itself (depending on your rates/salary) very quickly.  A good craftsman needs sharp tools to build quality products.
    2. Invest in your design skills.  Go out and buy design books, read them, share them with your team.  Robert C. Martin's Agile Principles, Patterns and Practices is a great start.  I haven't read it, but Head First Design Patterns gets rave reviews.  The Pragmatic Programmer and Domain-Driven Design are two more must-reads.  TDD is meant to supplement and guide good design, not to be a magical spout from which good designs spew.  You have to know where you want to go for the destination to make sense.
    3. Hire passion and talent, not acronyms.  If you want to have a good team that designs good software, they have to be passionate about the craft, and talent to achieve success.  An alphabet soup of tools in a resume is not nearly as important as the drive to learn.  Someone with passion, talent and an open mind can be taught TDD and design.  Someone in love with stored procedures because they like SQL cannot.
    4. Invest in your team's success.  Conduct book clubs, hold brown bag sessions, sponsor training, encourage learning.  A team member doing TDD simply because the tech lead told them to isn't going to have much passion for it.  A team regularly conducting introspective analysis and searching for ways to improve will see the full benefits of TDD beyond the unit tests that result.
    5. Consider enlisting a coach.  Tiger Woods has a swing coach.  If the best golfer in the world thinks it's appropriate to hire someone to help with his swing, why would it not be appropriate to hire an experienced guide to ease the transition into TDD?  A coach is a person who's already made the mistakes and learned from them, and can effectively pass on this knowledge to others.  While it might take months of failures before TDD starts to make sense and your team realizes the full benefits, a coach can set your team on the right path to begin with, as well as answer questions that might have led you down blind alleys and wrong turns.
    6. Get everyone on board.  Quality needs to be important to your team, as should be good design.  Accepting less is a team decision, but that decision needs to keep in mind the cost of technical debt.  One person can subvert an entire team's intention to start TDD, and it's up to the team leader to ensure that everyone moves together.  If a team member refuses to cooperate, then it may be time to escalate the issue to HR.  A team's success is more important than one team member's ego.
    7. Invest in your refactoring skills.  Find any and all refactoring books, and devour them.  From Fowler's Refactoring to Kerievsky's Refactoring to Patterns to Meszaros' xUnit Test Patterns, you'll need to understand how, when and why to change your code.  Refactoring is essential to good design, as you never design right the first time.  These books describe code smells and how to get from design A to design B, safely.
    8. Always do the simplest thing that could possibly work.  Because you've invested in your design and refactoring skills, you don't have to create the strategy or template method pattern every place you possibly could.  It becomes easy to move towards and away from patterns with Kerievsky's book.  With refactoring techniques, applying design patterns before they're actually needed becomes rather pointless and wasteful.
    9. Avoid the cargo cult.  If test doubles seem foreign and pointless, do the research on the why.  Perform a little analysis before jumping in or dismissing a tool or technique.  There are gabs of resources on TDD, design and refactoring in books, blogs, articles and presentations.  Applying a new technique like TDD without understanding the "why" can be a short path to a bad experience.
    10. Expect to fall down.  TDD is a skill, and like any other skill, requires patience, dedication, study and practice to master.  Expect to fall down, flat on your face as you learn.  Everything isn't wine and roses once you start TDD, and you'll need to have convinced yourself that the benefits will be worth the initial pain.  A coach will ease the initial pain, but TDD and design in general is a learning process.

    This list isn't exhaustive naturally, but I wish I had this list when I started doing TDD almost three years ago.  TDD is hard, as is good design.  The first time I threw a baseball, I was pretty bad at it.  With practice, I improved.  With guided and informed practice, I improved much more.

    Skills aren't static either, you're either getting better or you're getting worse.  These tips have helped me improve gradually and incrementally over time, but only because I wanted to.  I'm still not satisfied with where my skills are, and I never will be.  There are always inefficiencies to address, areas to improve, and new techniques to learn and employ.

    TDD is a technique I believe whole-heartedly in, not because it's vogue or the echo chamber is loud, but because I've seen the improvements in maintainability and design as a result.

    Posted Sep 21 2008, 10:00 PM by bogardj with 14 comment(s)
    Filed under:
  • Interfaces and isolation

    Roy Osherove has suggested a new name for mocks, fakes, stubs or any test double: Isolation.  True, the myriad of test double names can muddy the language, and Meszaros' suggested name of "test double" still confuses people that don't get the "stunt double" comparison.

    When I first started using mocking frameworks, before I understood the OO design techniques they were intended to support, I used primarily two methods in Rhino Mocks:

    • MockRepository.CreateMock
    • Expect.Call

    Using these two techniques of creating Mock objects and setting expectations, without good OO design, led to a lot of over-specified, brittle tests.  Setting expectations that seemed to mirror the system under test seemed to be rampant duplication and hindrances to change.  Over time, as I started to learn more about good OO design and the SOLID principles, the issues of brittle and over-specified tests simply went away.  Techniques such as:

    • Dependency inversion principle
    • Interface-based design
    • Command-query separation
    • Single responsibility principle
    • Separation of concerns

    All led to better specified behavior in my tests.

    Which is why the name "isolation" means nothing to me.

    When I'm using interface-based design, I'm doing so not because of some innate desire to increase testability, but because I want to separate concerns and invert my dependencies.  I want users of my class to know exactly what is needed for this class to operate.  I employ fanatical refactoring to ensure the names and responsibilities of the classes I create are clear to the maintainers of my application.

    If I employ the DIP and interface-based design, what exactly am I isolating my class from?  Interfaces of which the class already doesn't care which implementation is provided?  Again, I don't use interfaces solely to swap out a test double in a unit test, but to achieve clear separation of concerns, hone the single responsibility of the class, and invert the dependencies.

    When I use Rhino Mocks in the new AAA syntax, I wind up using only three techniques/methods in 99% of cases:

    • MockRepository.GenerateMock<T> to supply my class under test with any dependencies it needs to work
    • Stub extension method to control indirect inputs
    • AssertWasCalled extension method to verify indirect outputs

    Following these rules helps me describe very clear behavior in my tests, with obvious results for those reading the tests.  I don't need to isolate when I'm already depending upon abstractions.

    Posted Sep 20 2008, 04:29 PM by bogardj with 11 comment(s)
    Filed under:
  • TDD design trade-offs and junk food

    Tony Rasa recently talked about design trade-offs when doing TDD:

    When “doing TDD,” we consciously make design trade-offs to favor testability. ... we end up with a lot of single-implementation interfaces because of testability concerns and from weaknesses with our tools.

    When I first started doing TDD, many of the new designs that came out of this practice seemed backwards and counterproductive at first.  After all, I wound up with many more classes, lots of interfaces, and most interfaces had exactly one implementation.

    It turned out that before doing TDD, I wasn't very good at OO design (not that I'm all that great now).  I tried, but all in all it was largely a bunch of guesswork coupled with the fear of changing code.

    In college, I decided to give up caffeine, fast food and soft drinks.  Initially, healthy food tasted disgusting and it felt like I was going through junk food withdrawal.  After a month or so of following a healthier diet practice, I decided to splurge on a cheeseburger.  It was disgusting, and I was thoroughly nauseated.  My body was not used to such high levels of fat, and was letting me know it was not pleased.

    Starting TDD is giving up OO junk food.  No more short cuts, god classes or big balls of mud, which seemed so comforting before.  No more wallowing in the debugger, taking pride in knowing how to take apart the Gordian knot, or making a mental stack of variables.  It hurt at first, seemed a little alien, but led toward leaner, tighter and more cohesive designs.

    As for these so-called "trade-offs", they are the first steps towards better designs.  One-implementation interfaces aren't a design smell, it's separation of concerns in action, dependency inversion principle in action.  Breaking big pieces into smaller cohesive units works on any scale, and you'll be hard pressed to find the lower limit of its cost.

    Because my initial OO designs had next to zero interfaces, it seemed rather silly to have a bunch of seemingly one-off implementations.  Eventually, I saw interfaces for what they truly were: contracts.  An interface is a contract, that any implementation needs to adhere to.  Consumers of the interface do not care about implementation details, nor can they, as the interface provides no other information besides the signatures it provides.

    When doing TDD in a top-down behavioral design fashion, I create interfaces well before I ever create an implementation for them.  And that's reducing cost, as I don't write code I don't absolutely prove that I need yet.  TDD forces the developer to put down the cheeseburger, drop the chalupa and ditch the Mountain Dew, forming a leaner, tighter and more cohesive codebase.

    Posted Sep 18 2008, 07:56 PM by bogardj with 26 comment(s)
    Filed under:
  • Integrating StructureMap and NHibernate with WCF

    Following many examples I found online for other IoC containers, I borrowed utilized those designs to create a StructureMap instance provider.  One problem we ran into with that design was dealing with NHibernate.  Mainly, there are two types that are very important to care about when using NHibernate:

    • ISessionFactory
    • ISession

    The first type, as the name implies, is responsible for creating the ISession object.  The ISession acts as a Unit of Work, allowing me to execute queries, save entities, and pretty much any other persistence operation with my persistent types.

    Typically, in an ASP.NET environment, we'll follow a Session per Request pattern, and only one ISession object is associated with one request by storing the ISession in HttpContext.Items.  In WCF, configuration of instance contexts and configurable threading makes the picture a little more interesting.  In WCF, we can configure our services to be instantiated as a singleton, per call or per session.  The safest of these three options is per call, as we can be certain on the server side when call-context level items get created and later cleaned up.

    There are a few examples of integrating Windsor and NHibernate with WCF, and this solution is inspired by those designs.  Before we get started, let's create a wrapper for our ISessionFactory and ISession.

    Creating the ISessionFactory abstraction

    Because ISessionFactory is rather expensive to instantiate, only one of these should be created per application.  Since the ISessionFactory built from the NHibernate Configuration class is thread-safe and meant to be used across threads, we can just make a static reference to it.  The main reason behind an ISessionFactory abstraction is that it's easier to control lifetime of the ISession we need to provide to our Repository implementations.  This is merely a personal preference, but it makes managing the ISession a little easier.  First, here's our wrapper interface:

    public interface ISessionBuilder
    {
        ISession GetSession();
        void CloseSession();
    }
    

    We need to both create and close the ISession as part of our Session per Request pattern.  The actual ISessionBuilder implementation isn't that interesting:

    public class SessionBuilder : ISessionBuilder
    {
        private static ISessionFactory _sessionFactory;
        private ISession _session;
    
        public ISession GetSession()
        {
            Initialize();
            if (_session == null)
                _session = _sessionFactory.OpenSession();
    
            return _session;
        }
    
        public void CloseSession()
        {
            if (_session == null)
                return;
    
            _session.Close();
            _session.Dispose();
            _session = null;
        }
    
        private static void Initialize()
        {
            var sessionFactory = _sessionFactory;
            if (sessionFactory == null)
                _sessionFactory = new Configuration().Configure()
                                        .BuildSessionFactory();
        }
    }
    

    When it comes to the repositories that need an ISession, they'll instead depend on an ISessionBuilder to build the ISession for them:

    public class ProductRepository : IProductRepository
    {
        private readonly ISessionBuilder _sessionBuilder;
    
        public ProductRepository(ISessionBuilder sessionBuilder)
        {
            _sessionBuilder = sessionBuilder;
        }
    
        public Product[] GetExpensiveProducts()
        {
            var session = _sessionBuilder.GetSession();
            var criteria = session.CreateCriteria(typeof(Product));
            criteria.Add(Restrictions.Gt("UnitPrice", 100m));
    
            var results = criteria.List<Product>();
    
            return results.ToArray();
        }
    }
    

    I like this implementation of finding ISession rather than a service locator or static property, as it fits in well with TDD.  I don't have to worry about some opaque dependency being up to test my repositories.

    Now that we have our ISessionFactory and ISession wrapper in place, let's look at modifying our original IInstanceProvider.

    Modifying the IInstanceProvider

    The IInstanceProvider is a WCF extension that allows custom instantiation of the service instance.  Our original StructureMapInstanceProvider used StructureMap's service locator to build up the service:

    public class StructureMapInstanceProvider : IInstanceProvider
    {
        private readonly Type _serviceType;
    
        public StructureMapInstanceProvider(Type serviceType)
        {
            _serviceType = serviceType;
        }
    
        public object GetInstance(InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }
    
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return ObjectFactory.GetInstance(_serviceType);
        }
    
        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
        }
    }
    

    Normally, we wouldn't have to do anything to this implementation, if only one class depended on ISessionBuilder.  But we want every repository participating in a call to use the exact same ISessionBuilder implementation.  This ensures that everyone uses the same ISession instance in one call.  Otherwise, the Unit of Work and Identity Map would be split amongst each repository, leading to concurrency and identity problems.

    What we need is to ensure that for this one call to GetInstance, only one implementation of ISessionBuilder is used.  We can configure caching of the ISessionBuilder implementation, but that won't allow us to get access to the ISessionBuilder that was used.  In addition to guaranteeing only one instance of ISessionBuilder is used, we need to close and dispose of the ISession at the end of the WCF call.  Instead of using InstanceScope.PerRequest caching in StructureMap, we'll need to use the With method.

    The With method allows us to make a call to the service locator GetInstance, and substitute a specific instance of a dependency during instantiation.  This will allow us to create an instance of the ISessionBuilder, store it for closing later, and use it for the service location of the WCF service instance.  Here's the new Session per Request instance provider:

    public class SessionPerCallInstanceProvider : IInstanceProvider
    {
        private readonly Type _serviceType;
        private ISessionBuilder _sessionBuilder;
    
        public SessionPerCallInstanceProvider(Type serviceType)
        {
            _serviceType = serviceType;
        }
    
        public object GetInstance(InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }
    
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            _sessionBuilder = ObjectFactory.GetInstance<ISessionBuilder>();
    
            return ObjectFactory
                    .With(_sessionBuilder)
                    .GetInstance(_serviceType);
        }
    
        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            if (_sessionBuilder != null)
            {
                _sessionBuilder.CloseSession();
                _sessionBuilder = null;
            }
        }
    }
    

    In the above WCF extension, WCF will call GetInstance to...get an instance of the service.  Inside that method, we first create an instance of the ISessionBuilder, and store it in a local variable.  Next, we use the With method to tell StructureMap to use that specific instance of ISessionBuilder for the following call to GetInstance.  Adding some debug messages looking at hash codes confirms that the same instance is used here as well in any repositories, at any depth in the dependency graph.  Finally, we add the ReleaseInstance implementation, which closes the ISession at the end of each request.

    We're still not quite finished, we still need to tell WCF to use this instance provider.  For that, we'll need to create a custom service behavior.

    Creating the Session per Request service behavior

    In the previous post on using StructureMap to instantiate our service, I used a custom ServiceHostFactory and ServiceHost to attach the StructureMap service behavior at runtime.  This time, I'll use an attribute instead.  That way, the Session per Request behavior can be opted in for each service implementation by using an attribute.  There's not much interesting about this service behavior, other than it also inherits from Attribute:

    public class SessionPerCallServiceBehavior : Attribute, IServiceBehavior
    {
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher cd = cdb as ChannelDispatcher;
                if (cd != null)
                {
                    foreach (EndpointDispatcher ed in cd.Endpoints)
                    {
                        ed.DispatchRuntime.InstanceProvider =
                            new SessionPerCallInstanceProvider(serviceDescription.ServiceType);
                    }
                }
            }
        }
    
        public void AddBindingParameters(ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase, 
            Collection<ServiceEndpoint> endpoints, 
            BindingParameterCollection bindingParameters)
        {
        }
    
        public void Validate(ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase)
        {
        }
    }
    

    The only thing I need to do here is to attach the instance provider (with the correct service type) to the dispatch runtime.  Finally, I just need to decorate my services with the SessionPerCallServiceBehavior attribute:

    [SessionPerCallServiceBehavior]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class ComboService : IComboService
    {
        private readonly IProductRepository _productRepository;
        private readonly ICategoryRepository _categoryRepository;
    
        public ComboService(IProductRepository productRepository, 
                            ICategoryRepository categoryRepository)
        {
            _productRepository = productRepository;
            _categoryRepository = categoryRepository;
        }
    
    

    Notice we also configured our service to use the PerCall instance context mode.  We want this service to be instantiated for every single call to ensure that our ISession is not used across multiple client calls.  Configuring to the PerCall setting ensures that we only get one ISession per request.

    Although we have our behavior up and running, we still need to find a place to configure StructureMap.

    Configuring StructureMap

    There's just one more piece left to this puzzle.  I need to inject the StructureMap fluent configuration somewhere in this pipeline.  It only needs to happen once, so a custom service host factory would do the trick:

    public class DIServiceHostFactory : ServiceHostFactory
    {
        public DIServiceHostFactory()
        {
            StructureMapConfiguration
                .ScanAssemblies()
                .IncludeTheCallingAssembly()
                .With<DefaultConventionScanner>();
        }
    }
    

    The service host factory above isn't coupled in any way to our custom service behavior, but we need to inject the StructureMap configuration so StructureMap knows where to look for our dependencies.  Finally, we need to configure our endpoint to use this service host factory, which is done in our case by editing the .svc file:

    <%@ ServiceHost Language="C#" Debug="true" 
        Service="Wcf.ComboService"
        Factory="Wcf.DIServiceHostFactory" %>

    With all of these pieces in place, our WCF endpoints can now follow the Session per Request pattern.

    Wrapping it up

    To configure our WCF application to use the Session per Request pattern, we needed to:

    • Create a wrapper around ISession for our repositories to depend on
    • Create an IInstanceProvider implementation that:
      • Creates an ISessionBuilder and stores it locally
      • Uses the With method on ObjectFactory to scope a specific instance of a dependency for a single call to GetObject
      • Closes the ISession when the service instance is released by WCF
    • Create the custom IServiceBehavior and Attribute that attaches the custom IInstanceProvider
    • Decorate our WCF service implementation with the custom IServiceBehavior and set the instance context mode to PerCall
    • Create a custom ServiceHostFactory to house our StructureMap configuration code
    • Modify our endpoint .svc file to use that custom ServiceHostFactory

    With the Session per Request pattern in place, we can better control our transaction boundaries as we now have access to the underlying ISession instance used in each call.  Some additional WCF extension code allowed us to decorate each of our services that participates in the Session per Request pattern.  Although it can take quite a bit of code to configure WCF to follow these patterns, the good news is that we were able to do so, working with the framework.

  • Some IoC Container guidelines

    So Derick Bailey asked me the other day a few weeks ago to describe how we use our IoC container.  I wouldn't call it "best practices", though some of these tips came from the creator of our container of choice, StructureMap (Jeremy Miller).  Although IoC containers are everywhere these days, you don't see many instructions on where and how to use them.  We've developed a consistent approach to using our favorite container of choice, StructureMap.  The basic ideals behind our approach are:

    Hiding the framework

    If you look through one of our projects that use StructureMap, you'll be hard pressed to find any usages of StructureMap.  In fact, one recent project had exactly one file that used StructureMap.

    Just like any framework, littering your code with references and usages can make it nearly impossible to change to a different container.  I've heard this quite a bit, that it's nice to change the framework you use at any time.  This has never actually happened with me, so I'm still dubious that anyone designing their application to be able to change frameworks at any time, actually has.  However, the less we litter our code with the framework, the less impact the framework has on our code. Bending design to accommodate usage can spoil benefits of the framework.

    Looking at StructureMap, hiding the framework means preferring the fluent configuration or the xml configuration.  With fluent configuration, I get compile-time safety of all my types, and it plays well with refactoring tools like ReSharper.

    Minimize calls to the container

    Following the first rule, we shouldn't have a lot of calls to our container to instantiate things.  Ideally, we would like to reduce our interaction with the service locator (ObjectFactory in StructureMap) to one call to get an instance.  We like to relegate the ObjectFactory calls to infrastructure-level code, such as the IInstanceProvider for WCF.

    Reducing calls to the container from our code also greatly eases the burden in our tests.  It's possible, but just annoying, to configure the container just for a unit test because our class calls directly into the container.  When the ObjectFactory call resides in some infrastructure piece, our entire domain layer can be completely free of any knowledge of the container.  While nice from a swappability perspective, its true value lies in a higher cohesion and greater separation of concerns.  If I don't have to worry about StructureMap calls when I need to change a class, I can focus more on its core purpose.

    Prefer constructor injection

    Dependency injection comes in (basically) two flavors:

    • Property injection
    • Constructor injection

    Property injection should be used only with optional dependencies.  An optional dependency is one that the class will still function properly.  Examples of optional dependencies are things like logging frameworks.  Constructor injection should be used for required dependencies.  A required dependency is one that the class will not function properly without it.

    For users of a class, constructor injection through constructor arguments conveys far more meaning for required dependencies.  If a dependency is required, then it should be required for instantiation.  If you can't use a class without a dependency, don't let anyone use the class without it.

    Prefer interfaces over abstract classes

    This one is another in the "conveys more meaning" category.  An interface is a contract that conveys a concise set of operations.  I can do far more with interfaces than abstract classes, as interfaces support multiple inheritance, while classes do not.  If some default behavior is needed, I'll supply an additional abstract class that implements the interface.

    Test your configuration

    Just like anything else in our system, automated tests find bugs in our configuration much faster and more efficiently than manual testing.  Automated tests are also far more reliable, of course.

    Typically, we test any interesting configuration.  This includes the top-most dependencies in our system (which will then load the entire object graph), as well as any custom configured dependencies.  For array dependencies, we test that the class has the correct array with the items in the correct order.  Before we tested our configuration, we would get burned with error messages after running the software.  With automated tests in place, we have confidence that our container is configured correctly.

    No calls to the container in a test

    Unless you're testing the container, the container should not show up in your unit test.  If we're following the first two rules laid out, we don't have any work to do.  If I need to configure the container for a unit or integration test, it's a good sign that I'm doing something wrong.  Configuring the container inside a test is possible, but it muddies the intent of the test.  The container then becomes another opaque dependency, which defeats the purpose of using an inversion of control container.

    Guidelines aren't rules

    Of course, there are always exceptions here.  But I always take a second look at my code if I feel it's necessary to not follow the above guidelines.  It's a smell that I'm probably doing something wrong.

  • Quality and code coverage

    It's an age-old question: should our team's goal be 100% coverage?  A valid question, but one I've never much cared about in practice.  The idea is that the team, all practicing TDD, should dutifully measure and add unit tests until they reach the assumed pinnacle of unit testing: 100% coverage.

    The general motivation behind 100% coverage is that 100% coverage equals zero bugs.  But what exactly is 100% coverage?  Coverage of what?  Wikipedia lists several kinds of coverage measures:

    • Function coverage
    • Statement coverage
    • Condition (Branch) coverage
    • Path coverage
    • Entry/exit coverage

    One of the most popular code coverage tools in .NET is NCover, which supports:

    • Function coverage (implicitly)
    • Statement coverage
    • Branch coverage (Enterprise edition only)

    NCover is a powerful tool, but it still doesn't support all types of coverage.  Attaining 100% coverage in NCover still means there are paths that we haven't tested yet, which means there are still potential bugs in our code.  If 100% coverage is a goal, stopping with NCover's measurement would lead into a false sense of security, or that an assumption that your codebase is bug-free.  It isn't.

    Not only is it not bug-free, but code coverage says nothing of defects.  Defects occur when the users of the software tell you it's not working the way they would like, and the root cause is a gap in the story analysis, missing acceptance criteria, or even a new story altogether.  100% coverage doesn't mean we're done, not by a long shot.

    Only the interesting parts

    In recent projects where we measured coverage several months in to the project, we saw regular numbers of 90% coverage.  This was on a team doing 100% TDD.  So what happened to the extra 10%?  If we're doing TDD all the time, why isn't every statement covered?  Every change was introduced with TDD, yet we still had gaps.

    So are we doing TDD wrong?  Looking at our tests, it certainly doesn't seem so.  Every test introduced covered behavior we considered interesting.  If behavior isn't interesting, we don't care about it.  Things like parameter checks, properties, law of demeter violations, and other brain-dead code is not covered.  Why?  The behavior just isn't that interesting.  If tests are a description of the behavior of the system, why fill it with all the boring, trivial parts?  The effort required to cover triviality is just too high compared to other ways we can increase value.

    Diminishing returns

    Missing in the 100% coverage conversation is the effort required to get to 100%.  Attempting to get another 5% takes equal effort of the previous 10%.  The next 2% takes equal effort of the previous 15%, and so on.  The closer we try to get to 100%, the more difficult it is to achieve.  This is called the law of diminishing returns.  As we get closer and closer to 100%, it takes vastly more effort to get there.  At some point, you have to ask yourselves, is there value in this effort?  Often, bending code to get 100% can decrease design quality, as you're now twisting the original intent solely for coverage concerns, not usability, readability or other concerns.

    Which is why when the question of 100% coverage comes up, I'm very skeptical of it as a goal or bar to set.  Measuring coverage is an interesting data point, as are other measures such as static analysis.  But in the end, it's only a measure, an indication.  It's still up to the team to decide on the value of addressing missing areas, with the full knowledge that they are still limited to what the tool measures.

    Personally, I'd much rather spend the effort elsewhere, where I'll get a much better return on my effort spent.

    Posted Sep 09 2008, 07:59 AM by bogardj with 9 comment(s)
    Filed under: ,
  • On passion

    One of my favorite bloggers, JP Boodhoo, has a little saying he puts at the end of every post:

    Develop with passion!

    I've only met JP once, and it was quite obvious from that meeting that passion is somewhat of a mantra for him.  It's also something the best career counselor offered me years ago: "Find what you're passionate about, and do that".  When it comes to passion and programming professionally, I've seen two types of passion:

    • Passion for the craft
    • Passion for the domain

    I've met far too many programmers that have passion, but only for the craft.  When the business would ask for some feature, their passion would lead to a technically interesting solution, but one that may or may not have solved the original problem.  This could be solved with deeper analysis of course, but what would lead that developer towards deeper analysis?  What motivates them to do so?  If all the developer is looking for is technical success, they will only reluctantly care about the domain problem, and only enough to get back to the technical problems.

    We've all been guilty of this at one time or another.  Twisting requirements, hedging estimates to make a fun technical solution turn into a no-brainer business decision.  We're doing a great disservice to our employers with these actions.  I believe this strong motivation comes from a lack of passion for the domain.

    One pattern I've started to notice lately is that continued success requires passion.  Without passion comes little sustained success.  Short-term success may come by accident, but it's far easier to be unlucky in success than it is lucky.

    When we apply our passion, we can't put all of our eggs in the technical basket.  Domain-driven design talks quite a bit about the Ubiquitous Language, and how it is important for developers and domain experts to share the same language.  This language needs to come from the domain, not technical patterns and such.  Without passion for the domain, our organization will never achieve complete success.

    When we're in conversations with the business analysts about specific stories, working through the motivation behind the story, passion for the domain creates a two-way conversation.  Otherwise, it's two people speaking different languages, trying and failing to translate in and out of technical mumbo-jumbo. "So you want to use an ESB for that?" "All I want is to not have to work these orders manually" "So you want an ERP system?" "What?"

    Eventually the business analyst caves to technical demands, hoping that they solve the business problem at hand.  Since they aren't the technical expert, the BA puts their trust in the developer.  Without passion for the domain, their trust is truly misplaced.

    I truly believe it is a developer's job to bridge the gap between technology and business.  Success in bridging this gap requires both passion for the craft and passion for the domain.

    Posted Sep 04 2008, 08:05 AM by bogardj with 15 comment(s)
    Filed under:
  • Building arrays in StructureMap 2.5

    Although it was possible in previous versions of StructureMap, the new fluent interface for StructureMap configuration in version 2.5 allows easy configuration of array type constructor parameters.  For example, consider a simple order processor pipeline:

    public class OrderPipeline
    {
        private readonly IOrderPipelineStep[] _steps;
    
        public OrderPipeline(IOrderPipelineStep[] steps)
        {
            _steps = steps;
        }
    
        public IOrderPipelineStep[] Steps
        {
            get { return _steps; }
        }
    
        public OrderPipelineResponse Execute(OrderPipelineRequest request)
        {
            OrderPipelineResponse response = null;
            foreach (var step in _steps)
            {
                response = step.ExecuteStep(request);
                if (!response.IsSuccessful)
                    return response;
            }
            return response;
        }
    }
    

    This pipeline takes an array of pipeline steps.  Given a pipeline request, which may contain information necessary for the steps, it executes each of the steps in order.  If any of the responses is not successful, the pipeline stops executing and returns the current response.

    No where in this pipeline do we see which steps should be created.  Some steps may require service location from StructureMap, while others may not have any dependencies.  In any case, we want the construction of the pipeline steps for the pipeline to be external from the pipeline, as we can see it's only concerned with executing steps.

    But someone has to be concerned with which steps can be executed.  For many of our dependencies, the DefaultConventionScanner is all we need to construct our dependencies.  With an array parameter, there is no way StructureMap could automatically figure out which dependencies to create, and which order.  Instead, we can create a custom Registry to configure our dependency:

    public class PipelineRegistry : Registry
    {
        protected override void configure()
        {
            ForRequestedType<OrderPipeline>()
                .TheDefault.Is.OfConcreteType<OrderPipeline>()
                .TheArrayOf<IOrderPipelineStep>()
                .Contains(x =>
                              {
                                  x.OfConcreteType<ValidationStep>();
                                  x.OfConcreteType<SynchronizationStep>();
                                  x.OfConcreteType<RoutingStep>();
                                  x.OfConcreteType<PersistenceStep>();
                              });
        }
    }
    

    In this Registry, I tell StructureMap first what the requested and default concrete types are.  Next, I tell StructureMap that the array of IOrderPipelineStep contains a set of concrete types.  The Contains method takes a delegate, so I can use a lambda to configure all of the individual concrete types.  Each step is created in the order I specify in the lambda block.  Here's the passing test:

    [Test]
    public void Should_construct_the_pipeline_steps_correctly()
    {
        StructureMapConfiguration
            .ScanAssemblies()
            .IncludeTheCallingAssembly()
            .With<DefaultConventionScanner>();
    
        var pipeline = ObjectFactory.GetInstance<OrderPipeline>();
    
        pipeline.Steps.Length.ShouldEqual(4);
        pipeline.Steps[0].ShouldBeOfType(typeof (ValidationStep));
        pipeline.Steps[1].ShouldBeOfType(typeof (SynchronizationStep));
        pipeline.Steps[2].ShouldBeOfType(typeof (RoutingStep));
        pipeline.Steps[3].ShouldBeOfType(typeof (PersistenceStep));
    }
    

    Notice that I did not need to specify the individual Registry.  StructureMap scans the given assemblies for Registries, and automatically adds them to the configuration.  Next, I ask StructureMap for an instance of the OrderPipeline.  Again, nowhere do we see any code for constructing the correct list of IOrderPipelineSteps, this is encapsulated in our Registry.  Finally, the rest of the test asserts that both the correct steps were created, and in the right order.

    With the new fluent interface in StructureMap 2.5, I get a nice declarative interface to configure all of the special dependencies.  Although the DefaultConventionScanner picks up almost all of my dependencies, in some special cases I still need to configure them.  Array dependencies are created simply enough just with a lambda specifying the correct steps.

  • 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);