Matthew's profileMatthew WrenPhotosBlogLists Tools Help

Blog


    February 01

    Chain of Responsibility Hybrid : Sequential Mappers

    Recently I was faced with the simple task of persisting a domain object into a file in as XML, so that later I could re-hydrate the domain object from my file.  So, here was my intention:

    1. Create an XML schema to represent the object;
    2. Create a class decorated with the relevant attributes so that the .NET XML serializer can serialize its data to XML that is compliant to the schema;
    3. Write a mapper that maps between the domain object and the XML serializable class;
    4. Write the code that takes the object and persists it to the file system.

    Points one to three are quite straight forwards, but it was when I came to point four that I had a think about the way in which this should be executed.  Breaking down the sequence of processes:

    1. Map from domain object to XML class
    2. Convert XML class to a string
    3. Persist string to a file

    Expressed another way:

    1. A -> B
    2. B -> C
    3. C -> D

    Insignificant as it may seem, there is undeniably a pattern here:

    • Each process involves mapping one type to another; and,
    • The target of each mapping is the source of the next mapping in the sequence

    Yes, crazy as it seems I am actually going to write three mappers instead of the original one.  Why?

    • It follows the pattern I have established;
    • It separates out nicely our concerns, and it allows us to deal at a much more abstract level with the steps necessary to achieve the objective, also affording us the opportunity to easily reconfigure the sequence, for example change the end destination to a database.

    Traditionally we might want to use the chain of responsibility pattern to execute the workflow sequence here, but I decided instead to build a hybrid of this design pattern, so that I could declare my sequence of mappers using fluent interfaces, and also I wanted to yield a mapper that spanned the entire chain of mappers.  So my aim is to be able to write something like:

    IMapper<A,D> mapper =
        new MapFrom<A>()
        .To<B>(abMapper)
        .To<C>(bcMapper)
        .To<D>(cdMapper);
    

    So, my first implementation is as follows:

    public interface IMapper<SOURCE, TARGET>
    {
        TARGET Map(SOURCE source);
    }
    
    public interface IMapperChain<ROOTSOURCE, TARGET> : IMapper<ROOTSOURCE, TARGET>
    {
        IMapperChain<ROOTSOURCE, NEWTARGET> To<NEWTARGET>(IMapper<TARGET, NEWTARGET> mapper);
    }
    
    public interface IMappingHandler<SOURCE>
    {
        void Handle(SOURCE source);
    }
    
    public class MapFrom<SOURCE>
    {
        private IMappingHandler<SOURCE> _handler;
    
        public IMapperChain<SOURCE, TARGET> To<TARGET>(IMapper<SOURCE, TARGET> mapper)
        {
            _handler = new MapTo<SOURCE, SOURCE, TARGET>(mapper, this);
            return _handler as IMapperChain<SOURCE, TARGET>;
        }
    
        public void Handle(SOURCE source)
        {
            _handler.Handle(source);
        }
    }
    
    public class MapTo<ROOTSOURCE, SOURCE, TARGET> :
        IMappingHandler<SOURCE>,
        IMapperChain<ROOTSOURCE, TARGET>
    {
        private bool _terminateChain;
        private MapFrom<ROOTSOURCE> rootChain;
        private IMapper<SOURCE, TARGET> _mapper;
        private IMappingHandler<TARGET> _handler;
        private TARGET _value;
    
        public MapTo(
            IMapper<SOURCE, TARGET> mapper,
            MapFrom<ROOTSOURCE> rootChain)
        {
            _mapper = mapper;
            this.rootChain = rootChain;
        }
    
        public IMapperChain<ROOTSOURCE, NEWTARGET> To<NEWTARGET>(IMapper<TARGET, NEWTARGET> mapper)
        {
            _handler = new MapTo<ROOTSOURCE, TARGET, NEWTARGET>(mapper, rootChain);
            return _handler as IMapperChain<ROOTSOURCE, NEWTARGET>;
        }
    
        public TARGET Map(ROOTSOURCE source)
        {
            _terminateChain = true;
            rootChain.Handle(source);
            _terminateChain = false;
            return _value;
        }
    
        public void Handle(SOURCE source)
        {
            _value = _mapper.Map(source);
            if (!_terminateChain && _handler != null)
            {
                _handler.Handle(_value);
            }
        }
    }
    

    Comments

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    Trackbacks

    The trackback URL for this entry is:
    http://mdwren.spaces.live.com/blog/cns!9C2D8683D2D11400!281.trak
    Weblogs that reference this entry
    • None