vendredi 21 septembre 2018

Design with interfaces in C#

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.

Aucun commentaire:

Enregistrer un commentaire