Archive for July, 2016

ObservableCollection is a Leaky Abstraction

And so is BindingList. Or, rather, exposing them through your controller’s interface is a Leaky Abstraction.

Anyone who’s done any Windows desktop development has eventually come across ObservableCollection and BindingList. How often have you written a controller (ViewModel or Presenter, doesn’t matter what you call it, they’re all some variant of the Controller in MVC) that looks like this.

    public class LeakyWpfController : INotifyPropertyChanged
    {
        public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();

        public event PropertyChangedEventHandler PropertyChanged;
    }

Or this

    public class LeakyWinformsController
    {
        public BindingList<Person> People { get; } = new BindingList<Person>();
    }

What’s wrong with this code?! We need to expose a collection type that the framework understands for the databinding to work!

No. We don’t. Both Winforms and WPF don’t care what you expose. They only care that the backing field is of the proper type. All you need to expose in your public interface is an ICollection, in both cases. Databinding to these two controllers works perfectly well.

    public class WpfController : INotifyPropertyChanged
    {
        public ICollection<Person> People { get; } = new ObservableCollection<Person>();

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class WinformsController
    {
        public ICollection<Person> People { get; } = new BindingList<Person>();
    }

Ok, so big deal. It’s not like I’m ever going to use my ViewModel or Presenter for anything but the specific View they were built for…

Are you so absolutely sure of this? If you’re certain that you’ll never want to use the same presentation logic in more than one window or application, then sure. You’re probably right. Leaking this abstraction doesn’t hurt, but what if we wanted to reuse logic from our old Winforms application in our new WPF app? Or what if we wanted to set ourselves up to migrate away from that old Winforms app into a new WPF application? What do we do when WPF and ObservableCollection aren’t the newest, greatest thing anymore and we’d like to migrate to that newest framework? Wouldn’t it be nice to be able to do so with minimal effort?

Our two controllers are at this point so similar, we can combine them into one. (Thanks to the fact that WPF supports BindingList as a backing field.)


    public class Controller : INotifyPropertyChanged, IController
    {
        public ICollection<Person> People { get; } = new BindingList<Person>();
        public ICommand AddItemCommand = new AddItemCommand(this);

        public event PropertyChangedEventHandler PropertyChanged;
    }

I only recommend using BindingList as a backing field if your Controller (view model/presenter) needs to work in a Winforms environment though. There are some performance issues with BindingList. If you only need to target WPF or UWP, then ObservableCollection is the right way to go. Either way, there’s no reason to expose anything more than an ICollection to the outside world.

Advertisements

, , , , , , ,

Leave a comment