Instead of working against specific types, work against interfaces.
Below I am showing a mix of facade pattern and adapter pattern to 'transform' external data to a well-defined interface (IDocument
), effectively abstracting things your are working on.
Example 1 : query about an interface
Here are the types you'll work against:
public interface IDocument {
string Name { get; set; }
}
public interface IMetadata {
string[] Tags { get; set; }
}
This is your own representation, should you need any:
public class RichDocument : IDocument, IMetadata {
public string Name { get; set; }
public string[] Tags { get; set; }
}
This is the wrapper against external data:
(a bastard mix of facade and/or adapter concepts)
public class ExternalClass {
public string Whatever { get; set; }
}
public class ExternalDocument : IDocument /* only a basic object */ {
private readonly ExternalClass _class;
public ExternalDocument(ExternalClass @class) {
_class = @class;
}
public string Name {
get { return _class.Whatever; }
set { _class.Whatever = value; }
}
}
And a demo on how to use all that:
internal class Demo1 {
public Demo1() {
var documents = new List<IDocument> {
new ExternalDocument(new ExternalClass()),
new RichDocument()
};
foreach (var document in documents){
var name = document.Name;
Console.WriteLine(name);
// see if it implements some interface and do something with it
var metadata = document as IMetadata;
if (metadata != null) {
Console.WriteLine(metadata.Tags);
}
}
}
}
Example 2 : query about a component
This is a bit more involved by pushing the concept to treat everything in an uniform manner, you can find it in .NET framework, game development or whatever ...
Definitions you'll work against:
public interface IContainer {
IList<IComponent> Components { get; }
}
public interface IComponent {
// it can be/do anything
}
Some components you'll query about:
public interface IDocument : IComponent {
string Name { get; set; }
}
public interface IMetadata : IComponent {
string[] Tags { get; set; }
}
Your 'internal' type:
public class Container : IContainer {
public Container() {
Components = new List<IComponent>();
}
public IList<IComponent> Components { get; }
}
Your 'wrapper' against external data:
public class ExternalClass {
public string Whatever { get; set; }
}
public class ExternalContainer : IContainer {
private readonly List<IComponent> _components;
public ExternalContainer(ExternalClass @class) {
_components = new List<IComponent> {new ExternalDocument(@class)};
}
public IList<IComponent> Components {
get { return _components; }
}
}
public class ExternalDocument : IDocument {
private readonly ExternalClass _class;
public ExternalDocument(ExternalClass @class) {
_class = @class;
}
public string Name {
get { return _class.Whatever; }
set { _class.Whatever = value; }
}
}
And a usage example:
public class Demo2 {
public Demo2() {
var containers = new List<IContainer> {
new ExternalContainer(new ExternalClass()),
new Container()
};
foreach (var container in containers) {
// query container for some components
var components = container.Components;
var document = components.OfType<IDocument>().FirstOrDefault();
if (document != null) {
Console.WriteLine(document.Name);
}
var metadata = components.OfType<IMetadata>().FirstOrDefault();
if (metadata != null) {
Console.WriteLine(metadata.Tags);
}
}
}
}
Notes
The problem with inheritance is that it is a very rigid approach and generally once you start doing it and at some point you hit a wall and want to revert, it's hard to get out of it.
By working against abstractions things are more flexible and things are decoupled.
Here are two examples that might incite you to change your approach:
Composition over inheritance
Using Components