我的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来查找其他示例的正确术语,但我怀疑我的问题是否独特。社区,有没有更好的方法来解决我的问题?我认为使用不可变变量的线程安全解决方案会更好,但是我对如何实现这一点不太自信。
我只是依靠封装并声明两个接口来暴露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);
}
}
在极少数情况下,您希望“阅读”和“写作”是完全独立的职责,您可以删除两个接口之间的继承。