Problemi con la progettazione OO per l'applicazione con componenti collegabili

2

Sto cercando di ridefinire un codice brutto e renderlo facilmente estensibile in futuro.

L'applicazione non dovrebbe essere altro che una serie di componenti che hanno input (s) e output (s). I componenti sono concatenati in modo tale che l'input del componente corrente sia l'output di uno (o più) componenti precedenti.

Ecco una rapida panoramica di ciò che ho finora:

  1. Reader

    • Indica un'origine dati
    • Può essere un file su HDD, risorsa online, database, ecc.
  2. Splitter

    • L'input è Reader(s)
    • Divide il contenuto di ciò che il lettore consegna in parti
    • Le uscite sono contenuti divisi di Reader(s)
  3. Model

    • L'input è Splitter(s)
    • Crea un modello di qualcosa basato su Splitter(s) output
    • L'output è silenzioso, ma puoi dire che l'output è uno stato interno che può essere interrogato per i singoli input
  4. Tester

    • Input è un modello
    • Può interrogare un modello per un risultato su alcuni dati
    • L'output è facoltativo, ma nel caso venga utilizzato, è un flusso di dati (queryInput, queryOutput)
  5. Writer

    • L'input è praticamente tutto ciò che produce una collezione di oggetti
    • Scrive quella raccolta ovunque
    • Non so come dovrebbe essere l'output adesso

Quindi, voglio avere la possibilità di collegarli nel seguente modo:

-->Reader-->Splitter-->Model-->Tester-->Writer-->

Tuttavia, questa sarebbe anche una combinazione valida (ovviamente non fa altro che un semplice trasferimento di dati)

-->Reader-->Writer-->

Ora, dato che mi piacerebbe essere in grado di collegare (quasi) tutto a tutto e (possibilmente) creare catene piuttosto lunghe, suppongo che dovrei avere una sorta di interfaccia Pluggable .

Inoltre, quando creo una tale catena di grandi dimensioni, probabilmente vorremmo racchiuderla dietro un Facade . E poiché vorrei che ogni componente (classe) a innesto fosse sostituito da qualche altro, mi viene in mente il modello Strategy .

Ora, poiché ho già menzionato il termine catena qui, mi viene in mente il pattern Chain of Responsibility , nel modo seguente (o simile):

public interface Pluggable<InputType, OutputType> {
    public void consume(Pluggable<?, InputType> previous);
    public Collection<OutputType> produce();
}

Ad esempio, se volessi avere il mio Splitter per dividere un elenco di File s fornito da Reader , potrebbe essere qualcosa del genere:

public class Splitter<InputType, OutputType> implements Pluggable<?, InputType> {
    public void consume(Pluggable<?, InputType> previous) {
        // retrieves for example a list of InputType objects
    }

    public Collection<OutputType> produce() {
        // filters the collection it got and returns a sublist for example
    }
}

Alla fine, i componenti potrebbero apparire in questo modo:

Reader<File, Foo> --> Splitter<Foo, Foo> --> Model<Foo, Void> --> Test<Model, Bar> --> Writer<Bar, DAO>

Non so in quale altro modo descrivere il problema che sto avendo, ma sono sicuro che qualcosa del genere è abbastanza realizzabile. Per un esempio visivo, ecco l'immagine del processo di RapidMiner

Nota che non sto cercando di replicare o copiare Rapid Miner, è solo che il progetto che mi è stato assegnato sembra che possa essere implementato in modo similare.

Apprezzerei qualsiasi aiuto su come progettare un'applicazione di questo tipo.

    
posta ioreskovic 26.02.2015 - 06:48
fonte

2 risposte

3

No, Chain of Responsibility non ha senso qui, perché presuppone che tutti i componenti abbiano la stessa interfaccia. Non credo che il sistema di tipo di Java sia abbastanza buono da renderlo completamente generico, quindi opterei per digitare la cancellazione e una sorta di "gestore" che convoglia l'output di un modulo in input di quello successivo, mentre incapsula la cancellazione.

L'interfaccia del modulo sarebbe simile a questa:

public interface Pluggable {
    public Class inputType();
    public Class outputType();
    public Object handle(Object previous);
}

Il gestore accetta l'elenco di istanze che implementano questa interfaccia, controlla se le loro classi di input / output funzionano e quindi eseguono il piping tra di esse.

Se fosse C #, lo renderemmo ancora più carino creando una classe astratta, che sarebbe generica allo stesso modo della tua interfaccia, implementerei il Pluggable esplicitamente (per nasconderlo dalla vista) e implementare il inputType e outputType con typeof() e crea l'abstract e avvolge il metodo handle nel metodo astratto generico. Non so come sarebbe possibile in Java.

public interface IPluggable
{
    Type InputType { get; }
    Type OutputType { get; }

    Object Handle(Object value);
}

public abstract class Pluggable<TInput, TOutput> : IPluggable
{
    Type IPluggable.InputType { get { return typeof(TInput); } }
    Type IPluggable.OutputType { get { return typeof(TOutput); } }
    object IPluggable.Handle(object value)
    {
        return Handle((TInput)value);
    }

    protected abstract TOutput Handle(TInput value);
}

Ecco come potrebbe apparire la classe "manager". Spiacente di nuovo C #.

public class PluginPipeline
{
    public static PluginPipeline Create(IEnumerable<IPluggable> plugins)
    {
        EnsurePluginsAreInRightOrder(plugins);
        return new PluginPipeline(plugins);
    }

    private static void EnsurePluginsAreInRightOrder(IEnumerable<IPluggable> plugins)
    {
        Type previousType = typeof(object); // first is little special..
        foreach(IPluggable plugin in plugins)
        {
            if (plugin.InputType != previousType)
                throw new Exception("Invalid link in pipeline!");

            previousType = plugin.OutputType;
        }
    }

    private readonly IEnumerable<IPluggable> _plugins;

    private PluginPipeline(IEnumerable<IPluggable> plugins)
    {
        _plugins = plugins;
    }

    public void Execute()
    {
        object previousValue = null;

        foreach(IPluggable plugin in _plugins)
        {
            previousValue = plugin.Handle(previousValue);
        }
    }
}
    
risposta data 26.02.2015 - 08:43
fonte
-2

So che è una domanda vecchia, ma controlla ReactiveX se non l'hai già fatto. L'ho scoperto di recente, e davvero è cambiato il modo in cui vedo disegni come questi. È fondamentalmente una combinazione di osservabili sicuri per tipo, push basato sugli eventi (sincronizzazione o asincrona) e classici pipe-and-filters.

link

Basta scrivere piccoli componenti e farli scattare insieme in tubazioni, come il lego per gli idraulici.

    
risposta data 20.05.2015 - 00:45
fonte