具有完整层分离和可测试性的观察者模式实现,我做对了吗?
Observer pattern implementation with complete layer separation and testability, am I doing it right?
我对观察者模式的实现有疑问,但关注点完全分离。
下面的示例不是现实生活中的代码,只是我想如何做的一个想法示例。
在我的解决方案中,我有两个项目层:
- 桌面层(视图、视图模型、模型)
- 服务库层(带观察者)
我的视图模型是订阅观察者的主题。
虚拟机中的代码:
interface ISubject
{
void Subscribe(IObserverService observer);
void Unsubscribe(IObserverService observer);
void Notify();
}
public class MainWindowViewModel : ViewModelBase, ISubject
{
private readonly IObserverService _observer1;
private readonly IObserverService _observer2;
private ArrayList _observers;
public MainWindowViewModel(
IObserver1 observer1,
IObserver2 observer2)
{
_observer1 = observer1;
_observer2 = observer2;
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
private void InitProgram()
{
_observers = new ArrayList();
_observers.Add(_observer1);
_observers.Add(_observer2);
}
public List<IObserverService> Observers { get; set; }
private void OnSwitchCommand(object obj)
{
if (Jeden == true)
{
UiModel = _controlsService.SwitchOff();
}
else
{
UiModel = _controlsService.SwitchOn();
}
}
private void OnObserverCommand(object obj)
{
SomeValue++;
}
public void Subscribe(IObserverService observer)
{
Observers.Add(observer);
}
public void Unsubscribe(IObserverService observer)
{
Observers.Remove(observer);
}
public void Notify()
{
Observers.ForEach(x => x.Update(SomeValue));
}
public ICommand ObserverCommand { get; private set; }
private int _someValue;
public int SomeValue
{
get => _someValue;
set
{
_someValue = value;
InformObservers();
}
}
private void InformObservers()
{
foreach (IObserverService x in _observers)
{
x.Update(SomeValue);
}
}
}
而我在服务层的观察者非常简单。 Update
来自主题的调用显示新的 MessageBox
:
public interface IObserverService
{
void Update(int someValue);
}
public class Observer1 : IObserver1, IObserverService
{
public string ObserverName { get; private set; }
public Observer1(string name)
{
this.ObserverName = name;
}
public void Update(int someValue)
{
MessageBox.Show("New value: " + someValue.ToString() + " for " + ObserverName);
}
}
Observer2
同上
现在我怀疑我的构造函数应该是什么样子,如果我想创建一个带有名称参数的新观察者,例如:new Observer1("name1")
在这种情况下,保持分离,我的主题的 ctor 应该看起来像:
public MainWindowViewModel()
{
_observerService = observerService;
IObserverService observer1 = new ObserverService("name1");
IObserverService observer2 = new ObserverService("name2");
SwitchCommnad = new DelegateCommand(OnSwitchCommand);
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
这是正确的做法吗?它会被测试吗?或者我必须以某种方式注入 IObserverService?
如果您想测试您的 VM,请遵循 IoC,不要在其中创建您的 ObserverServices,但正如您所说,注入 IObserverService;因此,您将能够模拟服务并测试您的 VM,而无需整个服务行为。
我可能会建议您使用 Autofac 甚至 Ninject。有很多 DI 框架,因此请寻找适合您的需求的框架。
有道理,MainWindowViewModel 将通过构造函数接收一些外部观察者:
public MainWindowViewModel(IObserver1 observer1, IObserver2 observer2)
{
_observer1 = observer1;
_observer2 = observer2;
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
当您创建 MainWindowViewModel 的实例时(我假设它将用于 MainWindowView DataContext),您将传递一些真实的观察者:
IObserverService observer1 = new ObserverService("name1");
IObserverService observer2 = new ObserverService("name2");
var vm = new MainWindowViewModel(observer1, observer2);
mainWindow.DataContext = vm;
如果可以静态解决依赖关系,这里不需要 DI 容器
类似地,对于测试你可以有一些TestObserverService
(或 IObserverService 模拟):
IObserverService observer1 = new TestObserverService("name1");
IObserverService observer2 = new TestObserverService("name2");
var vm = new MainWindowViewModel(observer1, observer2);
MainWindowViewModel 可能 创建一些 IObserverServices,if 它具有值得从应用程序中的其他对象观察的属性(例如相关视图模型)
我对观察者模式的实现有疑问,但关注点完全分离。
下面的示例不是现实生活中的代码,只是我想如何做的一个想法示例。
在我的解决方案中,我有两个项目层:
- 桌面层(视图、视图模型、模型)
- 服务库层(带观察者)
我的视图模型是订阅观察者的主题。
虚拟机中的代码:
interface ISubject
{
void Subscribe(IObserverService observer);
void Unsubscribe(IObserverService observer);
void Notify();
}
public class MainWindowViewModel : ViewModelBase, ISubject
{
private readonly IObserverService _observer1;
private readonly IObserverService _observer2;
private ArrayList _observers;
public MainWindowViewModel(
IObserver1 observer1,
IObserver2 observer2)
{
_observer1 = observer1;
_observer2 = observer2;
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
private void InitProgram()
{
_observers = new ArrayList();
_observers.Add(_observer1);
_observers.Add(_observer2);
}
public List<IObserverService> Observers { get; set; }
private void OnSwitchCommand(object obj)
{
if (Jeden == true)
{
UiModel = _controlsService.SwitchOff();
}
else
{
UiModel = _controlsService.SwitchOn();
}
}
private void OnObserverCommand(object obj)
{
SomeValue++;
}
public void Subscribe(IObserverService observer)
{
Observers.Add(observer);
}
public void Unsubscribe(IObserverService observer)
{
Observers.Remove(observer);
}
public void Notify()
{
Observers.ForEach(x => x.Update(SomeValue));
}
public ICommand ObserverCommand { get; private set; }
private int _someValue;
public int SomeValue
{
get => _someValue;
set
{
_someValue = value;
InformObservers();
}
}
private void InformObservers()
{
foreach (IObserverService x in _observers)
{
x.Update(SomeValue);
}
}
}
而我在服务层的观察者非常简单。 Update
来自主题的调用显示新的 MessageBox
:
public interface IObserverService
{
void Update(int someValue);
}
public class Observer1 : IObserver1, IObserverService
{
public string ObserverName { get; private set; }
public Observer1(string name)
{
this.ObserverName = name;
}
public void Update(int someValue)
{
MessageBox.Show("New value: " + someValue.ToString() + " for " + ObserverName);
}
}
Observer2
同上
现在我怀疑我的构造函数应该是什么样子,如果我想创建一个带有名称参数的新观察者,例如:new Observer1("name1")
在这种情况下,保持分离,我的主题的 ctor 应该看起来像:
public MainWindowViewModel()
{
_observerService = observerService;
IObserverService observer1 = new ObserverService("name1");
IObserverService observer2 = new ObserverService("name2");
SwitchCommnad = new DelegateCommand(OnSwitchCommand);
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
这是正确的做法吗?它会被测试吗?或者我必须以某种方式注入 IObserverService?
如果您想测试您的 VM,请遵循 IoC,不要在其中创建您的 ObserverServices,但正如您所说,注入 IObserverService;因此,您将能够模拟服务并测试您的 VM,而无需整个服务行为。
我可能会建议您使用 Autofac 甚至 Ninject。有很多 DI 框架,因此请寻找适合您的需求的框架。
有道理,MainWindowViewModel 将通过构造函数接收一些外部观察者:
public MainWindowViewModel(IObserver1 observer1, IObserver2 observer2)
{
_observer1 = observer1;
_observer2 = observer2;
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
当您创建 MainWindowViewModel 的实例时(我假设它将用于 MainWindowView DataContext),您将传递一些真实的观察者:
IObserverService observer1 = new ObserverService("name1");
IObserverService observer2 = new ObserverService("name2");
var vm = new MainWindowViewModel(observer1, observer2);
mainWindow.DataContext = vm;
如果可以静态解决依赖关系,这里不需要 DI 容器
类似地,对于测试你可以有一些TestObserverService
(或 IObserverService 模拟):
IObserverService observer1 = new TestObserverService("name1");
IObserverService observer2 = new TestObserverService("name2");
var vm = new MainWindowViewModel(observer1, observer2);
MainWindowViewModel 可能 创建一些 IObserverServices,if 它具有值得从应用程序中的其他对象观察的属性(例如相关视图模型)