The DomainObjects should be separate from the UserInterface. The UI should have as little domain logic in it as possible, and the DomainObjects should have no UI in them. Following this pattern will make it easier to put a new UI on your applications, like converting a client-server application into a WebApplication. It will also make it easier to reuse your DomainObjects in a new application, and will reduce the duplication in your application.
You will eventually SeparateDomainFromPresentation if you follow OnceAndOnlyOnce and keep growing your application. However, separating domain from presentation from the beginning you'll build your application faster.
Summary of Disagreement
Here is a summary of much of the counter-positions taken below. Most UI API's are already interfaces. The separation being proposed is to create yet another interface to wrap or echo the first. This is seen as unnecessary or excessive redundency or excessive LayerOfIndirection. One is not hiding implementation, but hiding a vendor or industry standard interface that may be 70% perfect with a self-rolled one that is probably something like 80% perfect under ideal conditions. Thus, a lot of middle-men code and activity is created for at most a 10% improvement in interface abstraction. CantAbstractMuchPastInterfaces.
It's also sometimes not maintenance-friendly to separate. See BusinessLogicDefinitionDiscussion.
The author of the article referenced from MvcIsNotObjectOriented at first seems to take the opposing view - put GUI code in the model - but then backs away from it and discusses the PresentationAbstractionControl pattern.
I am not sure I fully agree with this. They are not purely separable in practice, and the heavy separation often creates cluttered indirection layers before they are actually needed (YagNi). For example, it is tough to make a layer that can translate/swap between a GUI interface and a web interface unless you stick with a lowest-common-denominator. The two are so different that a one-to-one translation often does not make sense. You simply have to do things differently in one than the other when you go beyond trivial applications. The whole "flavor" of interaction is different. Also, non-TuringComplete template-driven "style" managers are often too "dumb" and inflexible to deal with contingencies. I suggest keeping separation in mind, but don't go hog-wild with the concept. If you can find natural separation, use it. Otherwise, don't force it.
Another problem is the granularity. For example, suppose we had this event:
event onPageLoad(thisPage) if userNotToSeeButtonX(....) thisPage.buttonX.visible = False end if end eventThis implements the business rule: "Only show button X for certain users". It is both business logic and GUI logic. You can't really separate them because this is where they intersect. Further, if the function "userNotToSeeButtonX()" is small code-wise, then we might as well include it here also. Why have indirection if the indirection takes more code and creates more cross-references than simply including it? When reading code, I don't like having to jump all around to find stuff. It slows productivity. In other words, separation for the sake of separation is anti-YagNi with little or no future benefits.
This is easily separated from the GUI, would be better off separated from the GUI, and will result in simpler cleaner code.
event onPageLoad(thisPage) thisPage.buttonX.visible = pageModel.userNotToSeeButtonX(....) end eventThen it's testable and keeps the business rules away from the GUI in the model where they belong. There's never an excuse to put code in GUIs, there's always a cleaner way.
How is this testable? I can only test this visually, by running the code as each user type and checking every page this specific button may appear on. When a new page is created that wants to reuse this button, we can only hope that the programmer is diligent enough to find all of the separate configuration attributes that need to applied to the user interface button.
Is there a "page model" to mirror each GUI screen/page? If so, isn't this a violation of OnceAndOnlyOnce?
Yes, there can be, and no it's not a violation of OnceAndOnlyOnce. The form is concerted with widgets and binding and presentation of data, logic doesn't belong here, while the model is concerned with getting that data into a presentable form, logic goes here. These are separate concerns and deserve separate classes, FearOfAddingClasses is quite illogical, adding a few more classes tends to greatly simplify things, consolidate logic, reduce duplication, etc. With this separation, the program is much much more testable, flexible, and quite easy to slap any GUI on, without it, the app is hard to test, hard to change, generally buggier, hard to slap another GUI on, and generally just hard to work with.
I don't know, it seems like excessive indirection and duplication to me. Unless you know you are going to swap the GUI later on, I would deem it too much extra work if I were a manager. It could add an extra 15% to 40% effort to original coding and maintenance I would estimate. It is as if one is creating a "mirror" model of the GUI system and whenever one goes into the IDE they have to find the corresponding "other side" to make changes. It is bordering on a violation of OnceAndOnlyOnce. YagNi and OnceAndOnlyOnce override potential future swapping here in my book. Most companies will switch languages nearly as often as UI frameworks anyhow, and UI conventions keep changing over time (mainframe, mini, micro, GUI, web, etc.).
I would deem it too much extra work if I were a manager.
As a programmer, I would deem this MicroManagement.
As another programmer, I would deem it a lack of understanding. Adding those extra class, reduces code, and simplifies everything, including testing. More classes means simpler classes, smaller easier to understand classes allowing one to work much faster with less fear of breaking things. More classes simplify the design and help separate the concerns, it's not more work, it's less work, it's also easier work. It's working smarter rather than harder, putting code in the GUI makes programming more difficult, testing more difficult, change more difficult, it's working harder.
I don't find these claims very specific. We will just have to AgreeToDisagree I guess.
Note that this is only 1/2 step away from a FourLayerArchitecture.
...easier to put a new UI on your applications, like converting a client-server application into a WebApplication
This is rather naive. Converting a stateful client-server application into a stateless web application involves far more than changing the user interface semantics. It also involves involves redesigning the code packaging to move from an executable to a set of components and may involve a language port as well.
In general, I have found that as we increase the separation between the user interface and the underlying code, errors tend to go up. The extreme seems to be in "visual" programming where one programs the user interface in an entirely separate language and programming environment from the remainder of the code. We have spent far too much time verifying component attribute settings and have had far too many bug reports because of inconsistencies in these settings.
The enforcement of business rules begins at the user interface. The minimum and maximum length, the value range, the permitted character set, etc., should be controlled to allow the user to enter appropriate values. Too often, these rules are duplicated 3 times (or more) in the user interface, the business logic layer, and in the database (although I doubt I'll ever convince any DBAs that their precious constraints are of no benefit). I want to be able to create the rules for entering, editing, and displaying a specific data type, say a social security number, once and reuse it on multiple screens without cutting, pasting, and comparing attribute settings.
As for the ease of changing the user interface, it is not there. Try to change, for example, between a drop down list and a set of radio buttons. (As an added surprise, once you think you are done, go back and check the TAB order on the control.) There is a great degree of coupling between the user interface control and the values that populate it. I would like my screen object to be a simple collection of data objects with the display type contained within the data object. If I want a data object to appear on another screen, I merely would add it to that screen object collection as well, and all operating characteristics would carry over.
This is a perfect example of where the separation should be. The domain has a collection of things. The UI decides how to best present that collection. There's no coupling here, the model gets the collection (all necessary logic here), the UI presents it. [How does the user interface know that a collection is an exclusive set of choices? The selection of radio buttons is a business rule to restrict possible user selections. The user interface doesn't just magically decide this.]
I started programming in visual environments in the 1980's and appreciate the lowered level of effort to create the initial screen display. I have also noticed, however, the high proportion of bugs that resulted from the user interface, and also have noticed that these errors are ones that seem to get solved and then reappear several releases later. This is a class of errors that was largely absent in the bad old ASCII terminal based display days.
I don't find that separation of domain and presentation provide the "ease of change" benefits that proponents claim, and that the separation costs more due to lack of reuse and increased errors at the interface between the domain and presentation.
-- Wayne Mack
The common problem seems to be that one has to violate OnceAndOnlyOnce in order to get separation. To separate them you have to mirror some or all of the UI model on the "other side", and then try to keep them synchronized. That is why there is the duplication you talk about. I don't see how to get smooth separation without mirroring the model. Do you pro-separationists just live with the duplication, or is there a way around it? Plus, I can find no clear-cut rules for what is only a UI concern and what is only a business rule. They appear to be often intertwined. -- top
With a little experience, one finds ways to encode the rules in the domain model, not in the UI, and still reuse them in the UI so there aren't any violations of OnceAndOnlyOnce. This usually requires decent OO skills, so it's not surprising that many people can't seem to separate the two successfully, but that doesn't mean it can't be done.
I don't dispute that it can be done. I dispute the net advantages. It creates more indirection between the IDE and related code, making more maintenance steps.
Separating UI rules from business rules is quite easy, the UI doesn't contain any rules. The UI should only bind data to a screen element an/or removed data from the screen element. There's no need to have any rules at all in the UI. If you think there are, I'd like to see some example's of rules that should be in the UI. Maybe we just don't have the same idea of what a rule is.
How about we revisit the conditional button hiding example above. Or how about validation. For example, validation that at least one of two phone numbers, workPhone and homePhone, is filled in.
OK.... you want this
class Page ..event onPageLoad(thisPage) ....if userNotToSeeButtonX(...) ......thisPage.buttonX.visible = False ....end if ..end event ..function userNotToSeeButtonX(...){...} end classI want this
class Page ..pageModel = new PageModel ..event onPageLoad(thisPage) ....thisPage.buttonX.visible = pageModel.userNotToSeeButtonX(....) ..end event end class class PageModel ..function userNotToSeeButtonX(...){...} end class[Added dots to fix and prevent TabMunging]
With the model, we now have a place to put the rule, and test it, without any more real indirection that yours. You make a call to a userNotToSeeButtonX, and I call model.userNotToSeeButtonX. You can't test yours as easy as I can test mine. Nor is yours as flexible. Mine is also simpler, as it contains no conditional statements in the page, only binding. Conditional stuff is in the model, where it can be OnceAndOnlyOnce, even with multiple interfaces. No matter how you cut it, there's two kinds of code here, logic, and GUI. You can put them in one class, like you, or split them between two smaller classes by putting logic in one and GUI code in the other. The two object system is simpler than the one object system, because the objects are simpler, having only one responsibility and one level of abstraction each. More objects often simplify a design by simplifying an existing class into smaller parts. Model/View or Document/View architecture is standard industry practice because it works better than one big class doing both. Please explain your point of view.
{I don't like the idea of a mirror class for each page. If you have page1, page2, page3, etc., then you will need pageModel1, pageModel2, and pageModel3. And, there is still the extra step of going from the IDE to the decision code. It may not be a function/method but a small conditional expression. I only made it a function for example purposes. If it is a simple expression, then a function/method is not justified IMO. It gets bloated to wrap every damned line of code. Further, I don't see how busting things into smaller classes brings about magic simplification. You just have more classes to wade through. Further, what if the conditional referenced GUI components as part of its conditions? You are just jumping back and forth between the real pages classes and the mirror page classes. That jumping takes more time and/or code.}
See FearOfAddingClasses. Even a small conditional expression, should be hidden behind a function as it greatly simplifies later growth and reuse and clarifies intent. Putting implementation inline is just bad. More classes is a good thing, it means each one is more focused on a single responsibility and simplifies the code within it. Complex things are not made from complex code, they are made from lot's of simple code, in lot's of simple classes, working together to create complex behavior. Believe it or not, adding more classes usually actually decreases code by increasing reuse and simplifying conditional logic.
More code makes for more errors. Wrapping everything does not automatically create benefits. More indirection makes for more hopping around to debug or change stuff, adding to maintenance costs.
Read carefully.... believe it or not, adding more classes usually decreases code by increasing reuse and simplifying conditional logic. Please read FearOfAddingClasses 10 times or until you realize it IS true. Adding classes reduces maintenance costs and reduces code. It's not indirection, it's abstraction, and it makes things easier, not harder. Code should be simple enough to not need debugging, adding more classes makes this possible. If you need a debugger, you need to write better simpler code. Adding classes allows single location changes, since each class has one responsibility, thus no hopping around. Answer me a question, are you an OO programmer?
You don't need a mirror model to remove duplication. If a design results in duplication, THEN fix it.
The "fix" would involve rejecting your approach almost entirely. You seem to be proposing duplication of the GUI layout model, pure and simple. It may not be exact one-to-one duplication, but it is pretty close. You have two classes for every "page". That is a violation of OnceAndOnlyOnce in my book, and also a YagNi violation because there is no immediate need to have such "echo" classes. And, it is more code. In other words, "bloat". This may also lead to a HolyWar about lots of little methods/functions versus fewer, which other topics cover. No need to recreate that battle here.
I have an urge to rant, if you don't mind: Yes, I do have a fear of bloating my code with massive indirection and duplication. Yes I do have fear of unnecessary clutter. Yes I do have fear of eye burn-out from reading all that extra shit. It is my opinion that its often done out of an unrealistic sense of idealistic interface purism or a sense of bragging: "I isolate properly but you don't, neener neener!" It flunks practicality. --top
Let me present a real-world problem to see how coupling the user interface and the business logic would be beneficial. We capture a subject's weight in pounds and the business rule, as enforce by the database, restricts us to a range of 0 - 999, i.e., none of the subject data entered will be saved if the weight value entered violates its constraints. The coupled approach I propose is to create a weight class that uses a text box, has a character filter to reject all non-numeric values entered as they are typed, limits the text box width to 3 characters, contains the label "Weight in Pounds", and contains the database column name. This class can be a member of a database row collection and multiple screen collections. Whenever I add this class to a new screen, I am assured that all operations are carried with it. If I change the rules of the class, for example, extend the range to 0 - 9999, all using screens are updated. If I have multiple screens open and I change the value, the value is changed on all of the other screens as well with no refetching of values (pending a screen refresh). Most of the work in an MVC is implicit because I am sharing a single copy of the value, not duplicating it to provide separation. What is the separation alternative and what are its costs and benefits?
{If there is only one spot that does validation, then a dedicated Weight class/module is overkill IMO.}
We can go round and round in circles talking past each other, or you can just show some code. Because from what you describe, the weight class sounds like a model to me. If you have several screens sharing the same instance of the same weight class, that sounds like MVC to me. Maybe I don't understand your proposal, so code or pseudo code would help clarify.
A Proposed Implementation
Assume a base class UIControl with derived classes TextBox, RadioButtonArray, DropDownList, etc. These classes hold the necessary methods to Display() and read back a Value() from a screen image, but do not hold the data value.
class UIControl { UIControl(theLabel, theTag), Display(theValue), Value() }; class TextBox: UIControl; class RadioButtonArray: UIControl; class DropDownList: UIControl;Assume a base class Database'Column with derived classes Integer, String, Date, etc. These classes hold the necessary methods to extract a value(in a constructor) from and Update() a value into a database row, but do not hold the value.
class DatabaseColumn { DatabaseColumn(theDatabaseRow, theColumnName), Update() }; class Integer: DatabaseColumn; class String: DatabaseColumn; class Date: DatabaseColumn;Now, we can create a Weight class using the TextBox and Integer classes (assume inheritance for simplicity).
class Weight: TextBox, Integer;The Weight class now has the full responsibility to associate the database column name, the text box tag, the text box label, and the actual weight value. A Weight object would be associated with a database row object at construction:
myWeight = new Weight(myPersonalHistory);The Weight class can be added to one or more user interface screens through a simple add to a collection of UIControl objects.
thePatientInterviewScreen.Add(myWeight); theMedicalHistoryScreen.Add(myWeight);Note the minimal amount of code outside of the Weight class. The edit rules to be applied to the TextBox class are now contained within the Weight class as opposed to being part of any sort of View class. The Weight class can apply rules such as restricting keyboard input to the numeric set, limiting the size of the text box to 3 characters, handling value conversions if the database returns a value greater than 999, etc.
Once I have verified the correct operation of the Weight class on one display screen, I am assured it will be correct on any other screen I add it to.
Suppose I need to change the display to weight in kilograms while perhaps the database remains in pounds. I can update the screen label to "Weight in Kilograms", provide necessary conversions between the pounds and Kilograms, and provide a more complex set of data entry validation rules (perhaps reusing the conversion functions). This change is now propagated to the screens with no modification to any other classes.
Instead suppose I need to change the user interface to use a set of Radio Buttons or a Drop Down list, perhaps we are only really interested in weight in 100 pound increments (bear with the assumption, this is just an example). I can now change my Weight class derivation and probably some internal methods, but I do not need to touch any other classes.
class Weight: DropDownList, Integer;Although things like Model View Controller may be nice for the framework vendors, they force a lot of work onto the develop and maintenance programmers. Having the methods for a single item dispersed across multiple files leads to a high degree of coupling and requires modifications to be performed and duplicated across several files. I've seen far too much development time wasted when "this change works correctly except for on this one particular screen." I've seen too much time wasted because a simple screen change broke something that needed to be configured in some other file. I've seen how difficult it is to move a set of functionality from one screen to another, leading to frozen user interfaces despite complaints from the users.
Like I said earlier, seems we've been talking past each other. I have little objection to the above, the weight class is intelligent and managing it's own behavior. While I don't necessarily agree with smart UI controls and database columns, it is an acceptable approach. It is not however, putting logic into the form/page, as was the sample at the top of the page that I objected too. Those intelligent controls still keep the logic out of the form/page and in their own objects. Those intelligent controls are reusable, unlike code in a form/page, which was another of my objections. So it seems you didn't understand what I was objecting too, so let me clarify. I object to putting any logic into a GUI form/page, in such a way that it isn't reusable in another form/page or isn't testable via automation. If I can create your class, and test it without an actual GUI being shown, and can use it anywhere in the app, then I have no problems with the approach. [I think at least two of us have some underlying agreements, but there seem to be multiple voices. My preference is an approach that maximizes automated ProgrammerTesting and minimizes duplication of coding or configuration. I do not believe, though, that placing code within a GUI page necessarily makes it untestable. On the other hand, I feel it is an off shoot of frameworks, MVC, and Document-View architectures that create this problem by defining a separate View with unclear rules as to how to partition code between View, Model, Controller, or Document. It is probably time for a refactoring to highlight agreements and disagreements within this section. -- WayneMack]
Your approach still has a weakness. Were you to try and implement multiple GUI's, you'd have major problems, though it probably works very well if you only ever plan on a single GUI. [The approach above is very amenable to multiple GUIs by embedding a class factory within the Weight class. The inheritance for the Weight class would now change from TextBox to UIControl. I omitted this detail, as noted above, merely in the interest of keeping the example simple.]
Having an abstract page/form that represents a screens data does make it easy to implement several different representations of that screen, so MVC is still more flexible, and IMHO, simpler, if you plan on more than one view. [The problem with MVC is that so much View code is duplicated across Views. Ensuring that each View is implemented precisely the same way is what provides the difficulties in MVC.]
There are two change scenarios above:
It would only be PrematureAbstraction if there was only one view, but there are always at least two, the unit test, and the actual GUI. Therefore it's not PrematureAbstraction, it's NecessaryAbstraction. YouArentGonnaNeedIt doesn't apply, because YouAreGonnaNeedIt for testing. If you don't unit test, then we don't have much to talk about since I consider that mandatory.
You are assuming there is no way to unit test through the actual GUI. In same cases this may be true, and in some cases not. Besides, not all shops subscribe to unit testing, for good or bad.
Please consider that combining the GUI code and the business logic, in most environments, does not preclude ProgrammerTests that run outside the GUI. I say most environments, because I am not familiar with the test environments for things like ASP and JSP. I also note, however, that by pushing the business logic and display logic down into COM objects or Servlets, one can then test the methods directly. This is also true of Microsoft's Document-View architecture; the "windows" methods can usually be called directly. In the cases where this is not true, one merely needs to expose an underlying method for a test interface.
''This may be stupid question, but perhaps (for single inheritence languages) this ordering may be better (forgive any language mixing):
class UIControl { UIControl(theLabel, theTag), Display(theValue), Value() }; class TextBox: UIControl; class RadioButtonArray: UIControl; class DropDownList: UIControl; class DatabaseColumn { DatabaseColumn(theDatabaseRow, theColumnName), Update(), Name() }; class Integer: DatabaseColumn; class String: DatabaseColumn; class Date: DatabaseColumn; class Weight: Integer; myWeight = new Weight( myPersonalHistory ); myText = new Text( myWeight.Name( ) ); . . . thePatientInterviewScreen.Add( myText ); theMedicalHistoryScreen.Add( myText); myText.Display( myWeight ); . . . myWeight.Update( myText.Value( ) );''In this method, my object no longer needs display code and it is still simplistic to change from Text to RadioButtonArray or DropDownList. Display still calls the same set of data, it just needs to be properly accessible. Yes, a few more functions for one or the other class such, as DatabaseColumn including a Next() or Previous() function or using array notation for accessing each cell/row, may make this more portable. Display information is now made seperate from data resources. Smart resources are useful. This issue would then come down to an preference on where to place the reusable code. Both formats are still nothing less than reusable and this suggestion while it adds code lines, may be more readable to some users. infrequently posting, WyattMatthews
An example regarding limits of swappability of GUI engines: In VB you had a "combo-box" which is a pull-down list with the option of typing your own text. There is no HTML equivalent or one-size-fits-all substitution for HTML. The work-around will generally have to be considered on a case-by-case basis. (I suppose we could program in a default substitution for a heavily-used framework, but these kinds of things just add to the complexity and unpredictability.)
I have encountered a similar situation with report formats. Originally some cell borders had a double or "thick" line. However, some output options did not support double thickness. In some cases the lack of thickness could be ignored, in some a full space (empty row) was used to separate the items, and in others shading was used to set a given row or section apart. The substitution was essentially situational and not one-substitution-fits-all. I am at a loss to effectively automate such visual design decisions.
Another example is in how HTML tables can specify COLSPAN=100%. However, I could not find an equivalent for MS-Excel cells when I looked into a "generic report formatting" protocol in order to not reinvent the wheel for each format. Excel needed an explicit span number. If I wanted to target both output formats, I had to use explicit numbers, thus tossing out a nice abstraction that would allow auto-expanding if new columns were added. I suppose I could have made an intermediate column calculator, but that was way beyond the scope of the project, and would complicate the generic interface. I might as well have written an HTML-to-Excel translator and just use HTML (with output capture) rather than roll my own cell/table formatting API. HTML is not a perfect formatting language, but either is anything else so far. The Holy Grail of Format Genericy is elusive. Generally one is going to face something like this:
| Implementation Feature| ABCD ----------------------|------------------------ Multiple Cell thick. | X-X- Auto-span| -X-- Foo| X--- Bar| -X-X Etc...Either you abandon generacy, or stick with a lowest-common-denominator which is often limiting and does not satisfy the customer who wants rich presentations and will pay for it. Sometimes one can use "hints" to cater to multiple implementations, but this is messy.
-- Top
(copied from another discussion system)
Put yourself back in the early 90s before the web was revealed to the unwashed masses. How great it was for those of us with separated logic to easily and quickly front-end our systems with HTML.
The problem is not because it wasn't wrapped or separated, but because HTML and HTTP put *restraints* on UI flow that client/server systems didn't face. It is like writing output for a color printer, and then 5 years later you get a black-and-white one instead. Either you live with less options for 5 years, or you live with all the rich options your current system provides and worry about what-ifs later. And, this is assuming you could have predicted the loss of features. This is not merely a change of implementation, but a change in fundamental UI philosophy.
If the newer technologies are a super-set of the old ones, perhaps such would pay off. But as the case of web-based protocols, in many ways we got a sub-set of earlier GUI capabilities. With mobile devices we may have the same backward shuffle also.
Besides, if it was a matter of plug-and-play swapping, then why not just write the client/server API's to do HTML instead? Many c/s systems talked thru API's rather than raw system calls, so just re-implement them for web. Why not? Because HTML & Web is too limited to handle c/s API's.
See also: TooMuchGuiCode, SeparateMeaningFromPresentation, PresentationAbstractionControl, SeparationOfConcerns, MirrorModel
CategoryUserInterface, CategoryInfoPackaging, CategoryAbstraction, CategoryInterface