Как перегрузить метод во время выполнения или другие идеи в C # - PullRequest
3 голосов
/ 08 января 2012

Возможно, перегрузка метода не совсем то, что нужно, но это лучшее, что я мог придумать.

У меня есть класс:

public class Worker {
    private string jobType;

    public Worker(string jt)
    {
        this.jobType = jt;
    }

    public void ProcessJob()
    {
         if(jobType.Equals("Pizza") MakePizza();
         else if (jobType.Equals("Burger") MakeBurger();
    }

    private void MakePizza()
    {
        // make pizza
    }

    private void MakeBurger()
    {
        // make burger
    }
}

Выше приведен только пример иллюстрации. Когда класс создается, он создается с определенным типом задания, и это не изменится. Однако может потребоваться выполнить миллионы заданий, всегда одного и того же типа. ProcessJob() будет вызываться все время, но звонящий не будет знать, какой это тип работника. Я хотел бы не запускать проверку if каждый раз, должен быть способ выполнить эту проверку только один раз и подготовить ее.

В моем случае создание детских классов (пиццерия, бургер и т. Д.) Не вариант, так как в моем реальном случае класс велик, и есть только одна крошечная разница. Его изменение повлияет на всю архитектуру, поэтому его следует избегать.

Ответы [ 7 ]

4 голосов
/ 08 января 2012

Создайте абстрактный базовый класс, который содержит общие вещи, которые может делать работник. Затем объявите производные классы для специализированных работников.

public abstract class Worker
{    
    public abstract void ProcessJob();
}

public class PizzaWorker : Worker
{
    public override void ProcessJob()
    {
        // Make pizza
    }
}

public class BurgerWorker : Worker
{
    public override void ProcessJob()
    {
        // Make burger
    }
}

Теперь вы можете создавать работников разных типов и позволять им делать свою работу:

var workers = new List<Worker>();
workers.Add(new PizzaWorker());
workers.Add(new BurgerWorker());
foreach (Worker worker in workers) {
    woker.ProcessJob();
}

Это автоматически вызовет правильную реализацию ProcessJob для каждого типа работника.


Примечание. Каскады if-else-if и операторы switch часто указывают на то, что код работает процедурно, а не объектно-ориентированным образом. Рефакторинг, чтобы он был объектно-ориентированным!

3 голосов
/ 08 января 2012

Вы можете использовать делегат, созданный при создании объекта, таким образом, отправка выполняется автоматически:

public class Worker
{
    private delegate void MakeSomething();

    private MakeSomething makeWhat;
    private string jobType;

    public Worker(string jt)
    {
        this.jobType = jt;

        switch (jt)
        {
            case "Pizza":
                makeWhat = new MakeSomething(MakePizza);
                break;
            case "Burger":
                makeWhat = new MakeSomething(MakeBurger);
                break;
            default:
                throw new ArgumentException();
        }
    }

    public void ProcessJob()
    {
        makeWhat();
    }

    private void MakePizza()
    {
        //make pizza
    }

    private void MakeBurger()
    {
        //make burger
    }
}
1 голос
/ 08 января 2012

Нет, я не думаю, что этого следует избегать. Любой общий функционал должен идти в базовом классе. Я думаю, вам нужен статический фабричный метод , который возвращает дочерний класс на основе строкового параметра.

public abstract class Worker {
    public virtual void ProcessJob();

    public static Worker GetWorker(string jobType) {
        if(jobType.Equals("Pizza")
            return new PizzaWorker();
        else if (jobType.Equals("Burger")
            return new BurgerWorker();
        else
            throw new ArgumentException();
    }

    // Other common functionality
    protected int getFoo() {
        return 42;
    }
}

public class PizzaWorker : Worker {
    public override void ProcessJob() {
        // Make pizza
        int y = getFoo() / 2;
    }
}

public class BurgerWorker : Worker {
    public override void ProcessJob() {
        // Make burger
        int x = getFoo();
    }
}

Итак, чтобы использовать это:

Worker w = Worker.GetWorker("Pizza");
w.ProcessJob();   // A pizza is made.
1 голос
/ 08 января 2012

Я бы все еще рекомендовал использовать подклассы.Если вы не можете наследовать от Worker, создайте новую иерархию классов, которая будет использоваться внутри Worker.Таким образом, любой, кто использует класс Worker, не должен знать, что существуют подклассы.Если вы действительно ненавидите подклассы или у вас есть другая причина, по которой вы не хотите их использовать, вы можете использовать словарь.Он содержит тип задания в качестве ключа и Action в качестве метода, который он вызывает.Если вам нужно больше заданий, просто создайте приватный метод и зарегистрируйте его в методе RegisterWorkers.

private Dictionary<string, Action> actions = new Dictionary<string, Action>();

public Worker(string jt)
{
    this.jobType = jt;
    this.RegisterWorkers();
}

private void RegisterWorkers
{
    this.actions["Pizza"] = this.MakePizza;
    this.actions["Burger"] = this.MakeBurger;
}

public void ProcessJob()
{
    var action = this.actions[this.jobType];
    action();
}
1 голос
/ 08 января 2012

Именно поэтому существуют шаблоны: Команда , Стратегия , Декоратор .

Я считаю, что шаблон команды - это то, что вы ищете.Сначала у вас есть базовый шаблон «команды»:

public interface IJob {
    void ProcessJob();
}

Затем будут выполняться различные задания следующим образом:

public class MakePizza : IJob {
    // implement the interface
    public void ProcessJob() {
        // make a pizza
    }
}

Теперь у вас может быть JobFactory следующим образом:

public static class JobFactory {
    public static IJob GetJob(string jobType) {    
        if(jobType.Equals("Pizza"){
            return new MakePizza();
        } else (jobType.Equals("Burger") {
            return new MakeBurger();
        }
        // to add jobs, extend this if-else-if or convert to switch-case
    }
}

Рабочий теперь может выглядеть следующим образом:

public class Worker {
    private IJob job;

    public Worker(string jt) {
        job = JobFactory.GetJob(jt);
    }

    public void ProcessJob() {
         job.ProcessJob();
    }
}

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

0 голосов
/ 08 января 2012

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

0 голосов
/ 08 января 2012

Вы говорите об основном наследовании здесь. Есть несколько способов сделать это.

Создайте базовый класс,

public class Job
{
    virtual void ProcessJob();
}

Тогда MakePizza класс

public class MakePizza : Job
{
    public void ProcessJob()
    {
       //make Pizza
    }
}

Тогда в вашем рабочем классе вместо JobType в качестве string, что приведет ко всем видам потенциальных ошибок.

public class Worker{


  private Job jobType;


  public Worker(Job jt){

    this.jobType = jt;
  }


  public void ProcessJob()
  {
    Job.ProcessJob();  
  }

}

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

...