Wednesday, July 27, 2011

Decoupling classes for user interfaces

Someone recently asked the following question on Stack Exchange:

What is the best practice when it comes to writing classes that might have to know about the user interface. Wouldn't a class knowing how to draw itself break some best practices since it depends on what the user interface is (console, GUI, etc)?

In many programming books I've come across the "Shape" example that shows inheritance. The base class shape has a draw() method that each shape such as a circle and square override. This allows for polymorphism. But isn't the draw() method very much dependent on what the user interface is? If we write this class for say, Win Forms, then we cannot re-use it for a console app or web app. Is this correct?

The reason for the question is that I find myself always getting stuck and hung up on how to generalize classes so they are most useful. This is actually working against me and I'm wondering if I'm "trying too hard".

My answer was as follows:

There are a number of tried and true design patterns that have been developed over the years to address exactly what you are talking about. Other answers to your question have referred to the Single Reponsibility Principle - which is absolutely valid - and what seems to be driving your question. That principle simply states that a class needs to do one thing WELL. In other words raising Cohesion and lowering Coupling which is what good object oriented design is all about - does a class do one thing well, and not have a lot of dependencies on others.

Well...you are right in observing that if you want to draw a circle on an iPhone, it will be different than drawing one on a PC running windows. You MUST have (in this case) a concrete class that draws one well on the iPhone, and another that draws one well on a PC. This is where the basic OO tenent of inheritance that all of those shapes examples breaks down. You simply cannot do it with inheritance alone.

That is where interfaces come in - as the Gang of Four book states (MUST READ) - Always favor implementation over inheritance. In other words, use interfaces to piece together an architecture that can perform various functions in many ways without relying on hard coded dependencies.

I've seen referece to the SOLID principles. Those are great. The 'S' is the single responsiblity principle. BUT, the 'D' stands for Dependency Inversion. The Inversion of Control pattern (Dependency Injection) can be used here. It is very powerful and can be used to answer the question of how to architect a system that can draw a circle for an iPhone as well as one for the PC.

It is possible to create an architecture that contains common business rules and data access, but have various implementations of user interfaces using these constructs. It really helps, however, to have actually been on a team that implemented it and see it in action to really understand it.

This is just a quick high-level answer to a question that deserves a more detailed answer. I encourage you to look further into these patterns. Some more concrete implementation of these patters can be found as well known names of MVC and MVVM.

0 comments:

Post a Comment