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

问题描述 投票:0回答:1

我的C#7应用程序归结为具有三个可能状态的巨型状态机。我的一些对象需要了解当前状态,因为它们在不同状态下的行为有所不同。如图所示,我可以在不同状态下翻转文本框的可见性,也可以根据状态更改动作的作用。我相信我的问题比我选择的框架更多地针对C#中的特定设计模式,但是我正在使用WPF。我将根据要求添加此标签。

我希望远离对于可枚举状态(Singleton模式)具有全局可访问性。因此,我将我的顶级容器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);
    }
}

问题是我仍然可以全局修改Status类。为了读取当前状态而对其进行引用的任何人,也可以使用任何公共方式来更改当前状态(RequestToUpdate(Status))。我只希望班级的一个子集能够修改Status。首先,在我看来,这似乎是Observer pattern

的理想情况
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;
    }
}

但是这种解决方案看起来很丑。在每个要修改event RequestToUpdate的类中,我都必须声明OnRequestToUpdate()和将其升为Status的方法。另外,每个这些类都需要MachineStateEventArgs的知识。重复的样板数量使我感到困扰,但是我不能完全放置它。也许会破坏单一责任原则/封装。

所以观察者模式使我失败了。我的下一个想法是尝试使用Mediator pattern将所有这些代码封装到一个类中,仅将executeRequestToUpdate(Status)暴露给依赖项注入的那些类:

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来查找其他示例的正确术语,但我怀疑我的问题是否独特。社区,有没有更好的方法来解决我的问题?我认为使用不可变变量的线程安全解决方案会更好,但是我对如何实现这一点不太自信。

c# dependency-injection singleton observer-pattern mediator
1个回答
1
投票

我只是依靠封装并声明两个接口来暴露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);
    }
}

在极少数情况下,您希望“阅读”和“写作”是完全独立的职责,您可以删除两个接口之间的继承。

© www.soinside.com 2019 - 2024. All rights reserved.