0

My class pseudo code is in the following format:

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();
}

To access the TableSchema methods in DBOperations, all I needed to do was call schema.method1() and so on. However now I need to have multiple implementations of DBOperations which may not all have the same TableSchema methods to operate on (function name might be the same, but implementation is different). For this I was thinking of making a TableSchema interface and have multiple implementations of it based on my requirements along the lines of strategy pattern.

However if I use the strategy pattern, I will need to pass the constructor as well. However the values of the class TableSchema depends on the values I retreive in the class Config. How should I approach my design in this case?

migrated from stackoverflow.com Aug 26 '17 at 0:18

This question came from our site for professional and enthusiast programmers.

  • 1
    What does the code actually do? – Robert Harvey Aug 26 '17 at 1:02
  • @RobertHarvey The Config class basically reads from a text file which contains details about my database table. The Table schema class is used to store those values. Class DBOperations is used to perform certain operations on the table (like read get etc ) – user1692342 Aug 26 '17 at 1:06
  • 1
    I guess I asked the wrong question. What is the overall goal here? – Robert Harvey Aug 26 '17 at 1:16
  • @RobertHarvey I want to understand how I can remove the dependency of tableschema in class Config. SInce I will have 2 implementations of table schema. however the values required for table schema are dependent on Config class – user1692342 Aug 26 '17 at 1:18
  • 1
    You still haven't told me what the point of all this is. Are you sure you're not creating an inner platform? – Robert Harvey Aug 26 '17 at 1:20
1

A factory + strategy looks like a good pattern here BUT don't be fooled... even though it seems simple, the complexity involved in making this versatile is not worth the effort.

Time to take a serious look at IoC (inversion of control) -- specifically DI (dependency injection) for obtaining instances of these classes. An "IoC container" provides the "wiring" implementation for you. There are many flavors -- Spring is one of the more popular options (but it is so much more than just an IoC container).

The idea is you create the concrete instances using your configuration during application startup. The configuration itself can be registered in the IoC or simply passed to the constructor(s) when registering instances of classes that require it.

This will simplify the orchestration needed to obtain one of these custom implementations.

Then each class can be isolated completely from the rest of the system. You'll find it is so much easier for development, testing, and getting things "right" the first time. But, just like the async/await pattern, IoC quickly spreads throughout your entire codebase so be prepared for an awakening.

Once you have things going, you may begin to see that the DBOperations class is no longer necessary at all. Why not just have an ICustomerTable? Your class that uses ICustomerTable gets populated with whatever concrete instance is required but it doesn't matter which instance and it doesn't need to know how to get one. Who creates ICustomerTable and the class that uses it? The IoC container of course.

And all this complexity (or lack thereof) was isolated to the application startup meaning, if startup was successful, you're much more likely to have a working system when that first request comes through.

And from this elevation, you can see what you have just created is a fancy (simple, versatile) Factory + Strategy pattern that can be used for much more than just abstracting away your data access layer.

  • I am using Spring for DI. However in my case, I have my TableSchema class has its values filled out based on the values read by Config class. So I cannot pass the filled in constructor right – user1692342 Aug 26 '17 at 1:19
  • @user1692342 Couldn't you read the config during the application bootstrap and configure the injector then? – plalx Aug 26 '17 at 4:31
0

The key would be to define operations and create objects, populate the data through constructors and then implement factories for configuration, thy to encapsulate data and behavior and you will end up using strategy, template method, factory method, etc. Teh key is SOLID and OO design I suggest the following code:

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;
    }
}
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.