.net - sirve - interfaces poo
Métodos estáticos en una interfaz/clase abstracta (10)
En primer lugar, entiendo las razones por las que una interfaz o clase abstracta (en la terminología .NET / C #) no puede tener métodos abstractos estáticos. Mi pregunta está más centrada en la mejor solución de diseño.
Lo que quiero es un conjunto de clases "auxiliares" que tengan sus propios métodos estáticos, de modo que si obtengo los objetos A, B y C de un proveedor externo, pueda tener clases auxiliares con métodos tales como
AHelper.RetrieveByID(string id); AHelper.RetrieveByName(string name); AHelper.DumpToDatabase();
Como mis clases AHelper, BHelper y CHelper básicamente tendrán los mismos métodos, parece lógico mover estos métodos a una interfaz de la que estas clases derivan. Sin embargo, querer que estos métodos sean estáticos me impide tener una interfaz genérica o una clase abstracta de la que puedan derivarse todos ellos.
Siempre podría hacer que estos métodos no sean estáticos y luego instanciar los objetos primero, como
AHelper a = new AHelper(); a.DumpToDatabase();
Sin embargo, este código no me parece tan intuitivo. ¿Cuáles son tus sugerencias? ¿Debo abandonar el uso de una interfaz o clase de resumen por completo (la situación en la que me encuentro ahora) o puede ser refactorizada para lograr el diseño que estoy buscando?
¿Cómo publico mis comentarios sobre ? ¿Editar mi publicación original o publicar una "respuesta"? De todos modos, pensé que podría ser útil dar un ejemplo de lo que está sucediendo en AHelper.RetrieveByID () y BHelper.RetreiveByID ()
Básicamente, ambos métodos van en contra de un servicio web de terceros que devuelve varios objetos genéricos (moldeables) utilizando un método Query que toma una cadena pseudo-SQL como sus únicos parámetros.
Entonces, AHelper.RetrieveByID (string ID) podría verse como
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]; }
Espero que eso ayude. Como puede ver, los dos métodos son similares, pero la consulta puede ser bastante diferente en función del tipo de objeto que se devuelve.
Ah, y Rob, estoy completamente de acuerdo, esto es más que probable una limitación de mi diseño y no del lenguaje. :)
¿Estás buscando comportamiento polimórfico? Entonces querrás la interfaz y el constructor normal. ¿Qué no es intuitivo acerca de llamar a un constructor? Si no necesita polimorfismo (parece que no lo usa ahora), puede seguir con sus métodos estáticos. Si se trata de envoltorios alrededor de un componente de proveedor, entonces tal vez podría intentar usar un método de fábrica para crearlos como VendorBuilder.GetVendorThing ("A") que podría devolver un objeto de tipo IVendorWrapper.
Si yo fuera tú, trataría de evitar cualquier estática. En mi humilde opinión, siempre terminé con algún tipo de problemas de sincronización en el camino con la estática. Dicho esto, estás presentando un ejemplo clásico de programación genérica usando plantillas. Adoptaré la solución basada en plantillas de Rob Copper presentada en una de las publicaciones anteriores.
¿Cómo se relacionan ObjectA y AHelper? Es AHelper.RetrieveByID()
la misma lógica que BHelper.RetrieveByID()
En caso afirmativo, ¿qué tal un enfoque basado en clases de utilidades (clase con métodos estáticos públicos solamente y sin estado)?
static [return type] Helper.RetrieveByID(ObjectX x)
En C # 3.0, los métodos estáticos se pueden usar en las interfaces como si fueran parte de ellos mediante el uso de métodos de extensión, como con DumpToDatabase () a continuación:
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()));
}
}
No puede sobrecargar los métodos variando solo el tipo de devolución.
Puedes usar diferentes nombres:
static AObject GetAObject(string id);
static BObject GetBObject(string id);
O puedes crear una clase con operadores de casting:
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);
}
}
Entonces puedes llamarlo así:
var a = (AObject) AOrBObject.RetrieveByID(5);
var b = (BObject) AOrBObject.RetrieveByID(5);
Para una solución genérica a su ejemplo, puede hacer esto:
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];
}
Personalmente, quizás cuestionaría por qué cada uno de los tipos debe tener un método estático antes de pensar aún más.
¿Por qué no crear una clase utlity con los métodos estáticos que necesitan compartir? (por ejemplo, ClassHelper.RetrieveByID(string id)
o ClassHelper<ClassA>.RetrieveByID(string id)
En mi experiencia con este tipo de "obstáculos", el problema no son las limitaciones del lenguaje, sino las limitaciones de mi diseño.
En cuanto a su respuesta , estoy pensando en las siguientes líneas:
- Solo podría tener un método estático que tome un parámetro de tipo y realice la lógica esperada según el tipo.
- Puede crear un método virtual en su base abstracta, donde especifica el SQL en la clase concreta. De modo que contiene todo el código común requerido por ambos (por ejemplo, para ejecutar el comando y devolver el objeto) mientras encapsula los bits "especializados" (por ejemplo, el SQL) en las subclases.
Prefiero la segunda opción, aunque, por supuesto, depende de ti. Si necesita que entre más detalles, hágamelo saber y estaré encantado de editar / actualizar :)
marxidad Sólo un punto rápido para señalar, Justin ya ha dicho que el SQL varía mucho dependiendo del tipo, así que he trabajado sobre la base de que podría ser algo completamente diferente dependiendo del tipo, por lo tanto, delegándolo a las subclases en cuestión . Mientras que su solución acopla el SQL MUY estrechamente al tipo (es decir, es el SQL).
rptony Buen punto sobre los posibles problemas de sincronización con la estática, uno que no mencioné, así que gracias :) También, es Rob Cooper (no Copper) BTW;): D ( EDIT: solo pensé en mencionarlo en caso de que no fuera así Es un error tipográfico, espero que lo sea, ¡así que no hay problema!