Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a Fluent Interface with Generics

I wanted to create a fluent interface that can be used like so:

void Main() {
    ModelStateMappings.MapDomainModel<Book>().MapViewModel<BookViewModel>()
        .Properties(book => book.Author, vm => vm.AuthorsName)
        .Properties(book => book.Price, vm => vm.BookPrice);

    ModelStateMappings.MapDomainModel<Store>().MapViewModel<StoreViewModel>()
        .Properties(store => store.Owner, vm => vm.OwnersName)
        .Properties(store => store.Location, vm => vm.Location);
}

I wanted end up with a collection that looked something like this:

static class ModelStateaMappings {
    private static IList<ModelMappings> mappings;
    // other methods in here to get it working
}

class ModelMappings {
    public Type DomainModelType {get;set;}
    public Type ViewModelType {get;set;}
    public IList<PropertyMapping> PropertyMappings {get;set;}
}

class PropertyMapping {
    public Expression<Func<object, object>> DomainProperty {get;set;}
    public Expression<Func<object, object>> ViewModelProperty {get;set;}
}

I was not able to get the above accomplished but I did create something similar which works in a similar fashion but I don't particularly like how I had to setup the fluent interfaces. I would rather have it read like the way I have it above.

like image 632
Evan Larsen Avatar asked Jan 06 '14 22:01

Evan Larsen


People also ask

Can we create generic interface?

Declaring Variant Generic Interfaces You can declare variant generic interfaces by using the in and out keywords for generic type parameters. ref , in , and out parameters in C# cannot be variant. Value types also do not support variance. You can declare a generic type parameter covariant by using the out keyword.

Does C# support generics?

C# allows you to define generic classes, interfaces, abstract classes, fields, methods, static methods, properties, events, delegates, and operators using the type parameter and without the specific data type.

What is fluent interface in C#?

A fluent interface is an object-oriented API that depends largely on method chaining. The goal of a fluent interface is to reduce code complexity, make the code readable, and create a domain specific language (DSL). It is a type of method chaining in which the context is maintained using a chain.

What is fluent design pattern?

Fluent Interface pattern provides easily readable flowing interface to code. Wikipedia says. In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). Programmatic ...


Video Answer


1 Answers

There are two common ways to create a fluent interface.

One way is to add to the current instance of the class being built and return this from each method.

Something like this:

public class NamesBuilder
{
    private List<string> _names = new List<string>();
    public NamesBuilder AddName(string name)
    {
        _names.Add(name);
        return this;
    }
}

The problem with this kind of builder is that you can write buggy code easily:

var namesBuilder = new NamesBuilder();

var namesBuilder1 = namesBuilder.AddName("John");
var namesBuilder2 = namesBuilder.AddName("Jack");

If I saw this code I would expect that namesBuilder1 and namesBuilder2 would each only have one name, and that namesBuilder wouldn't have any. However the implementation would have both names in all three variables as they are the same instance.

The better way to implement a fluent interface is to create a chain on builder classes that are lazily evaluated so that you create the final class once you're done building. Then if you branch in the middle of the building process you can make a mistake.

Here's the kind of code I would expect to write:

var bookMap =
    ModelStateMappings
        .Build<Book, BookViewModel>()
        .AddProperty(book => book.Author, vm => vm.AuthorsName)
        .AddProperty(book => book.Price, vm => vm.BookPrice)
        .Create();

var bookStore =
    ModelStateMappings
        .Build<Store, StoreViewModel>()
        .AddProperty(store => store.Owner, vm => vm.OwnersName)
        .AddProperty(store => store.Location, vm => vm.Location)
        .Create();

The code to make this work is a little more complicated than the "names" example.

public static class ModelStateMappings
{
    public static Builder<M, VM> Build<M, VM>()
    {
        return new Builder<M, VM>();
    }

    public class Builder<M, VM>
    {
        public Builder() { }

        public Builder<M, VM> AddProperty<T>(
            Expression<Func<M, T>> domainMap,
            Expression<Func<VM, T>> viewModelMap)
        {
            return new BuilderProperty<M, VM, T>(this, domainMap, viewModelMap);
        }

        public virtual Map Create()
        {
            return new Map();
        }
    }

    public class BuilderProperty<M, VM, T> : Builder<M, VM>
    {
        private Builder<M, VM> _previousBuilder;
        private Expression<Func<M, T>> _domainMap;
        private Expression<Func<VM, T>> _viewModelMap;

        public BuilderProperty(
            Builder<M, VM> previousBuilder,
            Expression<Func<M, T>> domainMap,
            Expression<Func<VM, T>> viewModelMap)
        {
            _previousBuilder = previousBuilder;
            _domainMap = domainMap;
            _viewModelMap = viewModelMap;
        }

        public override Map Create()
        {
            var map = _previousBuilder.Create();
            /* code to add current map to Map class */
            return map;
        }
    }
}

The other advantage to this type of builder is that you also maintain strongly-typed property fields.

Of course you would need to put in the correct code for your mapping in the Create method.

like image 90
Enigmativity Avatar answered Sep 21 '22 10:09

Enigmativity