0

I've run into this issue a few times. I have preexisting framework objects that do not share a common ancestor. An example of this would be a class representing a an environment variable and a class representing a key in a config file. Both have a key and a value, and for the intents of my use, that is the part that I'm really concerned about, just the key and value.

If both classes shared a common ancestor I could use that when passing them around and performing operations on them, but they don't. I've managed to handle it clumsily by having a class which accepts and object and has key and value properties that do type checking, but it's gross -- I can pass instances of this class around but don't really like it -- I'd really like it if I could throw compile time errors instead of runtime errors (I tried doing multiple constructors taking advantage of argument overloading but that requires a new constructor for every supported type, which is also gross). I tried doing a generic type in C#, but that didn't really work out because I can't filter to where the item is an EnvironmentVariable OR a ConfigEntry. Is there a better more elegant way to do this?

public class KeyValueAccessibleType
{
  private readonly object backingObject

  public KeyValueAccessibleType(object Backer)
  {
     switch(Backer.GetType().Name())
     {
        case "EnvironmentVariable"
        case "ConfigEntry"
          backingObject = Backer;
          break;
        default:
          throw new NotImplimentedException($"Unable to use backing object of type {Backer.GetType().Name()}")
     }
     backingObject = Backer
  }

  public string GetKey()
  {
     switch(backingObject.GetType().Name())
     {
        case "EnvironmentVariable"
          return (backingObject as EnvironmentVariable).GetConfigKeyFunction()
        case "ConfigEntry"
          return (backingObject as ConfigEntry).GetConfigKeyFunction()
        default:
          throw new NotImplimentedException($"Unable to use backing object of type {backingObject.GetType().Name()}")
     }
  }

  public void SetKey(string Key) {...} // Uses a switch statement like GetKey()
  public string GetValue() {...} // Uses a switch statement like GetKey()
  public void SetValue(string Key) {...} // Uses a switch statement like GetKey()
}
2
  • 1
    I'm not sure I completely understand your question, but it looks like you're describing the Adapter pattern. Change KeyValueAccessibleType to an interface with the methods you need, then implement one concrete class EnvironmentVariableKeyValueAccessibleType and another ConfigEntryKeyValueAccessibleType. You can then have a Factory which instantiates the correct implementation depending on whatever criteria you have. Your clients only depend on the interface, which means you can swap the concrete implementation at any time. Commented Jun 7, 2019 at 16:21
  • @VincentSavard That works perfectly, and I'm duly ashamed at how obvious it is. If you want to post it as an answer I'll accept it.
    – Sidney
    Commented Jun 7, 2019 at 17:39

1 Answer 1

5

This looks like a good use case for the Adapter pattern.

You can change the class KeyValueAccessibleType to an interface:

public interface IKeyValueAccessibleType {
  string GetKey();

  // ...
}

Then, you can have one concrete implementation per your preexisting framework classes:

public class EnvironmentVariableKeyValueAccessibleType : IKeyValueAccessibleType {
  public string GetKey() {
    // env variable impl
  }
}

public class ConfigKeyValueAccessibleType : IKeyValueAccessibleType {
  public string GetKey() {
    // config impl
  }
}

Your clients will code to the interface IKeyValueAccessibleType and will not be required to know the concrete implementation. New implementations may be created as long as they respect the interface.

You may also want to use a Factory to help you instantiate the correct implementation according to whatever criteria you may have.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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