views:

264

answers:

5

I'll show a problem by example. There is a base class with fluent interface:

class FluentPerson
{
    private string _FirstName = String.Empty;
    private string _LastName = String.Empty;

    public FluentPerson WithFirstName(string firstName)
    {
        _FirstName = firstName;
        return this;
    }

    public FluentPerson WithLastName(string lastName)
    {
        _LastName = lastName;
        return this;
    }

    public override string ToString()
    {
        return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
    }
}

and a child class:

class FluentCustomer : FluentPerson
{
    private long _Id;

    private string _AccountNumber = String.Empty;

    public FluentCustomer WithAccountNumber(string accountNumber)
    {
        _AccountNumber = accountNumber;
        return this;
    }
    public FluentCustomer WithId(long id)
    {
        _Id = id;
        return this;
    }

    public override string ToString()
    {
        return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
    }
}

The problem is that when you call customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith") you can't add .WithId(123) in the end because return type of the WithLastName() method is FluentPerson (not FluentCustomer).

How this problem usually solved?

+2  A: 

Logically you need to configure stuff from most specific (customer) to least specific (person) or otherwise it is even hard to read it despite the fluent interface. Following this rule in most cases you won't need get into trouble. If however for any reason you still need to mix it you can use intermediate emphasizing statements like

static class Customers
{
   public static Customer AsCustomer(this Person person)
   {
       return (Customer)person;
   }
}

customer.WIthLastName("Bob").AsCustomer().WithId(10);
Dzmitry Huba
+2  A: 
 public class FluentPerson
 {
    private string _FirstName = String.Empty;
    private string _LastName = String.Empty;

    public FluentPerson WithFirstName(string firstName)
    {
        _FirstName = firstName;
        return this;
    }

    public FluentPerson WithLastName(string lastName)
    {
        _LastName = lastName;
        return this;
    }

    public override string ToString()
    {
        return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
    }
}


   public class FluentCustomer 
   {
       private string _AccountNumber = String.Empty;
       private string _id = String.Empty;
       FluentPerson objPers=new FluentPerson();



       public FluentCustomer WithAccountNumber(string accountNumber)
       {
           _AccountNumber = accountNumber;
           return this;
       }

       public FluentCustomer WithId(string id)
       {
           _id = id;
           return this;
       }

       public FluentCustomer WithFirstName(string firstName)
       {
           objPers.WithFirstName(firstName);
           return this;
       }

       public FluentCustomer WithLastName(string lastName)
       {
           objPers.WithLastName(lastName);
           return this;
       }


       public override string ToString()
       {
           return objPers.ToString() + String.Format(" account number: {0}",  _AccountNumber);
       }
   }

And invoke it using

  var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();
Ramesh Vel
Interesting : you have now eliminated the inheritance of class FluentCustomer from class FluentPerson.
BillW
@BillW, its always good implement the inheritance using interface than the class inheritance (though i havent tried that in my answer, but its simple to do that).
Ramesh Vel
@Ramesh I am fascinated by your solution in the sense that instead of looking from the perspective of "Customer" "isA" "Person," (inheritance) it seems to me to look from a perspective in which "Customer" "has-a" Person (too bad I'm not bright enough to know if that's "composition" :). Namaste, or Vannakum, if more appropriate.
BillW
@BillW, thanks mate.. yes its a form of composition, And actually its "Vanakkam" :) ..
Ramesh Vel
+2  A: 

Is a fluent interface really the best call here, or would an initializer be better?

 var p = new Person{
      LastName = "Smith",
      FirstName = "John"
      };

 var c = new Customer{
      LastName = "Smith",
      FirstName = "John",
      AccountNumber = "000",
      ID = "123"
      };

Unlike a fluent interface, this works fine without inherited methods giving back the base class and messing up the chain. When you inherit a property, the caller really shouldn't care whether FirstName was first implemented in Person or Customer or Object.

I find this more readable as well, whether on one line or multiple, and you don't have to go through the trouble of providing fluent self-decorating functions that correspond with each property.

richardtallent
I think OP was just using a simple example to illustrate the problem.
Gorpik
"Horses for courses" I think. Adding to Gorpik's comment above, the use of a fluent interface would allow you to combine setters with other behavioural elements if they were implemented, whereas initializers would limit you only to setting a fixed collection of values, and would require overloads or some other construct for optional elements. :-)
S.Robins
Initializer elements are *all* optional and *any* public writable property can be initialized. No need for overloads. But for setting more complex behaviors rather than just a short combination of primitive setters), you're right--fluent functions are handy.
richardtallent
@richardtallent, I stand corrected! That's what I get for glancing at a code example, rather than actually reading it! ;-)BTW, Re: readability, I find fluent interfaces tend to be more readable when used and implemented "sensibly", but less readable when the implementor tries to tie too many things together. A fluent interface is a great way to spaghettify your API implementation if you try to be too clever with them. My experience OTOH has been that they can also simplify the design if a little thought is put into it.
S.Robins
+8  A: 

You can use generics to achieve that.

public class FluentPerson<T>
    where T : FluentPerson<T>
{
    public T WithFirstName(string firstName)
    {
        // ...
        return (T)this;
    }

    public T WithLastName(string lastName)
    {
        // ...
        return (T)this;
    }
}

public class FluentCustomer : FluentPerson<FluentCustomer>
{
    public FluentCustomer WithAccountNumber(string accountNumber)
    {
        // ...
        return this;
    }
}

And now:

var customer = new FluentCustomer()
  .WithAccountNumber("123")
  .WithFirstName("Abc")
  .WithLastName("Def")
  .ToString();
Yann Trevin
Looks nice, but you cannot inherit further from `FluentCustomer`.
Gorpik
How do you propose to create an instance of FluentPerson? =)
Steck
@Gorpik; Good point. In fact, I think it should be possible to inherit further, but the syntax would be probably ugly (with nested generics). Anyway, it works fine for simple scenarios; for example when you need to build several specialized builders on top of a common abstract one.
Yann Trevin
@Steck. Should probably need to define a default non-generic type that inherits from the generic version of the class. Something like that: **public class FluentPerson : FluentPerson<FluentPerson> { }**. And now you can use: **var person = new FluentPerson();**.
Yann Trevin
That's the best solution in my case, thank you for the answer.
bniwredyc
+5  A: 

Try to use some Extention methods.

static class FluentManager
{
    public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
    {
        person.FirstName = firstName;
        return person;
    }

    public static T WithId<T>(this T customer, long id) where T : FluentCustomer
    {
        customer.ID = id;
        return customer;
    }
}

class FluentPerson
{
    public string FirstName { private get; set; }
    public string LastName { private get; set; }

    public override string ToString()
    {
        return string.Format("First name: {0} last name: {1}", FirstName, LastName);
    }
}

class FluentCustomer : FluentPerson
{
    public long ID { private get; set; }
    public long AccountNumber { private get; set; }

    public override string ToString()
    {
        return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
    }
}

after you can use like

new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
Steck
I like this approach better than the other proposed ideas simply because it would offer the greatest flexibility in terms of extensibility. Of course, the drawback would be if the OP's target .NET version doesn't support it!
S.Robins
Upvoted, I like this better as well, if fluent methods are required. Of course, the down side is that extension methods can only access the object's public members, so the fluent extension method may need to call a traditional method on the object (so the private work can be done) and then return the object.
richardtallent