C# - Как запретить подклассу напрямую вызывать защищенные методы? - PullRequest
0 голосов
/ 19 апреля 2020

Мой код:

abstract class StateMachine {
    protected string State { get; private set; }
    protected abstract void OnWorking();
    protected abstract void OnPrepare();
    protected abstract void OnCancel();

    public bool Prepare() {
        if(State != null) {
            return false;
        }
        State = "Preparing";
        OnPrepare();
        State = "Prepared";
        return true;
    }
    public bool Start() {
        if(State != "Prepared") {
            return false;
        }
        State = "Working";
        OnWorking();
        State = "Done";
        return true;
    }
    public bool Cancel() {
        if(State != "Working" || State == "Done") {
            return false;
        }
        OnCancel();
        State = "Canceled";
        return true;
    }
}

class Downloader : StateMachine {
    protected override void OnPrepare() {
        Console.WriteLine("I am preparing.");
        Thread.Sleep(1000);
    }
    protected override void OnWorking() {
        Console.WriteLine("I am working.");
        Thread.Sleep(1000);
    }
    protected override void OnCancel() {
        Console.WriteLine("Let's cancel the operation!");
    }
}

class Program {
    static void Main(string[] args) {
        Downloader downloader = new Downloader();
        Parallel.Invoke(() => {
            downloader.Prepare();
            downloader.Start();
        }, () => {
            // Cancel while working
            Thread.Sleep(1500);
            downloader.Cancel();
        });
    }
}

Вывод будет:

I am preparing.
I am working.
Let's cancel the operation!

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

Проблема в том, что ничто не может остановить подкласс (Downloader) от вызова этих защищенных методов в базовом классе (StateMachine) самостоятельно. Например, подкласс может иметь что-то вроде:

protected override void OnWorking(){
    Console.WriteLine("I am working.");
    Thread.Sleep(1000);
    OnCancel();
    OnPrepare();
}

Тогда результат будет:

I am preparing.
I am working.
Let's cancel the operation!
I am preparing.
Let's cancel the operation!

Что не ожидается с точки зрения StateMachine.

Поэтому я пытаюсь запретить подклассу вызывать защищенные методы. Но я чувствую, что делаю странные вещи. Я не думаю, что C# OOP концепции позволят такое поведение.

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

Что бы вы сделали в этой ситуации? Я имею в виду, что может быть элегантным способом решить эту проблему?

1 Ответ

1 голос
/ 19 апреля 2020

Вы можете определить их как делегаты, сделать их частными и установить их с помощью абстрактных вспомогательных методов следующим образом:

static void Main()
{
  Downloader downloader = new Downloader();
  Parallel.Invoke(() =>
  {
    downloader.Prepare();
    downloader.Start();
  }, () =>
  {
    // Cancel while working
    Thread.Sleep(1500);
    downloader.Cancel();
  });
}

abstract class StateMachine
{
  protected string State { get; private set; }

  private Action OnWorking;
  private Action OnPrepare;
  private Action OnCancel;

  // Helper methods to be implemented in subclass
  protected abstract Action DefineWorkingAction();
  protected abstract Action DefinePrepareAction();
  protected abstract Action DefineCancelAction();

  protected StateMachine()
  {
    this.OnWorking = DefineWorkingAction();
    this.OnPrepare = DefinePrepareAction();
    this.OnCancel = DefineCancelAction();
  }


  public bool Prepare()
  {
    if (State != null)
    {
      return false;
    }
    State = "Preparing";
    OnPrepare();
    State = "Prepared";
    return true;
  }
  public bool Start()
  {
    if (State != "Prepared")
    {
      return false;
    }
    State = "Working";
    OnWorking();
    State = "Done";
    return true;
  }
  public bool Cancel()
  {
    if (State != "Working" || State == "Done")
    {
      return false;
    }
    OnCancel();
    State = "Canceled";
    return true;
  }
}

class Downloader : StateMachine
{
  protected override  Action DefineWorkingAction()
  {
    return () =>
    {
      Console.WriteLine("I am working.");
      Thread.Sleep(1000);
    };
  }

  protected  override Action DefinePrepareAction()
  {
    return () =>
    {
      Console.WriteLine("I am preparing.");
      Thread.Sleep(1000);
    };
  }

  protected  override Action DefineCancelAction()
  {
    return () =>
    {
      Console.WriteLine("Let's cancel the operation!");
    };
  }
}

Теперь подклассы больше не могут их вызывать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...