7
\$\begingroup\$

I recently asked on Programmers:

How should one handle a Parent-Child relationship using MVP?

Based on the comments received, I tried this:

IParentView

public interface IParentView { }

IChildView

public interface IChildView<V> where V : IParentView {
    void Show(V parentView);
}

IChildPresenter

public interface IChildPresenter<V> where V : IParentView {
    void ShowView(V parentView);
}

CustomerManagementPresenter

public class CustomerManagementPresenter : Presenter<ICustomerManagementView>
    , ICustomerManagementUiHandler
    , IChildViewPresenter<IMainView> {
    public CustomerManagementPresenter(ICustomerManagementView view
        , ICustomerManagementService service) : base(view) {
        view.Handler = this;
        if (service == null) throw new ArgumentNullException("service");
        customerManagementService = service;
    }

    public void LoadCustomers() {
        var customers = customerManagementService.GetCustomers();
        View.ViewModel.Customers = customers;
    }

    public void ShowView(IMainView parentView) { View.Show(parentView); }

    private readonly ICustomerManagementService customerManagementService;
}

IMainView

public interface IMainView : IView, IParentView, IHasUiHandler<IMainViewUiHandler> { }

MainPresenter

public class MainPresenter : Presenter<IMainView>, IMainViewUiHandler {
    public MainPresenter(IMainView view, ICustomerManagementPresenterFactory factory)
        : base(view) {
        view.Handler = this;
        if (factory == null) throw new ArgumentNullException("factory");
        this.customerManagementPresenterFactory = factory;
    }

    public void ManageCustomers() {
        var p = customerManagementPresenterFactory.Create();
        var parent = this.View;
        p.ShowView(parent);
    }
}

CustomerManagementForm

public partial class CustomerManagementForm : Form, IMainView {
    public CustomerManagementForm(CustomerManagementViewModel viewModel) {
        InitializeComponent();

        if (viewModel == null) throw new ArgumentNullException("viewModel");

        customerManagementViewModel = viewModel;
        customerManagementViewModel.PropertyChanged += 
            new PropertyChangedEventHandler(
                customerManagementViewModel_PropertyChanged);
    }

    public ICustomerManagementUiHandler Handler { get; set; }
    public string Message { get; set; }
    public string Title { get { return Text; } set { Text = value; } }
    public CustomerManagementViewModel ViewModel { 
        get { return customerManagementViewModel; } 
    }    

    public void Show(IMainView parent) {
        MdiParent = (Form)parent;
        Show();
    }    

    private void CustomerManagementForm_Load(object sender, EventArgs e) {
        Handler.LoadCustomers();
    }

    private void CustomerManagementViewModel_PropertyChanged(
        object sender
        , PropertyChangedEventArgs e) {
        customerManagementViewModelBindingSource.DataSource = ViewModel;
    }

    private readonly CustomerManagementViewModel customerManagementViewModel;
}

I believe I could reduce complexity or repetition with my IChildPresenter and IChildView which are pretty similar. Besides, The Show() methods belongs expressly to the views, whilst ShowView() methods actually belongs to presenters. I tend to follow some such basic nomenclature to make it more understandable.

So now I wonder, what do you guys think of this approach?

Did I miss the point?

Any possible imporvement?

\$\endgroup\$
3
\$\begingroup\$

I'm no expert in design patterns, but I fail to see the benefit in having a completely empty interface.

public interface IParentView { }

Interfaces do two things for us.

  1. They enforce a contract. Anything implementing this interface must have this specific public api. Which allows for...
  2. The ability to code against an expected api, not an implementation.

While you've done a good job of coding against IParentView instead of IChildView, what does the parent view get us? Nothing as far as I can tell. There's no real contract being made here, so there's no reason you couldn't have coded directly against IChildView. (Which, at that point, would not be a good name.)

But don't take just my word for it, 24 people on stack overflow agree that this pattern is a code smell.

\$\endgroup\$
  • \$\begingroup\$ IParentView shall be used as a type constraint, so no other types is passed, except the type I decided to make the parent view, namely IMainView. So, this fulfills its role being a contract. As for the default getter/setter, the Title is implemented within a Form, so changing the Title shall actually change the Form.Text Property, whilst no control has been defined yet to display the Message property to the user. \$\endgroup\$ – Will Marcouiller Sep 26 '14 at 14:04
  • \$\begingroup\$ I removed the part about the getter/setter, but I still don't think you're getting anything from having an empty interface. \$\endgroup\$ – RubberDuck Sep 26 '14 at 14:12
  • \$\begingroup\$ I mean, yes... you do get the illusion of typesafety of passing an argument as an IParent, but is it really actually type safe if there's no guarantee that IChildview1 and IChildView2 actually have compatible APIs? \$\endgroup\$ – RubberDuck Sep 26 '14 at 14:15
  • \$\begingroup\$ Then, one simply doesn't implement the IChildView, or does nothing in its implementation. And actually, the use of IChildView simply makes sense if one wishes a parent/child UI relationship such as WinForms MDI or the like, otherwise one doesn't need to use the IChildView interface. \$\endgroup\$ – Will Marcouiller Sep 26 '14 at 14:43
  • 1
    \$\begingroup\$ I have read further on the linked post and the pattern I use is actually called a Marker Interface Pattern, and even Java uses it with its Serializeable interface. The other way around is to use Custom attributes. There's a point I find interesting with the Marker Interface Pattern, that is the compile-time check, which won't be using Custom Attributes. Somehow I get your your point and agree, and I also agree with my point of view. I'd like to get further details on this topic. \$\endgroup\$ – Will Marcouiller Oct 1 '14 at 18:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.