Come affrontare la progettazione della strategia in OOP.

0

Lo pseudo codice della mia classe è nel seguente formato:

public class Config {
    final private Map<String, TableSchema> schemas;

    public Config() {
        schemas = parseSchemas();
    }
    public TableSchema getSchema(String tableName) {
        TableSchema tableSchema = schemas.get(tableName);
        if(/*Based on certain conditions*/) {
            return new TableSchema(/*Pass some values*/)
        }
        return tableSchema;
    }
    Map<String, TableSchema> parseSchemas() {
        //Read some config files 
        tableSchema = new TableSchema(/*Pass some values based on the read config files*/);
        schemas.put(tableName, tableSchema);
    }
}

public class DBOperations {
    private final TableSchema schema;
    public DBOperations(Config config, String tableName) {
        schema = config.getSchema(tableName);
    }
}

public class TableSchema {
    method1();
    method2();
}

Per accedere ai metodi TableSchema in DBOperations, tutto ciò che dovevo fare era chiamare schema.method1() e così via. Tuttavia ora ho bisogno di avere più implementazioni di DBOperations che potrebbero non avere tutti gli stessi metodi TableSchema su cui operare (il nome della funzione potrebbe essere lo stesso, ma l'implementazione è diversa). Per questo ho pensato di creare un'interfaccia TableSchema e ho implementato molteplici implementazioni basate sulle mie esigenze lungo le linee del modello strategico.

Tuttavia, se utilizzo il modello di strategia, dovrò passare anche il costruttore. Tuttavia i valori della classe TableSchema dipendono dai valori che ritengo nella classe Config. Come dovrei affrontare il mio design in questo caso?

    
posta user1692342 26.08.2017 - 02:15
fonte

2 risposte

1

Una fabbrica + strategia sembra un buon modello qui MA non farti ingannare ... anche se sembra semplice, la la complessità necessaria per rendere questo versatile non vale la pena.

È tempo di dare un'occhiata seria a IoC (inversione del controllo) -  in particolare DI (dipendenza da iniezione) per ottenere istanze di queste classi. Un "contenitore IoC" fornisce l'implementazione "cablaggio" per te. Ci sono molti gusti: Spring è una delle opzioni più popolari (ma è molto più di un semplice contenitore IoC).

L'idea è di creare istanze concrete usando la tua configurazione durante l'avvio dell'applicazione. La configurazione stessa può essere registrata in IoC o semplicemente passata al costruttore (o ai costruttori) durante la registrazione delle istanze delle classi che lo richiedono.

Questo semplificherà l'orchestrazione necessaria per ottenere una di queste implementazioni personalizzate.

Quindi ogni classe può essere isolata completamente dal resto del sistema. Scoprirai che è molto più facile per lo sviluppo, il test e ottenere le cose "giuste" la prima volta. Ma, proprio come lo schema asincrono / atteso, IoC si diffonde rapidamente nell'intera base di codice in modo da essere pronto per un risveglio.

Una volta che le cose stanno andando, potresti iniziare a vedere che la classe DBOperations non è più necessaria affatto. Perché non avere solo ICustomerTable ? La classe che utilizza ICustomerTable viene popolata con qualsiasi istanza concreta richiesta ma non importa quale istanza e non è necessario sapere come ottenerne una. Chi crea ICustomerTable e la classe che lo usa? Il contenitore IoC, naturalmente.

E tutta questa complessità (o la sua mancanza) è stata isolata dal significato di avvio dell'applicazione, se l'avvio ha avuto successo, è molto più probabile che abbia un sistema funzionante quando questa prima richiesta arriva.

E da questa elevazione, puoi vedere ciò che hai appena creato è un modello di fantasia Fabbrica + strategia (semplice, versatile) che può essere utilizzato per molto più che semplicemente astrarre il tuo livello di accesso ai dati.

    
risposta data 26.08.2017 - 03:04
fonte
0

La chiave sarebbe definire le operazioni e creare oggetti, popolare i dati attraverso costruttori e quindi implementare le fabbriche per la configurazione, il tuo per incapsulare dati e comportamenti e finirai per usare strategia, metodo template, metodo factory, ecc. è un design SOLIDO e OO Suggerisco il seguente codice:

namespace DesignPatterns.TableSchemaModel
{
    /// <summary>
    /// Define a contract
    /// </summary>
public interface ITableSchema
{
    void Method1();
    void Method2();
}

/// <summary>
/// Use base class as template, you can implement some behavior, etc
/// </summary>
public abstract class TableSchemaTemplate : ITableSchema
{
    public abstract void Method1();
    public abstract void Method2();
}

public class TableSchemaSql : TableSchemaTemplate
{
    private readonly int otherParameter;
    private readonly string sqlParameters;

    public TableSchemaSql(string sqlParameters, int otherParameter)
    {
        this.sqlParameters = sqlParameters;
        this.otherParameter = otherParameter;
    }
    public override void Method1()
    {

    }

    public override void Method2()
    {

    }
}

public class JsonShema : TableSchemaTemplate
{
    private readonly byte otherParameterType;
    private readonly string jsonProperty;

    public JsonShema(byte otherParameterType, string jsonProperty)
    {
        this.jsonProperty = jsonProperty;
        this.otherParameterType = otherParameterType;
    }

    public override void Method1()
    {

    }

    public override void Method2()
    {

    }
}

public sealed class DbOperations
{
    private readonly ITableSchema schema;

    /// <summary>
    /// Inject TableSchema interface
    /// </summary>
    /// <param name="schema"></param>
    public DbOperations(ITableSchema schema)
    {
        this.schema = schema;
    }

    public void Execute()
    {
        schema.Method1();
        schema.Method2();
    }
}

public interface ITableSchemaFetcher
{
    ITableSchema Fetch();
}

public sealed class TableSchemaSqlFetcher : ITableSchemaFetcher
{
    private readonly string sqlParameters;
    private readonly int otherParameters;
    public TableSchemaSqlFetcher()
    {
        sqlParameters = "Get from constructor or from config";
        otherParameters = 0;
    }
    public ITableSchema Fetch()
    {
        return new TableSchemaSql(sqlParameters, otherParameters);
    }
}

public sealed class JsonSchemaFetcher : ITableSchemaFetcher
{
    private readonly byte otherParameterType;
    private readonly string jsonProperty;

    public JsonSchemaFetcher()
    {
        //Inject paramenters, contruct object
    }

    public ITableSchema Fetch()
    {
        return new JsonShema(otherParameterType, jsonProperty);
    }
}

internal class ConfigFactory
{
    private readonly Dictionary<string, ITableSchemaFetcher> schemas;

    public ConfigFactory(Dictionary<string, ITableSchemaFetcher> schemas)
    {
        this.schemas = schemas;
    }

    public ITableSchemaFetcher GetByKey(string key)
    {
        ITableSchemaFetcher schema = null;
        schemas.TryGetValue(key, out schema);
        return schema;
    }
}

public class TableSchemaConfig
{
    private readonly ConfigFactory configFactory;
    private readonly ITableSchema schema;

    public TableSchemaConfig(Dictionary<string, ITableSchemaFetcher> schemas, string key)
    {
        configFactory = new ConfigFactory(schemas);
        schema = configFactory.GetByKey(key).Fetch();
    }

    public void ExecuteSomething()
    {
        schema.Method1();
        schema.Method2();
    }
}

public class ClientCode
{
    public void Execute()
    {
        //Get from IOC
        var config = new TableSchemaConfig(GetDictionaries(), "Sql");
        config.ExecuteSomething();
    }

    private Dictionary<string, ITableSchemaFetcher> GetDictionaries()
    {
        var dictionary = new Dictionary<string, ITableSchemaFetcher>();
        dictionary.Add("Sql", new TableSchemaSqlFetcher());
        dictionary.Add("json", new JsonSchemaFetcher());
        return dictionary;
    }
}
}
    
risposta data 07.09.2017 - 00:02
fonte

Leggi altre domande sui tag