全局可读配置,仅从特定 类 更新

Globally readable configuration, updated from only specific classes

我的 C#7 应用程序归结为一个具有三种可能状态的巨型状态机。我的很多对象都需要了解当前状态,因为它们在不同状态下的行为不同。例如,我可能会在不同状态下翻转文本框的可见性,或者我可能会根据状态更改操作的内容。我相信我的问题针对的是 C# 中的特定设计模式,而不是我选择的框架,但我使用的是 WPF。我会根据要求添加此标签。

我希望远离状态可枚举的全局可访问性(单例模式)。所以我将我的顶级容器 MainViewModel 变成了 Dependency Injection 容器,并且只将我的状态可枚举注入到那些需要知道状态的对象中。我找到了类似于以下内容的解决方案:

enum MachineState
{
    SensingInputs,
    Inactive,
    Outputting,
}
class Status
{
    public MachineState state = MachineState.SensingInputs;
    public RequestToUpdate(MachineState state)
        {
            bool validTimeToChange = FunctionNotDefinedHere(state);
            if (validTimeToChange) this.state = state;
        }
}
class MainViewModel
{
    Status status;
    public MainViewModel()
    {
        status = new Status();
        MyClass1 object1 = new MyClass1(status);
        MyClass2 object2 = new MyClass2();
        MyClass3 object3 = new MyClass3(status);
    }
}

问题是,任何引用它以读取当前状态的人也可以使用任何 public 方法来改变当前状态 (RequestToUpdate(Status))。我只希望我的 classes 的一个子集能够修改 Status。起初,在我看来,这似乎是 观察者模式:

的理想情况
class Status
{
    public MachineState state = MachineState.SensingInputs;
    public handleRequestToUpdate(Object sender, MachineStateEventArgs e)
        {
            bool validTimeToChange = FunctionNotDefinedHere(e.state);
            if (validTimeToChange) this.state = e.state;
        }
}
class MainViewModel
{
    Status status;
    public MainViewModel()
    {
        status = new Status();
        MyClass1 object1 = new MyClass1(status);
        MyClass2 object2 = new MyClass2();
        MyClass3 object3 = new MyClass3(status);
        object3.RequestToUpdate += status.handleRequestToUpdate;
    }
}

但是这个解决方案看起来很难看。我必须在每个 class 中声明 event RequestToUpdate 和一个像 OnRequestToUpdate() 一样引发它的方法我想修改 Status。此外,这些 class 中的每一个都需要 MachineStateEventArgs 的知识。这些重复的样板文件让我很困扰,但我无法完全理解。也许它打破了单一职责原则/封装。

所以观察者模式让我失望了。我的下一个想法是尝试 中介模式 将所有这些代码封装到一个 class 中,仅将 executeRequestToUpdate(Status) 暴露给它所在的那些 classes是否注入依赖项:

class Status // Status is same as Observer pattern above
{
    public MachineState state = MachineState.SensingInputs;
    public handleRequestToUpdate(Object sender, MachineStateEventArgs e)
        {
            bool validTimeToChange = FunctionNotDefinedHere(e.state);
            if (validTimeToChange) this.state = e.state;
        }
}
class StatusMediator
{
    public StatusMediator(Status status)
    {
        RequestToUpdate += status.handleRequestToUpdate;
    }
    public event EventHandler<RequestToUpdateEventArgs> RequestToUpdate;
    public executeRequestToUpdate(MachineState state)
    {
        RequestToUpdateEventArgs e = new RequestToUpdateEventArgs()
        RequestToUpdate?.Invoke(this, e); 
    }
}
class MainViewModel
{
    Status status;
    StatusMediator mediator;
    public MainViewModel()
    {
        status = new Status();
        mediator = new StatusMediator(status);
        MyClass1 object1 = new MyClass1(status);
        MyClass2 object2 = new MyClass2();
        MyClass3 object3 = new MyClass3(status, mediator);
    }
}

这是我自己想出的最不丑陋的解决方案。我不知道输入 Google 以查找其他示例的正确术语,但我怀疑我的问题是独一无二的。社区,有没有更好的方法来解决我的问题?我认为使用不可变变量的线程安全解决方案会更好,但我对如何实现它不太有信心。

我就靠封装,声明两个接口来暴露Status对象的不同职责。

interface IReadOnlyStatus
{
    MachineState State { get; }
}

interface IStatus : IReadOnlyStatus
{
    void RequestToUpdate(MachineState state);
}

public class Status : IStatus
{
    MachineState State { get; private set; }
    void RequestToUpdate(MachineState state)
    {
        bool validTimeToChange = FunctionNotDefinedHere(state);
        if (validTimeToChange) this.State = state;
    }
}

从那里,您可以根据需要注入一个或另一个:

// Can only read status
class MyClass1
{
    MyClass1(IReadOnlyStatus status)
    {

    }
}

// Can read and update status
class MyClass3
{
    MyClass3(IStatus status)
    {

    }
}

class MainViewModel
{
    Status status;
    public MainViewModel()
    {
        status = new Status();
        // The cast is not necessary but added for clarity
        MyClass1 object1 = new MyClass1((IReadOnlyStatus)status);
        MyClass3 object3 = new MyClass3((IStatus)status);
    }
}

在不太可能的情况下,您希望 "reading" 和 "writing" 是完全独立的职责,您可以删除两个接口之间的继承。