I have doubts regarding my implementation of observer pattern, but with complete separation of concerns.
Example below is not a real life code, but just an example of idea how I want to do it.
In my solution I have two project layers:
My view model is a subject subscribing the observers.
Code in VM:
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);
}
}
}
And my observer in service layer is very simple. After Update
call from the subject is displaying new 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
is same as above.
Right now I have doubts how my constructor supposed to look like, if I want to create a new observer with a name parameter, for example: new Observer1("name1")
in this case, keeping separation, should my subject's ctor look like:
public MainWindowViewModel()
{
_observerService = observerService;
IObserverService observer1 = new ObserverService("name1");
IObserverService observer2 = new ObserverService("name2");
SwitchCommnad = new DelegateCommand(OnSwitchCommand);
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
Is it correct approach? Is it going to be testable? Or I have to inject IObserverService somehow?
it makes sense, that MainWindowViewModel will receive some external observers via constructor:
public MainWindowViewModel(IObserver1 observer1, IObserver2 observer2)
{
_observer1 = observer1;
_observer2 = observer2;
ObserverCommand = new DelegateCommand(OnObserverCommand);
InitProgram();
}
when you create an instance of MainWindowViewModel (I assume, it will be used for MainWindowView DataContext), you will pass some real observers:
IObserverService observer1 = new ObserverService("name1");
IObserverService observer2 = new ObserverService("name2");
var vm = new MainWindowViewModel(observer1, observer2);
mainWindow.DataContext = vm;
no need for DI container here if dependencies can be resolved statically
similarly, for test you can have some TestObserverService
(or IObserverService mock):
IObserverService observer1 = new TestObserverService("name1");
IObserverService observer2 = new TestObserverService("name2");
var vm = new MainWindowViewModel(observer1, observer2);
MainWindowViewModel might create some IObserverServices, if it has properties worth observing from other objects in the application (e.g. related view models)