Come posso mantenere il pattern DI di un writer di risultati, con 2 set di risultati diversi?

1

Nota: sono sicuro di come pronunciare il titolo

Questo è uno pseudo codice del mio progetto su cui sto lavorando. Per concisione ho omesso alcuni dettagli.

Ho due tipi di classi Consumer. Uno è ereditato dall'altro e fa alcune cose extra.

Consumer ha un thread che esegue una coda di attività.

Ho un IProducer che inoltro. Spara gli eventi, Consumer li ascolta e poi aggiunge i metodi da IDoer alla coda.

Ho avuto un'interfaccia IResultsWriter per scrivere i risultati di IDoer. In questo momento si limita a scrivere su un file (utilizzando CSVHelper) ma vorrei consentire altre opzioni in futuro, quindi l'interfaccia, come REST o SQL ecc.

Sto tentando di mantenere la mia API come dipendente conforme alle iniezioni di dipendenza il più possibile.

Il mio problema è che la mia classe Consumer derivata aggiunge alcune proprietà extra al set di risultati. I set di risultati prodotti da IDoer non vengono modificati. (questi sono aggiunti nel consumatore).

Non sembra che la mia unica opzione sia di eliminare la mia interfaccia e passare una classe concreta diversa a ciascun consumatore. Consumer otterrebbe ResultsWriter e Consumer_WithExtra otterrebbe ResultWriter_WithExtra

C'è un altro approccio che posso prendere in modo da poter mantenere il mio DI di ResultsWriter per consentire altri modi di scrivere in futuro.

public class Main
{

    public RunThese()
    {
        Consumer A =  new Consumer(new Producer(), new Doer(), new ResultWriter());
        Consumer_WithExtra = new Consumer_WithExtra(new Producer(), new Doer(), new ResultWriter_WithExtra());

    }

}


public class Consumer
{

    public Consumer(IProducer producer, IDoer Doer, IResultsWriter writer)
    {

    }   

    private void DoTriggeredByProducer()
    {       
        write(results); 
    }


    protected virtual void write(List<DoerResult> results)
    {
        writer.write(results);
    }
}



public class Consumer_WithExtra : Consumer
{

    public string ExtraSomething {get; set;}

    public Consumer_WithExtra(IProducer producer, IDoer doer, IResultsWriter writer) : base (producer, doer, writer)
    {

    }   

    private void DoTriggeredByProducer()
    {
        //** Need to get Extra something to the 
        write(results)

    }

    protected override void write(List<DoerResult> results)
    {

        List<DoerResult_WithExtra> convertedResults =ConvertResults(results)

        writer.write(convertedResults);
    }

    private List<DoerResult_WithExtra> ConvertResults(List<DoerResult> results)
    {
        List<DoerResult_WithExtra> convertedResults = new List<DoerResult_WithExtra>();
        foreach(var r in results)
        {
            convertedResults.Add(new DoerResult_WithExtra(r) { ExtraProperty = this.ExtraSomething }
        }

        return convertedResults;
    }
}


public class DoerResult
{
    public string Name {get; set;}
    public int BlahBlah {get; set;}
    public int BlahBlah {get; set;} 

}

public class DoerResult_WithExtra : DoerResult
{
    public string ExtraProperty {get; set;} 
}

public interface IResultsWriter()
{
    public void write(List<DoerResult> results);    
}

public class ResultWriter : IResultsWriter
{
    public virtual void write(List<DoerResult> results)
    {
        //** at a lost here
    }
}

public class ResultWriter_WithExtra : ResultWriter
{
    public override void write(List<DoerResult> results)
    {
        //** at a lost here
    }
}
    
posta TheColonel26 18.04.2018 - 20:13
fonte

2 risposte

2

Ci sono un paio di cambiamenti che farei. Per prima cosa renderei Consumer una classe astratta che accetta un generico, quindi metti i tuoi due Consumatori in cima a quello.

public abstract class Consumer<T>
{
    protected IResultWriter<T> _writer;
    public Consumer(IResultWriter<T> writer)
    {
        _writer = writer;
    }   

    private void DoTriggeredByProducer()
    {       
        //write(results); 
    }


    public virtual void write(List<T> results)
    {
        _writer.write(results);
    }
}

public class Consumer_WithoutExtra : Consumer<DoerResult>
{

    public Consumer_WithoutExtra(IResultWriter<DoerResult> writer) : base(writer)
    {
    }   

    private void DoTriggeredByProducer()
    {       
        //write(results); 
    }


    public virtual void write(List<DoerResult> results)
    {
        _writer.write(results);
    }
}



public class Consumer_WithExtra : Consumer<DoerResult_WithExtra>
{

    public string ExtraSomething {get; set;}

    public Consumer_WithExtra(IResultWriter<DoerResult_WithExtra> writer) : base(writer)
    {
    }   

    private void DoTriggeredByProducer()
    {
        //** Need to get Extra something to the 
        //write(results);

    }

    public override void write(List<DoerResult_WithExtra> results)
    {
        _writer.write(results);
    }

    private List<DoerResult_WithExtra> ConvertResults(List<DoerResult> results)
    {
        List<DoerResult_WithExtra> convertedResults = new List<DoerResult_WithExtra>();
        foreach(var r in results)
        {
           // convertedResults.Add(new DoerResult_WithExtra(r) { ExtraProperty = this.ExtraSomething });
        }

        return convertedResults;
    }
}

Quindi configurerei IResultWriter anche come generico

public interface IResultWriter<T>
{
    void write(List<T> results);
}

public class ResultWriter : IResultWriter<DoerResult>
{
    public virtual void write(List<DoerResult> results)
    {
        foreach(var r in results)
            Console.WriteLine(r.Name);
    }
}

public class ResultWriter_WithExtra : IResultWriter<DoerResult_WithExtra>
{
    public virtual void write(List<DoerResult_WithExtra> results)
    {
        foreach(var r in results)
            Console.WriteLine(r.Name + ", " + r.ExtraProperty);
    }
}

Questo dovrebbe ripulire il tuo DI in modo da passare il nuovo Consumer_WithExtra e il nuovo ResultWriter_WithExtra, nascondendo i generici, o il nuovo Consumer_WithoutExtra e il nuovo ResultWriter

Ne ho un esempio su link

    
risposta data 19.04.2018 - 03:39
fonte
1

Come ho capito, il tuo problema è che l'unico requisito di IResultWriter è che possa scrivere DoResults mentre tu vuoi che sia in grado di scrivere anche altre cose.

In questo caso puoi:

  1. Sposta la logica di scrittura sulla classe DoResult in modo che lo scrittore possa chiamarla
  2. Chiedi a questa classe di esporre le sue proprietà in modo strongmente tipizzato, in modo che lo Scrittore non debba ricorrere a indovinare il tipo o il riflesso. ad es. List<Property> Doresult.Properties
  3. Chiedi allo scrittore di utilizzare la riflessione per scrivere qualsiasi oggetto
  4. Divertiti con i generici!

Il problema con i generici è che tendono a farsi strada lungo la catena.

Quindi in fondo abbiamo

public interface IResultWriter<T>
{
    void write(List<T> results);
}

Questo significa che ogni redattore dei risultati conosce il tipo passato, quindi può usare la proprietà extra. Ma! devi anche abbinare alcune delle altre classi. Potenzialmente fino alla cima del tuo grafico

public interface Consumer<T>
{

    public Consumer(IProducer<T> producer, IDoer<T> Doer, IResultsWriter<T> writer)
    {

    }   

    private void DoTriggeredByProducer()
    {       
        write(results); 
    }


    protected virtual void write(List<T> results)
    {
        writer.write(results);
    }
}

Tuttavia !! nel complesso suggerirei un approccio alternativo. I costruttori non sono ereditati. quindi non c'è ragione per cui ogni Consumer abbia bisogno di avere un IResultWriter o qualsiasi altra forma di ResultWriter diversa da quella che si eredita da una classe base che ne richiede uno.

Non ereditare, invece hai un'interfaccia e componi.

public class Consumer_WithExtra : IConsumer
{
    public Consumer_WithExtra(
        IProducer producer, 
        IDoer doer
        )

    private void DoTriggeredByProducer()
    {
        //** whatever the hell I want to do with whatever I want to inject
    }
}
    
risposta data 18.04.2018 - 20:54
fonte

Leggi altre domande sui tag