I have a main object which has some properties and methods. This object can have multiple parts. These parts are required. The amount of these parts is variable.
Each part has different properties and is referenced to the main object.
To accomplish this in my GUI I have a tabcontrol. The first tab is the main object. The other tabs describes the main object further. These 'other' tabs are the parts I mentioned above.
I am trying to design an architecture, but I can't figure I hope yoy can help me.
As suggested from the answers, the part-tabs inherit from an interface. But how does the main know about it's parts? The parts can't be hardcoded because it is variable. To hardcode the parts is a violation of the OCP principle.
Also, when loading the main object, again, how does it knows about it parts? I have to 'register' them somewhere, but where?
Create interface for your parts, that can have reference to main object. And main object will contain collection of parts as Collection<IPart>
class MainObject
{
Collection<IPart> Parts {get;set;}
}
interface IPart
{
MainObject MainObject {get;set;}
}
class SomePartImpl : IPart
{
//properties of this IPart implementation
}
This classes is entities. Your data service must implement logic for saving and cascade operations.
Sample wcf service(from my project):
[EnableClientAccess]
public class ModelService : LinqToEntitiesDomainService<dpirtEntities>
{
public void InsertZone(Zone zone)
{
if ((zone.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(zone, EntityState.Added);
}
else
{
this.ObjectContext.Zones.AddObject(zone);
}
}
public void UpdateZone(Zone currentZone)
{
Zone originalZone = this.ChangeSet.GetOriginal(currentZone);
if ((currentZone.EntityState == EntityState.Detached))
{
if (originalZone != null)
{
this.ObjectContext.Zones.AttachAsModified(currentZone, originalZone);
}
else
{
this.ObjectContext.Zones.Attach(currentZone);
}
}
foreach (Document doc in this.ChangeSet.GetAssociatedChanges(currentZone, o => o.Documents))
{
ChangeOperation op = this.ChangeSet.GetChangeOperation(doc);
switch (op)
{
case ChangeOperation.Insert:
if ((doc.EntityState != EntityState.Added))
{
if ((doc.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(doc, EntityState.Added);
}
else
{
this.ObjectContext.AddToDocuments(doc);
}
}
break;
case ChangeOperation.Update:
this.ObjectContext.Documents.AttachAsModified(doc, this.ChangeSet.GetOriginal(doc));
break;
case ChangeOperation.Delete:
if (doc.EntityState == EntityState.Detached)
{
this.ObjectContext.Attach(doc);
}
this.ObjectContext.DeleteObject(doc);
break;
case ChangeOperation.None:
break;
default:
break;
}
}
}
public void DeleteZone(Zone zone)
{
if ((zone.EntityState == EntityState.Detached))
{
this.ObjectContext.Zones.Attach(zone);
}
this.ObjectContext.Zones.DeleteObject(zone);
}
}
Have a List or Dictionary in your Main class and store the references to the different objects.
For example:
All the tabs implement an interface called IScreenTab.
class MainTab : IScreenTab
{
// Store a map of scree name to screen object
// You can also just use a List<IScreenTab>
private Dictionary<string, IScreenTab> m_OtherScreens;
// Your implementation goes here
public MainTab(){ }
public MainTab(List<IScreenTab> screenTabList){ }
public AddTab(string screenName, IScreenTab screenTabObj){ }
}
I've done something similar in the past, and I decoupled my GUI from my domain design by using an IoC container. In my code I used StructureMap, which was very easy to adopt.
I had exactly the same setup in which there was an 'editor' which contained a number of 'tabs'. Each tab could either contain some different view of my 'object' or it could show an item from collections stored within the 'object'. So there were a number of static and variable tabs.
So, I needed two things.
1. A way to create an editor, with the correct number of tabs.
2. A way to create the tab, plus all it's controls.
So, I created an interface for each, which looked loosely like this.
public interface IEditorFactory<TObject>
{
Editor CreateEditor(TObject instance);
}
public interface ITabEditorFactory<TObject>
{
void CreateTab(TObject instance, Editor parent);
}
I'll leave Editor up to your imagination. By in my app it was a custom UserControl, with various features and behaviour.
Next, imagine we had a Person, who had personal info, an Address an multiple contracts.
public class Person
{
public string Title { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public string EmployeeNumber { get; set; }
public string NationalInsuranceNumber { get; set; }
public Address Address { get; set; }
public IEnumerable<Contract> Contracts { get; }
}
My app wanted to display one 'Personal Details' tab, one 'Employment' tab, one 'Address' tab and multiple 'Contract' tabs.
I implemented the following.
public class PersonalTab : ITabEditorFactory { ... }
public class EmployeeTab : ITabEditorFactory { ... }
public class AddressTab : ITabEditorFactory { ... }
public class ContractTab : ITabEditorFactory { ... }
Notice how #1 and #2 implement the same ITabEditorFactory. That's because they both display different aspects of the Person.
But before I had implemented those I implemented the PersonEditor
public class PersonEditor : IEditorFactory { ... }
It was good that I implemented this first, as it forced de-coupling my editor factory from all the tab factories. I wouldn't accidentally slip in any references to concrete classes. My editor factory just knew how to ask for a ITabEditorFactory<> for the Person, Address and Contract classes.
My final solution was a little more complicated than I outlined above, as it also covered Editor re-use for different instances, how to handle multiple (or no) tab editors for any a single class, and being able to define security on a tab-by-tab basis.
The end result was that I had a GUI model that was decoupled from my domain, and was extensible without requiring me to change a single line of existing code.
Lovely jubbly.
Related
Problem
I have a design issue I can't solve clevely. I'm sure there's an elegent solution, but I can't figure out how achieve it. I still managed to my my code work, but the result is ugly, and I want to learn better designs.
I did my best to provide a minimal implementation with only the bare minimum. Some aspects might therefore look weird. I hope I will get myself clear.
Context
So first, I have these simple classes that both implement the same interface:
public interface Human
{
string getName();
}
public class Adult : Human
{
public Adult(string name, string job)
{
Name = name;
Job = job;
}
public string Name { get; set; }
public string Job { get; set; }
public string getName()
{
return Name;
}
}
public class Child : Human
{
public Child(string name, string toy)
{
Name = name;
Toy = toy;
}
public string Name { get; set; }
public string Toy { get; set; }
public string getName()
{
return Name;
}
}
I use those classes in another, more complex class, that basically have the folloing structure:
class MasterClass
{
public string Name;
public string Job;
public string Toy;
private ObservableCollection<Adult> ListOfAdults;
private ObservableCollection<Child> ListOfChildren;
private ObservableCollection<Human> CurrentList; // Will point to one of the above list
public void InitiateLists()
{
// Populate above lists with data
}
public Human CurrentHuman;
public void ManageAdults()
{
CurrentList = new ObservableCollection<Human>(ListOfAdults);
}
public void ManageChildren()
{
CurrentList = new ObservableCollection<Human>(ListOfChildren);
}
public void setOtherHuman()
{
// Sets CurrentHuman as another adult/child according to currently managed list
}
public void SetManager(string newType)
{
switch (newType)
{
case "adult":
ManageAdults();
break;
case "child":
ManageChildren();
break;
}
}
void UpdateInfo()
{
// Set Name and Toy/Job according to currently managed human
}
void PrintInfo()
{
// Print Name and Toy/Job according to currently managed human
}
}
This is the skeleton of my current implementation, with aspects I can't modify due to other constraints. In this class, I want the methods PrintInfo() and UpdateInfo() to behave differently depending if the CurrentHuman is an Adult or a Child.
So far
I managed to make it work with a swich-case in both methods and some cast. Like this:
void UpdateInfo(string currentType)
{
Name = CurrentHuman.getName();
switch (currentType)
{
case: "adult":
Job = ((Adult) CurrentHuman).Job;
break;
case: "child":
Toy = ((Child) CurrentHuman).Toy;
break;
}
}
This is really not ideal though. In my actual design, I have a lot more types, and other methods that behave differently according to the type of the CurrentItem. So I'm now drowning in switch-cases. This makes my code messy, duplicated and very hard to maintain.
Possible solution with interfaces
Since I just discovered them, I thought I could use interfaces. I did my best, but couldn't get a solution to work.
I imagined a simple interface like so:
public interface IUpdater
{
void UpdateData(); // Takes the values from CurrentHuman and store them in the private members Name and Job/Toy depending on current type.
void Print();
}
I also implement my interface in two different ways:
class AdultUpdater : IUpdater
{
public void Print()
{
// Print Adult stuff only
}
public void UpdateData()
{
// Update Adult data only.
}
}
and a similar class ChildUpdater : IUpdater. They both implement the dedicated code for the Child/Adult.
If I declare a private IUpdater Updater as private member of my MasterClass, this allows me to change my methods ManageAdult()and ManageChildren() like this:
public void ManageAdults()
{
CurrentList = new ObservableCollection<Human>(ListOfAdults); // Same as before
Updater = new AdultUpdater(); // Specify implementation to use
}
(similar for ManageChildren()).
I can then brilliantly implement my UpdateInfo() like this:
void UpdateInfo()
{
Updater.UpdateData();
}
and my PrintInfo() method like this:
void PrintInfo()
{
Updater.Print();
}
Interfaces are truly amazing! Oh but wait...
New problem
This seems very promising. My problem is that I don't know how to implement the code of my class AdultUpdater() and class ChildUpdater(). More precisely, these two classes need to access private members of the MasterClass, namely the members Name, Job and Toy. The UpdateData() need to modify them, and the Print() need to display them. I feel so stupidely stuck at this point, so close to a very elegent solution. Does someone have an idea how to finalize this design?
Thank you for reading... I'm sorry if this issue could have been reduced to a more concise question. I had the feeling some details about my current implementation were necessary to get a suitable answer.
As I see it, you are trying to "manage" your humans. Just let them self do the job.
f.e. Don't Print from the manager/masterclass and decide, what to print, but get the printed data (even if only parts, but the parts that are different) from humans and just put it all together in the masterclass.
Use Polymorphism for you. They (your objects/humans) already know, what to print out or update, so let them do the Job. Try to spread the work, instead of pulling it all into one class.
Here is what I advise,
You have a Human class which corresponds to your IHuman, something like this
public class Human : IHuman
{
public Human(string name, string job)
{
Name = name;
Job = job;
}
public string Name { get; set; }
public string Job { get; set; }
public string getName()
{
return Name;
}
}
Your adult and child class would then Inherit the Human class and pass back the constructor values.
public Adult(string name, string job) : base (name, job)
{
}
When you create an instance of adult, you will pass in the name and job, and you can call getName because it will be inherited from the Human class.
I'm not so sure that DryIoc is worth my time. It looks like lightweight and supported well in cross-platform environment (with Xamarin). But I feel a bit difficult working with it (in term of exploring everything myself).
The DryIoc community is also not very large (and by reading through some answers about DryIoc, I recognized that looks like there is only the author jumping in and giving the answers). Here is my problem as mentioned in the title. Suppose I have 2 ViewModel classes, the second has a property which should always match (mapped) with a property of the first, like this:
public class ParentViewModel {
}
public class FirstViewModel {
public FirstViewModel(ParentViewModel parent){
}
public string A {
//...
}
}
public class SecondViewModel {
public SecondViewModel(ParentViewModel parent){
}
public string A {
//...
}
}
Now I can use dryioc to register singleton for both ViewModels BUT for the second one, I also need to inject the property A with value from the first's property A as well.
container.Register<ParentViewModel>();
container.Register<FirstViewModel>(reuse: Reuse.Singleton, made: Made.Of(() => new FirstViewModel(Arg.Of<ParentViewModel>())));
container.Register<SecondViewModel>(reuse: Reuse.Singleton, made: Made.Of(() => new SecondViewModel(Arg.Of<ParentViewModel>())));
So as you can see the first registration should be fine because no property dependency is required. However the second one should have its A property depend on the A of the first one.
Really I cannot explore this myself. Injecting properties of some registered type is fine (and at least I know how to do that), but here the injected value is another property of some registered type.
Here is the straightforward way (but may be not the best one) to achieve that:
using System;
using DryIoc;
public class Program
{
public static void Main()
{
var container = new Container();
container.Register<ParentViewModel>();
container.Register<FirstViewModel>(Reuse.Singleton);
container.Register<SecondViewModel>(Reuse.Singleton,
made: PropertiesAndFields.Of.Name("A", r => r.Container.Resolve<FirstViewModel>().A));
var firstVM = container.Resolve<FirstViewModel>();
firstVM.A = "blah";
var secondVM = container.Resolve<SecondViewModel>();
Console.WriteLine(secondVM.A); // should output "blah"
}
public class ParentViewModel {
}
public class FirstViewModel {
public FirstViewModel(ParentViewModel parent) { }
public string A { get; set; }
}
public class SecondViewModel {
public SecondViewModel(ParentViewModel parent) {}
public string A { get; set; }
}
}
In my opinion, the better and more simple way will the inversion of control: creating the A outside of both VMs and then injecting them into both.
I am writing a program that synchronizes playlists among different streaming services, the problem is every service uses different structures and functions.
I want to make it "modular" so i can add new services and syncronize them with the others without programming them for every service i have already in the application and the best idea i came out with is to implement it by using a sort of intermediate language (or intermediate models), e.g.
namespace Service1
{
class Service1Album
{
public string ID { get; set; }
public Service1Artist Artist { get; set; }
//Some other props...
public IntermediateAlbum ToIntermediate()
{
//conversion...
}
}
class Service1Artist
{
//Some props...
public string ID { get; set; }
public IntermediateArtist ToIntermediate()
{
//conversion...
}
}
}
namespace Intermediate
{
class IntermediateArtist
{
//Props that every service has in common...
}
class IntermediateAlbum
{
//Props that every service has in common...
}
}
In this way every service I implement accept as parameter for its functions Intermediate models and outputs his own models that can be converted.
Service1Album album = service1.GetAllAlbums()[0];
IntermediateAlbum intermediateAlbum = album.ToIntermediate();
service2.AddAlbum(intermediateAlbum);
service3.AddAlbum(intermediateAlbum);
Is there a way I can implement this more elegantly? And if so, is there a way I can inherit every service from something like a ServiceContainer that abstracts every service like this?
var sList = new List<ServiceContainer>{};
sList.Add(new Service1());
sList.Add(new Service2());
foreach (var service in sList)
{
service.addAlbum(new IntermediateAlbum()
{
//properties
});
}
Luca - Welcome! You're discovering the need for some design patterns in your code. That's really great - it means things are getting complex enough that you can start to lean on the patterns others have spent years refining in building your solution.
#1 - Switch to using Interfaces + Design Patterns
If I were you, I'd start by abstracting everything to interfaces (eg - you'd run your work in each Service against IAlbum's and IArtist's instead of concrete implementations).
Then, once your interfaces are in place, take a look at the .net code for the gang-of-four patterns to figure out how to layout the work.
#2 - The basics
There's a lot of code to write, but the skeleton might end up looking like this
namespace MusicService
{
public interface IAlbum {
//common album properties/methods
}
public interface IArtist {
//common artist properties/methods
}
internal abstract class AlbumBase:IAlbum {
// implement common functions and properties if you have them...
// otherwise, skip this
}
internal abstract class ArtistBase:IAlbum {
// implement common functions and properties if you have them...
// otherwise, skip this
}
}
namespace MusicService.AppleMusic
{
internal class Album:AlbumBase
{ //OR Album:IAlbum if you skipped that AlbumBase thing
//code apple music specific stuff here ...
// think of this as a "mask" that the apple album is
// wearing so that it can pretend to be an iAlbum
}
internal class Artist:ArtistBase
{
//same comments apply here
}
}
namespace MusicService.Spotify
{
internal class Album:AlbumBase
{
//GO and do liekwise with Spotify
}
internal class Artist:ArtistBase
{
//GO and do liekwise with Spotify
}
}
#3 - Implementing the actual SYNC
Now that you have the code you need for your various music services (neatly arranged, so your fellow programmers don't have to dig through your brain to figure out what you were thinking), you can code your SyncService.
I don't want to spoil the fun (sync service could use the decorator pattern or it could be a sort of composite -- not sure what your sync code needs to do), but your final sync code could be as easy as:
var myService = new SyncService();
myService.AddAppleMusic();
myService.AddSpotify();
myService.Sync();
Spoiling the Fun
OK. Fine. Here's what I'd do.
namespace MusicService {
public interface IService {
//propably a List<IAlbum> and List<IA> somewhere in here
void Sync();
}
public class SyncService {
internal List<IService> _services;
public void AddService(IService musicService) {
if(_services==null){_services=new List<IService>();}
_services.Add(musicService);
}
public void Sync() {
foreach(IService ms in _services) {
ms.Sync();
}
}
}
}
namespace MusicService.AppleMusic {
internal AppleSyncService:IService {
public AppleSyncService() {
//Do your apple-specific initializations here
}
public void Sync() {
//apple-sync
}
}
internal class ExtendService(){
public static void AddAppleMusic(this SyncService syncAgent) {
syncAgent.AddService(new AppleSyncService());
}
}
}
Obviously - none of that code compiles, and coding in notepad is probably a bad idea. But, it gives you a pattern-based alternative to your sample code above. AND - if you add a third music service, you don't run the risk of breaking apple and spotify just to wedge in that new one!
Good luck. Sounds like a fun project.
Every time I talk to experienced programmers, they talk about having global variables being a bad practice because of debugging or security exploits. I have a simple List of strings I want to load from a a textfile and access across different methods in my form. Before, I would simply initialize said variable at the top, inside of my form class and use it across methods. I always try to reduce that practice when I can and only initialize those variables when I really need them. Is it a bad practice to do this or do more experienced programmers do this too? Is there a standard design pattern method of doing this so you don't have to use "global variables" at the top of your form?
As you're talking about C# and it's a fully-object-oriented programming language, there's no way to declare global variables.
In an OOP language like C#, a bad practice can be simulating global variables using static classes:
public static class Global
{
public static string Value1 { get; set; }
public static int Value2 { get; set; }
}
...to later get or set these values from other classes. Definitely, this a bad practice because state should be held by specific and meaningful objects.
Usually, in a perfect/ideal OOP solution, you should pass such values from class to class using constructors:
public class X
{
public int Value1 { get; set; }
public void DoStuff()
{
Y y = new Y(this);
y.DoChildStuff();
}
}
public class Y
{
public class Y(X parent)
{
Parent = parent;
}
public X Parent { get; }
public void DoChildStuff()
{
// Do some stuff with Parent
}
}
Or also, you might pass states providing arguments to some method:
public class Y
{
public void DoChildStuff(X parent)
{
// Do some stuff with "parent"
}
}
Since you're passing states with reference types, if any of the methods in the chain decide to change Parent.Value1 with another value, all objects holding a reference to the same X object will get the new X.Value1.
Some fellows might argue that we usually build configuration objects which own a lot of properties accessed by other arbitrary objects, right? BTW, configuration is a concept per se, isn't it? And we usually categorize configuration values using composition:
public class ApplicationConfiguration
{
public DatabaseConfiguration Database { get; } = new DatabaseConfiguration();
public StorageConfiguration Storage { get; } = new StorageConfiguration();
}
public class DatabaseConfiguration
{
public string ConnectionString { get; set; }
}
public class StorageConfiguration
{
public string TemporalFileDirectoryPath { get; set; }
public string BinaryDirectoryPath { get; set; }
}
So later we inject the application configuration wherever we need it:
// Please note that it's a VERY hypothetical example, don't take
// it as an actual advise on how to implement a data mapper!!
public class DataMapper
{
public DataMapper(ApplicationConfiguration appConfig)
{
AppConfig = appConfig;
}
public ApplicationConfiguration AppConfig { get; }
private IDbConnection Connection { get; }
public void Connect()
{
// We access the configured connection string
// from the application configuration object
Connection = new SqlConnection(AppConfig.Database.ConnectionString);
Connection.Open();
}
}
In summary, and since I love comparing real-world and programming use cases, imagine that you never clean your room and you would use a single box to store every tool you might need some day. One day you need a screwdriver from the whole box, and you know that's inside it... But you need to throw everything in the box to the ground and work out the mess prior to find the priceless screwdriver to complete some home task.
Or imagine that you've bought a toolbox to store your tools in order, and once you need a screwdriver, you know that's in the toolbox and in the section where you store your screwdrivers.
You know that the second approach is the most mind-friendly. That is, when you develop software, you need to design mind-friendly architectures rather than a big mess of unrelated data and behaviors working together.
Background and Problem
I have a WPF application which basically consist of a collection of different Skill-objects where each skill is inherited from an abstract base class (SkillBase). Each Skill should have its own View and ViewModel to support editing the values of the specific Skill. When the user selects a Skill in a List and presses "Edit", the corresponding Edit-ViewModel should be instantiated with the Skill injected into it.
How do I choose which Edit-ViewModel that should be instantiated when I only have a Skill-data object? Skills can be added through a plugin system so I don't know the type of the skills beforehand.
My solution today (which works) is to decorate each Edit-ViewModel with an attribute (EditViewModelForAttribute) telling what datatype it should map to. Is this the way to go or is there any better ways of doing this? Am I even on the right track?
Other solutions that I can think of is to skip the attribute and use some kind of naming convention of Edit-ViewModels and another solution would be, at startup, register a Skill type with a Edit-ViewModel type through an injected service (EditService.RegisterEditViewModel(typeof(WalkSkill), typeof(EditWalkSkillViewModel));)
This is my code:
"Data Layer"
public abstract class SkillBase
{
// Common properties for all skills
}
public class WalkSkill : SkillBase
{
// Some properties
}
public class RunSkill : SkillBase
{
// Some properties
}
"ViewModel Layer"
public abstract class EditSkillViewModel<T> : ViewModelBase where T : Skill
{
public abstract T Skill { get; protected set; }
}
[EditViewModelFor(typeof(WalkSkill))] // Attribute telling that this viewmodel should be instantiated when we want to edit a WalkSkill object
public class EditWalkSkillViewModel : EditSkillViewModel<WalkSkill>
{
public override WalkSkill Skill { get; protected set; }
public EditWalkSkillViewModel(WalkSkill skill)
{
Skill = skill;
}
}
[EditViewModelFor(typeof(RunSkill))] // Attribute telling that this viewmodel should be instantiated when we want to edit a RunSkill object
public class EditRunSkillViewModel : EditSkillViewModel<RunSkill>
{
public override RunSkill Skill { get; protected set; }
public EditRunSkillViewModel(RunSkill skill)
{
Skill = skill;
}
}
To locate the correct ViewModel for a specific Skill i have an extension method to search for types in the app domain that has the EditViewModelForAttribute with the specific Skill type:
public static ViewModelBase GetEditSkillViewModel(this Skill skill)
{
var viewModelType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where
type.IsDefined(typeof(EditViewModelForAttribute)) &&
type.GetCustomAttribute<EditViewModelForAttribute>().SkillType == skill.GetType()
select type).SingleOrDefault();
return viewModelType == null ? null : (ViewModelBase)Activator.CreateInstance(viewModelType, skill);
}
Which is called like this:
var editViewModel = selectedSkill.GetEditSkillViewModel();
The current solution you are using seems fine by me, as long as you only have one ViewModel for each Skill.
One suggestion though, use FirstOrDefault instead of SingleOrDefault and cache the result somewhere. Searching through all the types can be time consuming.
As far as the software development goes, you seem to be on the right track. There's nothing wrong with your current solution. This is classical approach if you're using such frameworks like MEF.
That said, if you're going to continue; consider switching to MEF, or any other framework, instead of writing your own attributes / import functions / other jargon.
// Define other methods and classes here
public abstract class EditSkillViewModel<T> where T : SkillBase
{
public T Skill { get; protected set; }
protected EditSkillViewModel(T skill){
Skill = skill;
}
}
public static class EditSkillViewModelManager
{
private static IDictionary<Type, Type> _types;
public static EditSkillViewModel<T> CreateEditSkillViewModel<T>(T skill)
where T : SkillBase
{
return (EditSkillViewModel<T>)Activator.CreateInstance(_types[typeof(T)], skill);
}
static EditSkillViewModelManager()
{
var editSkillViewModelTypes = from x in Assembly.GetAssembly(typeof(SkillBase)).GetTypes()
let y = x.BaseType
where !x.IsAbstract &&
!x.IsInterface &&
y != null &&
y.IsGenericType &&
y.GetGenericTypeDefinition() == typeof(EditSkillViewModel<>)
select x;
_types = editSkillViewModelTypes
.Select(x =>
new {
To = x, // EditWalkSkillViewModel
From = x.BaseType.GetGenericArguments()[0] // WalkSkill
})
.ToList()
.ToDictionary(x => x.From, x => x.To);
_types.Dump();
}
}
public class EditWalkSkillViewModel : EditSkillViewModel<WalkSkill>
{
public EditWalkSkillViewModel(WalkSkill skill) : base(skill)
{
}
}
public class EditRunSkillViewModel : EditSkillViewModel<RunSkill>
{
public EditRunSkillViewModel(RunSkill skill): base(skill)
{
}
}
I was solving very similar problem and here is what I did - converted to your classes:
Skills as you have it right now + add attribute/generic that says "I need configuration of type X".
Configuration class for each skill (can be shared/inherited, if "walk" and "run" have speed, etc)
View for each Configuration with attribute/generic saying "I can edit configuration of type X"
One EditViewModel that has property of Configuration class. This VM can also have properties that are common to all Skills - name, id, enabled, ....
It is based on the same idea as your solution, but the abstraction is somewhere else. I made it this way, because I had to separate the UI architecture from my logic. The only thing I have to change if I want to migrate from WPF to something else is to create Views for all configurations. There is only one EditViewModel that joins WPF UI with my application logic/models.