2015-02-11 2 views
0

С учетом принципа DRY, как бы вы решали практически одинаковые методы (с разными сигнатурами), которые работают с IEnumerable. То есть одна подпись работает с определенным параметром типа. Мой вопрос распространяется на вызов частных методов и их множественные подписи.Принцип DRY при работе с несколькими сигнатурами методов

Я не хочу иметь два метода с одинаковой логикой. Если что-то меняется, я должен изменить оба набора логики. Вызывающий частных методов, например, как я могу сделать частный способ принять любой тип 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) 
    }  
} 
+2

И вопрос в том, что? – Jamiec

+0

Вы можете задать вопрос, добавив здесь вопрос. –

+0

Возможно, вы можете использовать какую-то абстракцию, как вы изначально имеете с вашей схемой наследования. Таким образом, вы можете свести к одному методу, используя самый абстрактный IEnumerable. –

ответ

1

Один из вариантов для вызова первого метода от второго:

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

    // do SupremeBeing specific stuff 
} 

Другой вариант иметь частный метод, который делает все вещи Person.

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 
} 

Тонкие различия между двумя вариантами без дополнительной информации трудно понять, что было бы лучше.

+0

Вам не нужно явно взывать, поскольку SupremeBeing наследует людей –

+0

У вас есть to: выбирается наиболее конкретная перегрузка, которая в этом случае приведет к циклу обратной связи и 'StackOverflowException'. Кастинг гарантирует, что выбрана перегрузка 'IEnumerable '. –

3

Он смотрит на меня, как то, что вы хотите больше абстракция на Payroll класса

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 
    } 
} 

Вы бы тогда реализовать это для конкретных типов

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


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

Вы бы затем использовать некоторые формы завода класса, чтобы создать правильную «Заработную плату» для списка людей, с которыми вы имеете дело.

+0

Я, наверное, должен был сказать, что в моей реальной жизненной проблеме класс Payroll был бы статическим html-помощником, вызываемым из представления. Но я вижу, что вы говорите. – ManxJason

+0

У нас также есть имя для этого шаблона «Шаблон метода шаблона» в сочетании с «Любопытно повторяющимся шаблоном шаблона» –

0

Я не понимаю вашу проблему. Вы можете создать общий метод, который будет делать ваши общие вещи, а затем создать метод, который может быть переопределен для ваших специальных материалов.

Как:

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) 
     } 
    } 
} 

код не тестировалось только что ввели.

И затем создайте один класс для каждого частного случая. В этих классах вы просто переопределяете DoSomethingSpecial для каждого типа, и все готово.

2

Объектно-ориентированный подход заключается в том, чтобы классы сами обрабатывали свои различия. Например, вы можете использовать виртуальные методы для реализации одной реализации для каждого класса.

Если вы можете рассматривать каждый объект как объект Person независимо от фактического типа, вам нужен только один набор методов для обработки заработной платы, и вам не нужно будет вызывать его с отдельными списками для каждого класса, вы могут поместить их все в один список и вызвать метод.

Пример:

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); 
    } 
    } 

} 
+0

На первый взгляд я согласен с этим ответом, за исключением того, что мой опыт показывает мне, что это приводит к нарушению СРП. Обычно «Лицо» - это просто DTO, и поэтому не должно знать, как делать такие вещи, как вычисление зарплаты, - это что-то другое. В упрощенном примере это выглядит * прекрасно, но в реальном мире, что расчет зарплаты должен пойти на xyz другие услуги. Это быстро становится беспорядочным. – Jamiec

+0

@Jamiec: Если подсчет зарплаты должен быть сделан где-то в другом месте, то объект Person должен иметь только свойства и методы для предоставления информации, необходимой для расчета. – Guffa

0

Вы можете просто класс Payroll рассортировать различные типы людей после того, как он выполнять все обычные операции и вперед людей к соответствующим расширенным методам.

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 
    } 
} 
Смежные вопросы