E 'possibile evitare l'inversione dell'astrazione in questo scenario?

6

Sto progettando una libreria basata su web scraping che tenta di fornire un'API a un sito di notizie popolare. Sto rappresentando ciascuno dei suoi articoli come una raccolta di "elementi" ( IElement ), come immagini, video, blocchi di testo, colonne sonore, ecc. Il problema è che non riesco a pensare a nulla che ogni "elemento" abbia in comune.

  • Testo? No, le immagini (e i video e le colonne sonore) non hanno testo.
  • Un URL per accedere al file? No; mentre visiterò un URL remoto per accedere a un'immagine oa un video (poiché non è pratico passarne quelli in giro), non ho bisogno di andare su un URL per ottenere il testo (posso solo passarlo come stringa) .

L'unica cosa che posso pensare è avere un membro che descriva il tipo di elemento, ma che potrebbe potenzialmente portare a un'API molto intuitiva. Non voglio che la mia API finisca in questo modo:

// API code
enum ElementType
{
    Text, Hyperlink, Image, Video, Soundtrack
}

interface IArticle
{
    IEnumerable<IElement> Contents { get; }
}

interface IElement
{
    ElementType Type { get; }
}

class TextElement : IElement { /*details*/ }
class ImageElement : IElement { /*details*/ }

// .. and so on

// Usage in app code
foreach (var element in article.Contents)
{
    switch (element.Type)
    {
        case ElementType.Text:
            RenderText(((TextElement)element).Text);
            break;
        case ElementType.Image:
            DisplayImage(GetImageFromUri(((ImageElement)element).ImageUri));
            break;
        // and so on
    }
}

Come puoi vedere, in questo scenario viene aggiunta molta verbosità perché l'utente deve 1) attivare il tipo di elemento, 2) controllare per ogni ElementType , e poi 3) downcast per usare l'implementazione specifica caratteristiche.

Esiste un'alternativa al controllo / downcasting del tipo di runtime disordinato in questo scenario? In che modo altre app client (ad esempio i client Facebook / Twitter) gestiscono questo tipo di problema?

    
posta James Ko 21.06.2015 - 23:42
fonte

2 risposte

5

Una parte del tuo problema è che stai cercando di reinventare il modello di oggetto documento HTML. A meno che il contenuto non abbia bisogno di qualche elaborazione speciale, sarebbe meglio fornire i frammenti HTML.

Il type switch può essere eliminato utilizzando il pattern visitor: definiamo un'interfaccia visitatore IElementVisitor che elenca tutti i casi e aggiungiamo un metodo void Accept(IElementVisitor) all'interfaccia IElement che verrà implementata in ogni tipo di calcestruzzo come

void Accept(IElementVisitor v) {
  v.visit(this);
}

Quindi, un RenderVisitor sarà simile a

class RenderVisitor : IElementVisitor {
  void Render(IElement e) { e.Accept(this); }
  void Visit(TextElement e) {
    RenderText(e);
  }
  void Visit(ImageElement e) {
    RenderImage(e)
  }
  ...
}

Al posto dello switch, ora avremmo new RenderVisitor().Render(element); .

    
risposta data 22.06.2015 - 06:33
fonte
2

Usa OOP, se puoi. Quando si chiama un metodo di interfaccia o un metodo di classe base sovrascritto, la lingua esegue il cast dell'oggetto sul tipo di implementazione e lo fornisce come puntatore this . Non avrai bisogno di casting o di un'istruzione switch.

In questo caso, potresti avere un metodo come public void DoIt () , che è implementato in ogni classe in modo diverso. Il codice nei casi di switch si sposta nell'implementazione dei sottotipi specifici di DoIt ()

    
risposta data 22.06.2015 - 00:25
fonte

Leggi altre domande sui tag