I'm currently working on a project which involves EntityFramework with a (very) rich model (lots of inheritance). I'm currently facing a problem, quite interesting in my opinion.
My model is described in the Domain
namespace. It involves 2 classes that inherit from 1 abstract class.
What I want to do is to be able to perform some specific actions on instances of these classes without knowing their concrete type. I could of course use abstract or virtual methods directly in the model.
But I have many reasons for not doing that:
- I want to keep my model classes as straight POCOs
- In real life, those modifying methods will have dependencies on other objects (that could be database or network retrieving of information), and I want to keep my model without any dependency
After reading on that problem it appeared to me that a solution to this problem is to use the Visitor pattern. But this implies to modify my model, so I thought about finding another solution, and I came to this one.
I've 2 problems with that solution:
- The use of the
dynamic
keyword in the static resolution mechanism - The abstract getter in the
public abstract class PersonModifier
Is this a known pattern? If not, is it clean? If not, is there an appropriate pattern to perform such an operation?
namespace Domain
{
public abstract class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
public class Child : Person
{
public decimal PocketMoney { get; set; }
}
public class Adult : Person
{
public string FavoriteBook { get; set; }
}
}
namespace Modifiers
{
public static class Resolver
{
private static PersonModifier ConcreteResolve(Domain.Child person)
{
return new ChildModifier(person);
}
private static PersonModifier ConcreteResolve(Domain.Adult person)
{
return new AdultModifier(person);
}
private static PersonModifier ConcreteResolve(Domain.Person person)
{
throw new System.Exception("Impossible, can't find an appropriate modifier for type " + person.GetType());
}
public static PersonModifier Resolve(this Domain.Person person)
{
return ConcreteResolve((dynamic)person);
}
}
public abstract class PersonModifier
{
protected abstract Domain.Person Person { get; }
public virtual void PerformSpecificActions()
{
this.Person.Age += 1;
this.Person.Name = "Hi " + this.Person.Name;
}
}
public class ChildModifier : PersonModifier
{
protected Domain.Child _child;
public ChildModifier(Domain.Child child)
{
this._child = child;
}
protected override Domain.Person Person
{
get { return this._child; }
}
public override void PerformSpecificActions()
{
base.PerformSpecificActions();
this._child.PocketMoney *= 10;
}
}
public class AdultModifier : PersonModifier
{
protected Domain.Adult _adult;
public AdultModifier(Domain.Adult adult)
{
this._adult = adult;
}
protected override Domain.Person Person
{
get { return this._adult; }
}
public override void PerformSpecificActions()
{
base.PerformSpecificActions();
this._adult.FavoriteBook = "Utopia";
}
}
}
namespace ConsoleApplication7
{
using Modifiers;
public class Program
{
public static void Main(string[] args)
{
var child = new Domain.Child
{
Age = 12,
Name = "Mark",
PocketMoney = 12.5m
};
Domain.Person childAsPerson = child;
System.Console.WriteLine("Name:" + child.Name + ", Age:" + child.Age + ", PocketMoney:" + child.PocketMoney);
childAsPerson.Resolve().PerformSpecificActions();
System.Console.WriteLine("Name:" + child.Name + ", Age:" + child.Age + ", PocketMoney:" + child.PocketMoney);
var adult = new Domain.Adult
{
Age = 42,
Name = "Lola",
FavoriteBook = "Dorian Gray"
};
Domain.Person adultAsPerson = adult;
System.Console.WriteLine("Name:" + adult.Name + ", Age:" + adult.Age + ", FavoriteBook:" + adult.FavoriteBook);
adultAsPerson.Resolve().PerformSpecificActions();
System.Console.WriteLine("Name:" + adult.Name + ", Age:" + adult.Age + ", FavoriteBook:" + adult.FavoriteBook);
}
}
}
Once again, there is no Console.WriteLine() in my production code.
This is exactly why one of the CodeReview rules states, that you have to post the real code you own and maintain when asking a question. It is impossible to tell, which design is better in your case because your hypothetical code does not behave the same way your real code does. IMHO, if you want to recive a decent answer, you should either come up with an example, which accurately demonstrates both the problem and the usecase, or just post your real code. For now, I voted to close your question. \$\endgroup\$