Estoy escribiendo un analizador microformats en C# y estoy buscando algunos consejos de refactorización. Este es probablemente el primer proyecto "real" que he intentado en C# durante algún tiempo (programo casi exclusivamente en VB6 en mi trabajo diario), así que tengo la sensación de que esta pregunta puede convertirse en la primera de una serie ;-)¿Cómo se podría refactorizar un "tipo de interruptor" para que sea polimórfico si no se controlan los tipos implicados?
Permítanme proporcionar algunos antecedentes sobre lo que tengo hasta ahora, para que mi pregunta (con suerte) tenga sentido.
En este momento, tengo una sola clase, MicroformatsParser
, haciendo todo el trabajo. Tiene un constructor sobrecargado que le permite pasar un System.Uri
o un string
que contiene un URI: al momento de la construcción, descarga el documento HTML en el URI dado y lo carga en un HtmlAgilityPack.HtmlDocument
para una fácil manipulación por parte de la clase.
La API básica funciona así (o la voluntad, una vez que termine el código ...):
MicroformatsParser mp = new MicroformatsParser("http://microformats.org");
List<HCard> hcards = mp.GetAll<HCard>();
foreach(HCard hcard in hcards)
{
Console.WriteLine("Full Name: {0}", hcard.FullName);
foreach(string email in hcard.EmailAddresses)
Console.WriteLine("E-Mail Address: {0}", email);
}
El uso de genéricos aquí es intencional. Me inspiré en la forma en que funciona la biblioteca Microformats en Firefox 3 (y la gema Ruby mofo
). La idea aquí es que el analizador hace el trabajo pesado (encontrar el contenido real del microformato en el HTML), y las propias clases de microformato (HCard
en el ejemplo anterior) básicamente proporcionan el esquema que le dice al analizador cómo manejar los datos que encuentra.
El código para la clase HCard
debería hacer esto más claro (tenga en cuenta que este es un no una implementación completa):
[ContainerName("vcard")]
public class HCard
{
[PropertyName("fn")]
public string FullName;
[PropertyName("email")]
public List<string> EmailAddresses;
[PropertyName("adr")]
public List<Address> Addresses;
public HCard()
{
}
}
Los atributos aquí son utilizados por el programa de análisis para determinar cómo rellenar una instancia de la clase con datos de un documento HTML. El analizador hace lo siguiente cuando se llama GetAll<T>()
:
- Comprueba que el tipo
T
tiene un atributoContainerName
(y no es blanco) - Búsquedas el documento HTML para todos los nodos con un atributo
class
que coincide con elContainerName
. Llámalos los "nodos del contenedor". - Para cada nodo contenedor:
- utiliza la reflexión para crear un objeto de tipo
T
. - Obtener los campos públicos (un
MemberInfo[]
) para el tipo deT
a través de la reflexión - Para cada campo
MemberInfo
- Si el campo tiene un atributo
PropertyName
- Obtener el valor de la propiedad microformato correspondiente de la HTML
- Inyecte el valor encontrado en el HTML en el campo (es decir,establecer el valor del campo en el objeto de tipo
T
creada en el primer paso) - Añadir el objeto de tipo
T
a unList<T>
- Si el campo tiene un atributo
- Volver al
List<T>
, que ahora contiene un montón de microformato objetos
- utiliza la reflexión para crear un objeto de tipo
estoy tratando de encontrar una mejor manera de poner en práctica el paso en negrita . El problema es que Type
de un campo determinado en la clase de microformato no solo determina qué nodo buscar en el HTML, sino también cómo interpretar los datos.
Por ejemplo, volviendo a la clase HCard
I definida anteriormente, la propiedad "email"
está obligado a EmailAddresses
el campo, que es un List<string>
. Después de que el analizador encuentre todos los nodos secundarios "email"
del nodo padre "vcard"
en el HTML, tiene que ponerlos en un List<string>
.
Lo que es más, si quiero que mi HCard
para poder devolver la información del número de teléfono, probablemente me gustaría ser capaz de declarar un nuevo campo de tipo List<HCard.TelephoneNumber>
(que tendría su propio atributo ContainerName("tel")
) para mantener esa información, porque puede haber múltiples "tel"
elementos en el HTML, y el formato "tel"
tiene sus propias subpropiedades. Pero ahora el analizador necesita saber cómo poner los datos del teléfono en un List<HCard.TelephoneNumber>
.
El mismo problema se aplica a Float
S, DateTime
S, List<Float>
S, List<Integer>
S, etc.
La respuesta obvia es tener el interruptor analizador del tipo de campo, y hacer las conversiones apropiadas para cada caso , pero quiero evitar una declaración switch
gigante. Tenga en cuenta que no estoy planeando hacer que el analizador soporte todos los posibles Type
existentes, pero querré que maneje la mayoría de los tipos escalares, y las versiones List<T>
de ellos, junto con la capacidad de reconocer otras clases de microformato (para que un microformato la clase puede estar compuesta de otras clases de microformato).
¿Algún consejo sobre la mejor manera de manejar esto?
ya que el analizador tiene que manejar tipos de datos primitivos, no creo que pueda añadir polimorfismo en el nivel de tipo ...
Mi primera idea era utilizar la sobrecarga de métodos, por lo que tendría una serie de un GetPropValue
se sobrecarga como GetPropValue(HtmlNode node, ref string retrievedValue)
, GetPropValue(HtmlNode, ref List<Float> retrievedValue)
, etc. pero me pregunto si hay un mejor enfoque para este problema.
También podría intentar http://refactormycode.com/ –