문제

Given:

  1. Our organization has a standard windows form application
  2. The form and business logic are intertwined -- i.e. the Autonomous View. We know that the Autonomous View pattern makes writing unit tests hard. The goal is to take a single form and to decouple the presentation logic from the form itself so that we have a stand alone domain entity/object that can more easily have unit tests written against it.
  3. We are NOT doing a complete rewrite. I am looking at a gradual approach that allows one screen to be dealt with in isolation.
  4. After some research I think the Presentation Model pattern or the MVVM pattern are most appropriate for the way this organization does things.
  5. This organization generally prefers fewer layers of indirection, thus simpler (even though less robust) is better.
  6. The transition from what was to what will be should be formulaic (easy to teach any developer already familiar with in-house concepts). That's why they want to stick with Windows forms over other newer technologies like WPF.
  7. The domain model will know nothing of the view (form). The form will be completely aware of changes to the domain model. That makes 2 layers of indirection. 1 more would be fine (that's why I would allow for MVVM).

Most of the MVVM examples I've found illustrate how it fits together with WPF, not with plain old Windows forms.

Two questions:

Given all this, is there anything I stated that has you thinking I'm not on a good path or that I am on the wrong track? I'm looking to make some recommendations to management.

Finally, do you know of a good online code example that would help me flesh out a prototype?

도움이 되었습니까?

해결책 4

I hate to be one to answer my own question but additional research and experimentation has illuminated things a bit.

The ideal solution would be one where the extraction of a domain object would itself be very similar in nature to the design practices used at the organization. Essentially, it would have been an extraction of a visual form in all of its UI glory, to a more abstract form ("VM" or view-model) that embodies the concepts used on the forms (combos, buttons, etc.) without using actual user controls. The VM would then be bound to the view where the controls are actually found.

Unfortunately, this is an arduous endeavor where the abstractions would have to be robust enough to behave in a similar manner with the controls to which they map. Being that our organization has dozens and dozens of custom controls, that is no short order.

Take a simple in-house combo box for example. A combo represents an input with a restricted set of choice options. Our data model is an in-memory dataset. The combo binds to a particular table and as the user fills in the form and thus provides criteria his permissible selections are further filtered. That combo displays a value to the user (DisplayMember) and persists another to the DB (ValueMember). There's probably a bit more to it than that; however, the "field" abstraction for a combo has to account for some of these concepts so that it can ultimately be properly mapped to the view. Furthermore our form subscribes to a host of events originating from its controls. These would have to be mapped in some fashion. So it seems to me that a great deal of form (view) functionality has to have a counterpart in the VM. The VM thus becomes to some degree a shadow of the physical form it represents.

I originally thought it might be possible to extract a very general abstraction of a form and its constituent controls and then to map that abstraction (which closely resembles the original form) to the view. While it may be possible, I now believe it is impractical and may itself present a wide array of new issues.

So while I wanted to avoid having tests run against an actual form instance, I think this may be better that trying to add a layer of abstraction. Building that abstraction would be similar in effort to rewriting the our screens using another paradigm and thus keeping none of the original shape of the screen. That is, even as we might rewrite one form at a time thus allowing a gradual transition, the final version would not be reusing as much of the original structure as would have been hoped.

다른 팁

Perhaps the best you can do is look at your code base with an eye for the single responsibility principle. One of the main reasons that that the autonomous view (and winforms, which fosters it) is so difficult to test, is because developers tend to lump everything together in an event handler.

Take this question on SO - https://stackoverflow.com/questions/16599778/asp-net-mvc3-linq-make-multiple-related-rows-fields-in-a-1-to-many-relationshi, it's about MVC3, but it's a total mess - one method that is responsible configuring the gridview, configuring the response, and retrieving the data to populate the gridview. It's tough to even know where to begin to answer the question, let alone write any reasonable (read: concise & fast to execute) tests to ensure the solution works.

If you can carefully go through your code, and carefully encapsulate all of the business logic and/or integration points into services/components/interfaces (and implementations), preferably in separate assemblies outside.

Once you have broken everything the logic into separate components each focused on it's own concern you can write tests for then to ensure that they perform the task they are meant to, without having to test any other part of your application, these will be your services. You want to shoot for each service type to be an interface backed by an implementation.

After you have all of these different projects and assemblies written and tested, you introduce them back into your application using inversion of control (a form of dependency injection). This further decouples your UI from the various business logic that your application is meant to perform. The dream here, is that you will get to a place where, when you are ready to rewrite the UI, you can reuse the business logic components that have already been written and tested.

I'm thinking that the winform class will have a constructor that accepts many arguments (a mix of the various services discussed above). The DI framework will be responsible for providing the services to the winform class. After that, ideally, your winforms event handlers will be relatively small, simply calling service methods with parameter values collected from the various form fields.

Here's a post on using Castle Windsor (a dependency injection framework) with winforms: Using Castle.Windsor with Windows Forms Applications. There are many different DI frameworks, I use Castle Windsor because it's the one I learned first, they all do essentially the same thing, so, all you need to do is find one you are comfortable with.

Here is a separation of concerns tutorial that is based on a web application, but should be instructive as to how to identify & refactor services out of an existing 'kitchen sink' application.

This answer is turning into a book, and it's all very abstract. The main thing is you need to think about application as a set of lego blocks that you combine to produce functionality (each block is a concern), and the UI is simply the glue that holds the blocks together (that analogy isn't perfect).

Really, it's more art than science, but you can train your mind to look at problems in that manner, and once you do, programming, in general, becomes a lot easier. The curve is a bit steep, but, keep at it and you will get there.

In WPF we can do a DataBinding between the objects in the view and the content in the ViewModel and because of that MVVM is the perfect pattern for WPF.

I've learned MVVM from this example: http://www.scottlogic.co.uk/blog/colin/2011/05/a-simple-windows-phone-7-mvvm-tombstoning-example/

It is a Windows Phone Tutorial, but if you can understand how it works you can easily use it with any WPF application.

I think your best option is to put ElementHosts in your existing winforms app and put WPF content inside of them. Then you can use WPF and MVVM and gradually upgrade your winforms app to WPF.

Even if you use some kind of helper framework in winforms, you will always encounter limitations which will lead to dirty, hacky solutions that pollute the cleanliness you're trying to achieve.

For example, there's nothing in winforms that can provide the same functionality as a WPF ItemsControl with DataTemplates. Take a look at this example of a ListBox with some DataTemplates in WPF. There's no way you can achieve anything like that in winforms with a ListBox, no matter how many third party frameworks you incorporate.

Bottom line: put ElementHosts with WPF content inside each Form, and you can gradually clean up all the winforms mess into a clean, MVVM, testable, resolution independent, simply beautiful WPF UI. I insist (and nobody has yet been able to prove me wrong) that this is the only way to get anything decent out of winforms.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top