Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Abstraction is good, but it's important to remember that at some point <em>something</em> has to know a thing or two about a thing or two, or else we'll just have a pile of nicely abstracted legos sitting on the floor instead of them being assembled into a house.</p> <p>An inversion-of-control/dependency injection/flippy-dippy-upside-down-whatever-we're-calling-it-this-week container like <a href="http://code.google.com/p/autofac/" rel="noreferrer">Autofac</a> can really help in piecing this all together.</p> <p>When I throw together a WinForms application, I usually end up with a repeating pattern.</p> <p>I'll start with a <code>Program.cs</code> file that configures the Autofac container and then fetches an instance of the <code>MainForm</code> from it, and shows the <code>MainForm</code>. Some people call this the shell or the workspace or the desktop but at any rate it's "the form" that has the menu bar and displays either child windows or child user controls, and when it closes, the application exits.</p> <p>Next is the aforementioned <code>MainForm</code>. I do the basic stuff like drag-and-dropping some <code>SplitContainers</code> and <code>MenuBar</code>s and such in the Visual Studio visual designer, and then I start getting fancy in code: I'll have certain key interfaces "injected" into the <code>MainForm</code>'s constructor so that I can make use of them, so that my MainForm can orchestrate child controls without really having to know that much about them.</p> <p>For example, I might have an <code>IEventBroker</code> interface that lets various components publish or subscribe to "events" like <code>BarcodeScanned</code> or <code>ProductSaved</code>. This allows parts of the application to respond to events in a loosely coupled way, without having to rely on wiring up traditional .NET events. For example, the <code>EditProductPresenter</code> that goes along with my <code>EditProductUserControl</code> could say <code>this.eventBroker.Fire("ProductSaved", new EventArgs&lt;Product&gt;(blah))</code> and the <code>IEventBroker</code> would check its list of subscribers for that event and call their callbacks. For example, the <code>ListProductsPresenter</code> could listen for that event and dynamically update the <code>ListProductsUserControl</code> that it is attached to. The net result is that if a user saves a product in one user control, another user control's presenter can react and update itself if it happens to be open, without either control having to be aware of each other's existence, and without the <code>MainForm</code> having to orchestrate that event.</p> <p>If I'm designing an MDI application, I might have the <code>MainForm</code> implement an <code>IWindowWorkspace</code> interface that has <code>Open()</code> and <code>Close()</code> methods. I could inject that interface into my various presenters to allow them to open and close additional windows without them being aware of the <code>MainForm</code> directly. For example, the <code>ListProductsPresenter</code> might want to open an <code>EditProductPresenter</code> and corresponding <code>EditProductUserControl</code> when the user double-clicks a row in a data grid in a <code>ListProductsUserControl</code>. It can reference an <code>IWindowWorkspace</code>--which is actually the <code>MainForm</code>, but it doesn't need to know that--and call <code>Open(newInstanceOfAnEditControl)</code> and assume that the control was shown in the appropriate place of the application somehow. (The <code>MainForm</code> implementation would, presumably, swap the control into view on a panel somewhere.)</p> <p>But how the hell would the <code>ListProductsPresenter</code> <em>create</em> that instance of the <code>EditProductUserControl</code>? <a href="http://code.google.com/p/autofac/wiki/DelegateFactories" rel="noreferrer">Autofac's delegate factories</a> are a true joy here, since you can just inject a delegate into the presenter and Autofac will automagically wire it up as if it were a factory (pseudocode follows):</p> <pre><code> public class EditProductUserControl : UserControl { public EditProductUserControl(EditProductPresenter presenter) { // initialize databindings based on properties of the presenter } } public class EditProductPresenter { // Autofac will do some magic when it sees this injected anywhere public delegate EditProductPresenter Factory(int productId); public EditProductPresenter( ISession session, // The NHibernate session reference IEventBroker eventBroker, int productId) // An optional product identifier { // do stuff.... } public void Save() { // do stuff... this.eventBroker.Publish("ProductSaved", new EventArgs(this.product)); } } public class ListProductsPresenter { private IEventBroker eventBroker; private EditProductsPresenter.Factory factory; private IWindowWorkspace workspace; public ListProductsPresenter( IEventBroker eventBroker, EditProductsPresenter.Factory factory, IWindowWorkspace workspace) { this.eventBroker = eventBroker; this.factory = factory; this.workspace = workspace; this.eventBroker.Subscribe("ProductSaved", this.WhenProductSaved); } public void WhenDataGridRowDoubleClicked(int productId) { var editPresenter = this.factory(productId); var editControl = new EditProductUserControl(editPresenter); this.workspace.Open(editControl); } public void WhenProductSaved(object sender, EventArgs e) { // refresh the data grid, etc. } } </code> </pre> <p>So the <code>ListProductsPresenter</code> knows about the <code>Edit</code> feature set (i.e., the edit presenter and the edit user control)--and this is perfectly fine, they go hand-in-hand--but it doesn't need to know about all of the <em>dependencies</em> of the <code>Edit</code> feature set, instead relying on a delegate provided by Autofac to resolve all of those dependencies for it.</p> <p>Generally, I find that I have a one-to-one correspondence between a "presenter/view model/supervising controller" (let's not too caught up on the differences as at the end of the day they are all quite similar) and a "<code>UserControl</code>/<code>Form</code>". The <code>UserControl</code> accepts the presenter/view model/controller in its constructor and databinds itself as is appropriate, deferring to the presenter as much as possible. Some people hide the <code>UserControl</code> from the presenter via an interface, like <code>IEditProductView</code>, which can be useful if the view is not completely passive. I tend to use databinding for everything so the communication is done via <code>INotifyPropertyChanged</code> and don't bother.</p> <p>But, you will make your life much easier if the presenter is shamelessly tied to the view. Does a property in your object model not mesh with databinding? Expose a new property so it does. You are never going to have an <code>EditProductPresenter</code> and an <code>EditProductUserControl</code> with one layout and then want to write a new version of the user control that works with the same presenter. You will just edit them both, they are for all intents and purpose one unit, one feature, the presenter only existing because it is easily unit testable and the user control is not.</p> <p>If you want a feature to be replaceable, you need to abstract the entire feature as such. So you might have an <code>INavigationFeature</code> interface that your <code>MainForm</code> talks to. You can have a <code>TreeBasedNavigationPresenter</code> that implements <code>INavigationFeature</code> and is consumed by a <code>TreeBasedUserControl</code>. And you might have a <code>CarouselBasedNavigationPresenter</code> that also implements <code>INavigationFeature</code> and is consumed by a <code>CarouselBasedUserControl</code>. The user controls and the presenters still go hand-in-hand, but your <code>MainForm</code> would not have to care if it is interacting with a tree-based view or a carousel-based one, and you could swap them out without the <code>MainForm</code> being the wiser.</p> <p>In closing, it is easy to confuse yourself. Everyone is pedantic and uses slightly different terminology to convey they subtle (and oftentimes unimportant) differences between what are similar architectural patterns. In my humble opinion, dependency injection does wonders for building composable, extensible applications, since coupling is kept down; separation of features into "presenters/view models/controllers" and "views/user controls/forms" does wonders for quality since most logic is pulled into the former, allowing it to be easily unit tested; and combining the two principles seems to really be what you're looking for, you're just getting confused on the terminology.</p> <p>Or, I could be full of it. Good luck!</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload