ManxJason
ManxJason

Reputation: 940

DRY principle when working with multiple method signatures

With the DRY principle in mind, how would you tackle almost identical methods (with different signatures) that work with an IEnumerable. I.e. one signature works with a specific type parameter. My question extends to the calling of private methods, and their multiple signatures.

I don't want to have two methods with identical logic - If something changes then I have to change both sets of logic. The calling of the private methods for example, how can I make the private method accept either type of IEnumerable

public class Person
{
    public string Name {get; set;}
    public string Age {get; set;}
}

public class SupremeBeing : Person
{
    public string Power {get; set;}
}

public class Payroll
{
    public void DoSomething(IEnumerable<Person> peopleIn)
    {
        // Do this with peopleIn
        // Do that with peopleIn
        // Call private method passing in peopleIn (which also has 2 signatures)
    }

    public void DoSomething(IEnumerable<SupremeBeing> peopleIn)
    {
        // Do this with peopleIn
        // Do that with peopleIn
        // Call private method passing in peopleIn (which also has 2 signatures)
    }       
}

Upvotes: 0

Views: 297

Answers (5)

reachingnexus
reachingnexus

Reputation: 101

You could just have the Payroll class sort out the different types of Persons after it has run all the common operations and forward people to the appropriate extended methods.

interface IPerson {
    string Name { get; set; }
    string Age { get; set; }

}
public class Person : IPerson {
    public string Name { get; set; }
    public string Age { get; set; }
}
public class SupremeBeing : Person
{
    public string Power { get; set; }
}

public class Payroll 
{
    public void DoSomething(IEnumerable<IPerson> peopleIn)
    {
        //..everyone does this and that 
        IEnumerable<Person> NormalPeople = peopleIn.OfType<Person>();
        if (NormalPeople.Count() > 0) DoSomethingNormalSpecific(NormalPeople);

        IEnumerable<SupremeBeing> SupremeBeings = peopleIn.OfType<SupremeBeing>();
        if (SupremeBeings.Count() > 0) DoSomethingSupremeSpecific(SupremeBeings);

    }

    public void DoSomethingNormalSpecific(IEnumerable<Person> normalPeopleIn) 
    {
        // just normal people
    }

    public void DoSomethingSupremeSpecific(IEnumerable<SupremeBeing> supremeBeingsIn)
    {
        // just Supreme Beings
    }
}

Upvotes: 0

Guffa
Guffa

Reputation: 700442

The object oriented approach would be to make the classes handle their differences themselves. You can for example use virtual methods to have one implementation for each class.

When you can treat every object as a Person object regardless of the actual type, then you only need one set of methods to handle the payroll, and you wouldn't need to call it with separate lists for each class, you can put them all in the same list and call the method.

Example:

public class Person {

  public string Name {get; set;}
  public string Age {get; set;}

  virtual public int Salary { get { return 1000 + Age * 10; } }

  override public string ToString() {
    return Name + "(" + Age + ")";
  }

}

public class SupremeBeing : Person {

  public string Power {get; set;}

  override public int Salary { get { return 5000 + Age * 7; } }

  override public string ToString() {
    return Power + " " + Name;
  }

}

public class Payroll {

  public void DoSomething(IEnumerable<Person> peopleIn) {
    foreach (Person p in peopleIn) {
      Console.log("{0} earns {1}", p, p.Salary);
    }
  }

}

Upvotes: 2

user743414
user743414

Reputation: 936

I don't understand your problem. You could create a generic method which does your generic stuff and then create a method which could be overridden for your special stuff.

Like:

class Something
{
    protected virtual void DoSomethingSpecial<TYPE>(TYPE item)
    {
    }

    public void DoSomethingy<TYPE>(IEnumerable<TYPE> items)
    {
        foreach(TYPE item in items)
        {
          // do whatever you have to do for every type

          // do whatever you have to do in special
          this.DoSomethingSpecial(item)
        }
    }
}

Code not tested just typed.

And then create one class for every special case. In these classes you just override DoSomethingSpecial for every type and you're done.

Upvotes: 0

dav_i
dav_i

Reputation: 28107

One option is to call the first method from the second:

public void DoSomething(IEnumerable<SupremeBeing> peopleIn)
{
    this.DoSomething(peopleIn.Cast<Person>());

    // do SupremeBeing specific stuff
}

Another option is to have a private method which does all the Person stuff.

public void DoSomething(IEnumerable<SupremeBeing> peopleIn)
{
    this.DoSomethingWithPersons(peopleIn);
}

public void DoSomething(IEnumerable<Person> peopleIn)
{
    this.DoSomethingWithPersons(peopleIn);
}

private void DoSomethingWithPersons(IEnumerable<Person> peopleIn)
{
    // do stuff
}

Subtle differences between the two options without more information it's hard to know which would be better.

Upvotes: 1

Jamiec
Jamiec

Reputation: 136124

It looks to me like what you want is more abstraction on the Payroll Class

public abstract class PayrollBase<T> where T : Person
{
    public void DoSomething(IEnumerable<T> peopleIn)
    {
        // Do this with peopleIn
        // Do that with peopleIn
        this.InternalLogic(peopleIn);
    }

    protected virtual InternalLogic(IEnumerable<T> peopleIn)
    {
       // do something super special
    }
}

You would then implement this for your specific types

public PersonPayroll : PayrollBase<Person>
{
    protected override InternalLogic(IEnumerable<Person> peopleIn)
    { ... } 
}


public SupremeBeingPayroll : PayrollBase<SupremeBeing>
{
    protected override InternalLogic(IEnumerable<SupremeBeing> peopleIn)
    { ... } 
}

You would then use some form of factory class to instantiate the right "Payroll" for the list of people you're dealing with.

Upvotes: 3

Related Questions