tags:

views:

473

answers:

7

I'm sure this must be a common problem. I've got a class that in an ideal world would have the following constructors

public Thing(string connectionString)

public Thing(string fileName)

Obviously this isn't allowed because the signatures are the same. Does anybody know of an elegant solution to this problem?

+1  A: 

Make two public properties ConnectionString and FileName and then use these to fill your object.

In C# you can use an object initalizer. Like this:

Thing thing = new Thing{FileName = "abc", ConnectionString = "123"};
Gerrie Schenck
what's with the mark down? it's a valid use case.It has issues in theis scenario in that t doesn't make the illegality of setting both obvious but it's hardly totally flawed
ShuggyCoUk
I didn't vote this down, however I wouldn't suggest this either. Very often, you want an object "set up" once it has been constructed. That way you know that once methods are called on it, it's already in a certain state you're expecting it to be. With this approach, you don't have that.
BFree
+18  A: 

You can used the named constructor idiom:

public static Thing withConnection(string connectionString) {
    Thing t = new Thing();
    t.doSomethingWithConnectionString(connection);
    return t;
}

public static Thing withFilename(string filename) {
    Thing t = new Thing();
    t.doSomethingWithFilename(filename);
    return t;
}

private Thing() {
    /* Make this private to clear things up */
}
Sean Bright
While this was my initial take as well, use Factory Methods, I'm concerned about the a design that tries to encapsulate both file-based and database access in one class. Valid pattern, just concerned about it's use disguising a code smell.
tvanfosson
I had the same thought, but then I realized that "connectionString" might not refer to a database connection but a file-based connection. I agree that if `Thing` is pulling double duty, it should probably be broken out into separate classes.
Sean Bright
@sean -- still suspicious, normally I'd think either fileName or path for file-based connections. For a file-based database, you could always just give the correct connection string.
tvanfosson
A slight variation on this is to make a nested type called "Factory" that has these methods in it, so you write "Thing.Factory.CreateWithConnection()". The factory type has access to the private ctor of Thing. It helps separate concerns nicely.
Jay Bazuzi
@tvanfosson - well with the class name "Thing" we can only speculate as to the implementation.
Sean Bright
+5  A: 

You can make all the constructors private and create factory methods (static methods on the class like CreateFromConnectionString()).

Brian
A: 

Here are some workarounds.

Have one constructor that takes a connection string, and then have a factory method on the class that takes filename. Something like this:

public static Thing CreateThing(string fileName)

this method can call a private parameter less constructor, and you can take it from there.

Another option, is to have an enum that has two types in it. FileName and ConnectionString. Then just have one constructor that takes a string, and the enum. Then based on the enum you can determine which way to go.

BFree
+2  A: 

These actually seem like different "things" to me, either a class associated with a file or a class associated with a database. I'd define an interface, then have separate implementations for each. Use a Factory to generate the correct implementation.

A hint that you may need to change your design is if your methods have to decide whether they are working with a file or a database before they perform the required action. If this is the case, then separating into different classes would be the way I would go.

public interface IThing
{
   ... methods to do the things that Things do
}

public class FileThing : IThing
{
  ... file-based methods
}

public class DatabaseThing : IThing
{
  ... database-based methods
}

public static class ThingFactory
{
     public IThing GetFileThing( string name )
     {
         return new FileThing( name );
     }

     public IThing GetDatabaseThing( string connectionString )
     {
         return new DatabaseThing( connectionString );
     }
}

If you had common behavior you could alternatively define an abstract class containing the default/common behavior and derive from it instead of/in addition to the interface.

tvanfosson
A: 

I like static constructor-functions:

class Thing
{
   public static Thing NewConnection(string connectionString)
   {
       return new Thing(connectionString, true);
   }

   public static Thing NewFile(string fileName);
   {
        return new Thing(fileName, false);
   }
}
.
.
.
{
    var myObj = Thing.NewConnection("connect=foo");
    var Obj2 = Thing.NewFile("myFile.txt");
}

(not shown, but straight-forward, the implementation of the Thing-Constructor with an extra boolean parameter).

abelenky
+5  A: 

Well, there are several potentials - what's considered elegent depends on the usage scenario.

  • Static factory methods, that call into a private constructor.

    static Thing thingWithFileName(string fileName)
    
  • Create a different type for one of the parameters, or use a builtin. Rather than a string fileName, you could use a System.IO.FileStream. This is also more type safe, as I can't accidently pass the wrong data into the wrong static method, or field.

  • Pass a second parameter to the constructor, either an enum or a boolean, indicating the intent of the first parameter

    enum ThingType { FileName, ConnectionString }
    Thing(string str, ThingType type) ...
    
  • Subclass Thing, so you have a ConnectionTypeThing and a FileBackedThing

  • Completely eliminate Thing doing it's connection, and have preconnected data sources provided. So you end up with

    Thing(InputStream dataSource)
    

    or something analogous.

My "elegance" money goes on either the first or second suggestions, but I'd need more context to be happy with any choice.

Adam Wright