jerhinesmith
jerhinesmith

Reputation: 15492

Static Methods in an Interface/Abstract Class

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?

Upvotes: 10

Views: 9134

Answers (10)

Mark Cidade
Mark Cidade

Reputation: 99957

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];
}

Upvotes: 3

Mark Cidade
Mark Cidade

Reputation: 99957

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); 

Upvotes: 2

Gishu
Gishu

Reputation: 136593

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) 

Upvotes: 2

Mark Cidade
Mark Cidade

Reputation: 99957

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())); 
     }
 }

Upvotes: 1

Rob Cooper
Rob Cooper

Reputation: 28857

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..

Upvotes: 2

Rob Cooper
Rob Cooper

Reputation: 28857

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!)

Upvotes: 0

rptony
rptony

Reputation: 1024

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.

Upvotes: 5

Rob Cooper
Rob Cooper

Reputation: 28857

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 :)

Upvotes: 3

DancesWithBamboo
DancesWithBamboo

Reputation: 4156

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.

Upvotes: 0

jerhinesmith
jerhinesmith

Reputation: 15492

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. :)

Upvotes: 0

Related Questions