views:

2088

answers:

10

First off, I understand the reasons why an interface or abstract class (in the .NET/C# terminology) cannot have abstract static methods. My question is then more focused on the best design solution.

What I want is a set of "helper" classes that all have their own static methods such that if I get objects A, B, and C from a third party vendor, I can have helper classes with methods such as

AHelper.RetrieveByID(string id);
AHelper.RetrieveByName(string name);
AHelper.DumpToDatabase();

Since my AHelper, BHelper, and CHelper classes will all basically have the same methods, it seems to makes sense to move these methods to an interface that these classes then derive from. However, wanting these methods to be static precludes me from having a generic interface or abstract class for all of them to derive from.

I could always make these methods non-static and then instantiate the objects first such as

AHelper a = new AHelper();
a.DumpToDatabase();

However, this code doesn't seem as intuitive to me. What are your suggestions? Should I abandon using an interface or abstract class altogether (the situation I'm in now) or can this possibly be refactored to accomplish the design I'm looking for?

A: 

In C# 3.0, static methods can be used on interfaces as if they were a part of them by using extension methods, as with DumpToDatabase() below:

static class HelperMethods
 {  //IHelper h = new HeleperA();
    //h.DumpToDatabase() 
    public static void DumpToDatabase(this IHelper helper) { /* ... */ }

    //IHelper h = a.RetrieveByID(5)
    public static IHelper RetrieveByID(this ObjectA a, int id) 
     { return new HelperA(a.GetByID(id));
     }

    //Ihelper h = b.RetrieveByID(5)       
    public static IHelper RetrieveByID(this ObjectB b, int id)
     { return new HelperB(b.GetById(id.ToString())); 
     }

 }
Mark Cidade
+2  A: 

I personally would perhaps question why each of the types need to have a static method before even thinking further..

Why not create a utlity class with the static methods that they need to share? (e.g. ClassHelper.RetrieveByID(string id) or ClassHelper<ClassA>.RetrieveByID(string id)

In my experience with these sort of "roadblocks" the problem is not the limitations of the language, but the limitations of my design..

I hope this helps/give food for thought, if not, lets discuss! :)

Rob Cooper
+2  A: 

How are ObjectA and AHelper related? Is AHelper.RetrieveByID() the same logic as BHelper.RetrieveByID()

If Yes, how about a Utility class based approach (class with public static methods only and no state) static [return type] Helper.RetrieveByID(ObjectX x)

edit: Looks like Rob beat me to the punch :) _

Gishu
A: 

How do I post feedback on Stack Overflow? Edit my original post or post an "answer"? Anyway, I thought it might help to give an example of what is going on in AHelper.RetrieveByID() and BHelper.RetreiveByID()

Basically, both of these methods are going up against a third party webservice that returns various a generic (castable) object using a Query method that takes in a pseudo-SQL string as its only parameters.

So, AHelper.RetrieveByID(string ID) might look like

public static AObject RetrieveByID(string ID)
{
  QueryResult qr = webservice.query("SELECT Id,Name FROM AObject WHERE Id = '" + ID + "'");

  return (AObject)qr.records[0];
}

public static BObject RetrieveByID(string ID)
{
  QueryResult qr = webservice.query("SELECT Id,Name,Company FROM BObject WHERE Id = '" + ID + "'");

  return (BObject)qr.records[0];
}

Hopefully that helps. As you can see, the two methods are similar, but the query can be quite a bit different based on the different object type being returned.

Oh, and Rob, I completely agree -- this is more than likely a limitation of my design and not the language. :)

jerhinesmith
A: 

Are you looking for polymorphic behavior? Then you'll want the interface and normal constructor. What is unintuitive about calling a constructor? If you don't need polymorphism (sounds like you don't use it now), then you can stick with your static methods. If these are all wrappers around a vendor component, then maybe you might try to use a factory method to create them like VendorBuilder.GetVendorThing("A") which could return an object of type IVendorWrapper.

DancesWithBamboo
+2  A: 

You can't overload methods by varying just the return type.

You can use different names:

static AObject GetAObject(string id);
static BObject GetBObject(string id);

Or you can create a class with casting operators:

class AOrBObject
 { string id;
   AOrBObject(string id) {this.id = id;}

   static public AOrBObject RetrieveByID(string id)
    { return new AOrBObject(id);
    }

   public static AObject explicit operator(AOrBObject ab) 
    { return AObjectQuery(ab.id);
    }

   public static BObject explicit operator(AOrBObject ab)
    { return BObjectQuery(ab.id);
    } 
 }

Then you can call it like so:

 var a = (AObject) AOrBObject.RetrieveByID(5);
 var b = (BObject) AOrBObject.RetrieveByID(5);
Mark Cidade
+3  A: 

Hey jerhinesmith,

Looking at your response I am thinking along the following lines:

  • You could just have a static method that takes a type parameter and performs the expected logic based on the type.
  • You could create a virtual method in your abstract base, where you specify the SQL in the concrete class. So that contains all the common code that is required by both (e.g. exectuting the command and returning the object) while encapsulating the "specialist" bits (e.g. the SQL) in the sub classes.

I prefer the second option, although its of course down to you. If you need me to go into further detail, please let me know and I will be happy to edit/update :)

Rob Cooper
+3  A: 

For a generic solution to your example, you can do this:

public static T RetrieveByID<T>(string ID)
 { var fieldNames = getFieldNamesBasedOnType(typeof(T));
   QueryResult qr = webservice.query( "SELECT "+fieldNames+" FROM "
                                      +tyepof(T).Name
                                     +" WHERE Id = '" + ID + "'");
   return (T) qr.records[0];
 }
Mark Cidade
+1. I do this in several querying libs, especially the ones that flow through to web services.
jro
+4  A: 

If I were you I would try to avoid any statics. IMHO I always ended up with some sort of synchronization issues down the road with statics. That being said you are presenting a classic example of generic programming using templates. I will adopt the template based solution of Rob Copper presented in one of the posts above.

rptony
A: 

marxidad Just a quick point to note, Justin has already said that the SQL varies a lot dependant on the type, so I have worked on the basis that it could be something completely different dependant on the type, hence delegating it to the subclasses in question. Whereas your solution couples the SQL VERY tightly to the Type (i.e. it is the SQL).

rptony Good point on the possible sync issues with statics, one I failed to mention, so thank you :) Also, its Rob Cooper (not Copper) BTW ;) :D ( EDIT: Just thought I would mention that in case it wasn't a typo, I expect it is, so no problem!)

Rob Cooper