ReactiveUI, View/ViewModel injection and DI in general
Asked Answered
W

2

13

Lately I've trying to get myself into the new age of UI development and discovered ReactiveUI. I love its declarative nature.

I wanted to make a complete switch, so I tried to understand how are things made in this new world of ReactiveUI. I choose ReactiveUI because I've seen that is maintained by a very clever guy (Paul C. Betts).

I'm very new to it and I will likely be flooding StackOverflow with questions about it because I has a huge power and I think it deserves to be learnt and mastered.

Let's get into the details:

I've always used View-First. I'm a veteran user of the Cinch Framework (http://cinch.codeplex.com/)

It uses MEF to inject the ViewModels to each View. You just have to decorate your ViewModel with [ViewModel("SampleView")] and add an Attached Property to your View (ViewModelLocator.ViewModel="SampleView"), and whenever the View is Loaded, the corresponding ViewModel is instantiated and injected as its DataContext with the life cycle you choose.

This mechanism, while it's valid, has some inconveniences. The worst of them: It uses a Locator.

As Mark Seemann suggest in his book, ServiceLocator is an anti-pattern that should be avoided.

  1. So my first question is: is ReactiveUI built on top of a Locator-based infrastructure?
  2. View-First or ViewModel-First? What's better in terms of good practices, decoupling, SOLID, and stuff like these that are concerns of a crazy, pro-Microsoft Clean Code lover like me? Which will make me sleep better and provide my application with all those *ibility goodness?
Weig answered 12/11, 2014 at 23:6 Comment(0)
P
6

Paul will probably chime in with the official answer, but I will put in my $0.02 as a person who has used the framework for a few projects but is by no means an expert.

1) I'm a big Mark Seemann fan and I do agree with his conclusion about the ServiceLocator anti-pattern. While ReactiveUI does use the Locator "Splat", I wouldn't consider it built on top of a Locator based infrastructure. There are a few Global items that are used like thread schedulers and a few major settings, but these mainly get set in application startup (like any DI container) and you don't deal with them directly in your classes for the most part. The only real location is the ViewModelHost control which uses a specific interface (IViewFor) on views to register against ViewModels. This is better than the attribute method since it keeps the ViewModels blissfully unaware of the Views. But this happens in the control itself and is part of the framework, so I don't feel it is an abuse of the ServiceLocator anti-pattern. I don't feel it is any different than registering anything else in setting up a DI container.

2) In just my experience since using ReactiveUI, my Views have gotten super-simple. Basically slap up some basic XAML to get the look and layout right, implement the IViewFor in the code behind, and do all my binding in the constructor, which I find easier now with ReactiveUI than doing in XAML (although you still can if you want to). Then everything logic-wise is done in the ViewModels. I think I usually do a ViewModel First approach solely for the fact that I need to have it (or at least its interface) defined to implement IViewFor<> for it on the View. I like the type-checking and stuff (another reason I like to bind in the constructor not in XAML). But I don't think there is a strong reason to do it one way or the other, from my experience.

Puppy answered 13/11, 2014 at 14:44 Comment(0)
S
30

ServiceLocator is an anti-pattern that should be avoided.

I generally think a lot of the advice around IoC/DI is pretty bad in the domain of 'cross-platform mobile applications', because you have to remember that a lot of their ideas were written for web apps, not mobile or desktop apps.

For example, the vast majority of popular IoC containers concern themselves solely with resolution speed on a warm cache, while basically completely disregarding memory usage or startup time - this is 100% fine for server applications, because these things don't matter; but for a mobile app? Startup time is huge.

Splat's Service Location solves a number of issues for RxUI:

  1. Service Location is fast, and has almost no overhead to set up.
  2. It encapsulates several different common object lifetime models (i.e. 'create new every time', 'singleton', 'lazy'), just by writing the Func differently
  3. It's Mono Linker friendly (generally)
  4. Service Location allows us to register types in platform-specific code, but use them in PCL code.

The best way to use Service Locator

In fact, I do generally agree with Mark Seemann, in that constructor injection is the preferred way to go - this is the pattern I really like:

    public SuspensionHost(ISuspensionDriver driver = null)
    {
        driver = driver ?? Locator.Current.GetService<ISuspensionDriver>();
    }

This uses a Service Located interface for the default interface, but only if the caller didn't give an explicit one in the constructor. Far more straightforward to test in a unit test runner than trying to construct a sham IoC container, but still falls back to a default implementation at runtime.

View-First or ViewModel-First?

Whether you can use VM-based routing (i.e. RoutedViewHost, IScreen, RoutingState, and friends) in ReactiveUI depends on the platform you're on:

  • WPF, Xamarin Forms: Absolutely
  • WP8, WinRT: You can make it work, you lose some transitions and niceties
  • Android, iOS Native: Very difficult to make work
Sprung answered 14/11, 2014 at 6:17 Comment(3)
Have a look at DryIoc.Zero, it solves issues of slow registration, but provides amazing functionality.Nanon
Still don't like the fact that I have to rely on static locator instead of an injected interface-type that can be easily mocked in external projects. There should at least be in option to avoid using the Locator in its current form.Rhyme
Regarding the routing, is there a platform agnostic technique for the bootstrapping and routing? I'm starting off a new client project that will be written with Uno Platform, chosen because I can write a simple UWP app that compiles and renders on desktops, mobile and web. Thank you!Rhyme
P
6

Paul will probably chime in with the official answer, but I will put in my $0.02 as a person who has used the framework for a few projects but is by no means an expert.

1) I'm a big Mark Seemann fan and I do agree with his conclusion about the ServiceLocator anti-pattern. While ReactiveUI does use the Locator "Splat", I wouldn't consider it built on top of a Locator based infrastructure. There are a few Global items that are used like thread schedulers and a few major settings, but these mainly get set in application startup (like any DI container) and you don't deal with them directly in your classes for the most part. The only real location is the ViewModelHost control which uses a specific interface (IViewFor) on views to register against ViewModels. This is better than the attribute method since it keeps the ViewModels blissfully unaware of the Views. But this happens in the control itself and is part of the framework, so I don't feel it is an abuse of the ServiceLocator anti-pattern. I don't feel it is any different than registering anything else in setting up a DI container.

2) In just my experience since using ReactiveUI, my Views have gotten super-simple. Basically slap up some basic XAML to get the look and layout right, implement the IViewFor in the code behind, and do all my binding in the constructor, which I find easier now with ReactiveUI than doing in XAML (although you still can if you want to). Then everything logic-wise is done in the ViewModels. I think I usually do a ViewModel First approach solely for the fact that I need to have it (or at least its interface) defined to implement IViewFor<> for it on the View. I like the type-checking and stuff (another reason I like to bind in the constructor not in XAML). But I don't think there is a strong reason to do it one way or the other, from my experience.

Puppy answered 13/11, 2014 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.