By Jean Minnaar
Purpose of Article
• To explain the flaws in what is generally considered Pattern-Oriented development and to show how to overcome those flaws by putting a retooled Pattern-Oriented approach to work.
• To stress that informal design patterns make up a large portion of the patterns discovered during Pattern-Oriented development.
• To list examples of a few design patterns of the Framework as found in a number of Web applications developed by the author - add practical value to an otherwise pure theoretical conversation.
• To show that design patterns can and often depend on other design patterns.
In summary, to give you a marginally different viewpoint of design patterns, stress the importance of Pattern-Oriented development and thereby instill the need to look into design patterns and software frameworks more closely.
"We adopted Agile/Scrum, Test-Driven Development and we are object oriented. Our productivity certainly improved in comparison to what we experienced before. But why do our projects still overrun? This is very frustrating. There must be something else we are we missing..."
There can certainly be a multitude of reasons. For instance, do you have thousands of lines of CSS because the developers did not understand how to separate positioning from style and how to "extend" CSS classes? Lack of skills certainly could be a huge contributor to the lack of success that development teams experience.
More than often it is a matter of emphasis not being placed on Pattern-Oriented Development. In general, Pattern-Oriented Development greatly lacks relevant and valuable coverage in the Information Technology space. Without being pattern-oriented, systems could end up containing twice as many lines of code. We also know that with an increase in lines of code, the complexity of a system exponentially increases.
Does this mean you have to look for where you missed the Strategy, Adapter, or Bridge, etc. pattern in your system? Looking for where you missed the formal design patterns is likely not where you will find the answers. The issue is more likely the lack of discovering and acting upon informal design patterns in your system.
Design Pattern Objections
Let's look at the objections against design patterns first and then at how to actually do it right.
Patterns have been criticized widely and rightly so. Here are some objections:
1. The need for design patterns resulted from using computer languages or techniques with insufficient abstraction ability. Peter Norvig provided a similar argument. He demonstrated that 16 out of the 23 patterns in the Design Patterns book (which is primarily focused on C++) are simplified or eliminated (via direct language support) in other languages.
2. Design patterns lack formal foundations. At an OOPSLA conference, the Gang of Four was (with their full cooperation) subjected to a show trial in which they were "charged" with numerous crimes against computer science. They were "convicted" by 2/3 of the "jurors" who attended the trial.
3. Design patterns do not differ significantly from other abstractions. Some authors allege that design patterns don't differ significantly from other forms of abstraction, and that the use of new terminology (borrowed from the architecture community) to describe existing phenomena in the field of programming is unnecessary.
4. Design patterns lead to inefficient solutions. It is almost always a more efficient solution to use a well-factored implementation rather than a "just barely good enough" design pattern.
Common Definitions of Libraries and Frameworks
If you look at Internet articles you will encounter Library and Framework definitions such as the following:
• A software library is essentially a set of functions that you can call, usually organized into classes. Each call does some work and returns control to the client.
• On the other hand, a software framework embodies some abstract design, with more behavior built in. In order to use it, you need to insert your behavior into various places in the framework. The framework's code can also call your code at given points.
Software Framework Redefined
Here is my view on software frameworks:
A software framework is a set of design patterns (formal and/or informal) accompanied by the code necessary to take care of the common functionality of the design patterns and to expose the framework component functionality to the developers.
Frameworks are almost always accompanied by what people describe as a library. For instance, you will find, in the framework described in this article, components that get registered with the framework which provides some functionality for the components. This could be described as library functionality, but this is essentially the Decorator pattern.
Software Frameworks are all about design patterns, elimination of repetitive work, and are used to speed up development. They are used to streamline software development by allowing designers and programmers to devote their time to meeting software requirements rather than dealing with the common functionality and more standard low-level details of providing a working system. A software framework's purpose is to reduce overall development time.
With Pattern-Oriented Development the framework (a pattern driven software framework) is the forerunner deliverable leading the rest of development. It is generally understood that the most effective software frameworks are those that evolve from refactoring the common code of the enterprise. The software framework covered in this article certainly evolved this way - it was created by developers for developers.
What the above tells you, is not to stop at design patterns but to take it a step further by adding the code for the design patterns by which time we label it as a [software] Framework - a Framework with Pattern-Oriented origins.
References to "Framework" in the rest of this article pertain to the pattern-oriented [software] frameworks as described in this section.
Assume the task given to you is to service Apache helicopters and C-130 freighter planes. How will you go about this? First you break the work up for each to see what must be done to service them.
For the helicopter you have to:
• Resurface the rotor blades
• Do a bunch of other stuff
• Pump the tires
• Do a bunch of more things
For the C-130 you have to:
• Do some stuff
• Pump the tires
• Do some more stuff
From the above domain narrative, object discovery shows that we need a pump. We are good so far. But what commonly occurring problems did this analysis exercise not reveal? Later in the article you will see real world examples of such.
The mentioned Framework applies to Web apps that run on over 100 device types including desktops, tablets and smartphones. The Framework examples shown in this article pertain specifically to Web-based applications. These Web applications were all developed with a mobile-first approach. Regardless of the screen size of the device, the Web applications developed, work equally well and renders well regardless of the form factor of the device.
The mobile-first approach, just like the Web technologies being used in these apps, impacted the direction taken with this Framework.
From the bits of the Framework exposed below, you will be able to tell that exception case handling was part of the solution right off the bat. Habitually developers actually write for the flawless conditions first. Why not? They get told, "We have to have this next Tuesday!" When they are done with "Tuesday's" deliverable, they claim they are 90% done and they "only" need to add exception handling. Logging could be another deliverable in that remaining "10%" bucket.
Then the developers start adding the code for the exceptional cases, logging and so on. This results in the code having to be re-opened (violating the open-close principal). It also results in code paths that worked before, to be broken now and the system remains in a "90% done" state for a very, very long time.
Not meaning to deviate from the topic, but let me just mention that the developers take cover under the Agile methodology and call these next efforts, refactoring. There is a big difference between refactoring and building systems in a wrong and hurtful sequence.
Pattern-Oriented Development eliminates the mentioned issues - exception cases are handled right from the start, along with logging and more. Furthermore, these challenges are handled consistently across the application. The Framework takes care of the heavy lifting for the developers, thereby freeing them up so that they only need to focus on their domain.
What is very important to take note of is that this Framework already covers the common components found in any Web application. That in itself is an excellent start for any Web app project.
Let's look at a few examples of patterns found in the Web apps developed by the author.
Pattern: Server Side Generated User Messages
We have situations where at the server-end your code recognizes the need to show a message to the user. This could occur during a page request using any HTTP method (GET, POST, etc.) or during an AJAX (GET, POST, etc.) request. Developers should not have to be concerned with what the message boxes look like or how messaging should be implemented. There could have been a redirect between the time the message was turned over to the Framework and the time the user gets to see the message.
The developers shouldn't have to concern themselves with such details. Even if another message gets handed off to the Framework, be that elsewhere in the server-side code in the same HTTP request or the newly redirected to page, the user should be presented those messages. The Framework is responsible for making sure the user gets to see every message in a timely fashion.
Is this one pattern or more? There is (1) the pattern telling the developers how to implement messaging or actually how to turn messaging over to the Framework instead of each developer rolling his/her own. Then, there is (2) the pattern dealing with the actual implementation details of messaging (not described here). Finally, (3) underneath the second pattern are patterns like the Proxy Pattern. The patterns mentioned in this article pertain to the Framework component plus a brief mention of the (usage) patterns presented to the application developers (as opposed to the Framework developers). The patterns geared towards the developers at large usually are and should be very simple patterns - simplicity rules!
Earlier in the aircraft servicing example, the article mentioned that not all common problem areas get discovered from the problem/requirement narratives. When dealing with requirements or acceptance criteria such a messaging problem as seen here would possibly not be revealed and it would very easily be dealt with by a developer wherever it is needed in whatever way the developer sees fit at the time.
Pattern: Client Side Generated User Messages
The out-of-the-box Web technology methods window.alert() and window.confirm() lack in functionality as well as professional user experience. Developers request the Framework to present messages to the user together with the button text and user options (for multiple selection message boxes), then leave the rest up to the Framework. At the end the developer code gets called back and told which option the user selected (for multi selection message boxes).
Pattern: Web Forms and AJAX Post Backs
This pattern addresses the fact that when using ASP.NET WebForms and that performance is critical, not to use ASP.NET's Update Panel for any AJAX work. Here the Framework takes care of the core AJAX needs such as:
• Packaging up the form elements for post back.
• Make the AJAX call on behalf of the developer's code.
• Assist at the server end with interpreting the received request and data.
• Deal with any exception handling and resultant user messaging including validation error handling.
Developer code only needs to request the Framework to perform the AJAX call and (only) implement the success code path.
Pattern: App Insights
App Insights is just a better term for what we usually call Help. Help is usually an on demand type Help, but you cannot clutter your UI everywhere with "information" icons either. The approach taken here is to show the user the App Insight when the user visits a given function of the system for the first time.
There, the user can temporarily or permanently dismiss the shown message. After permanently dismissing the message, the message will not be shown to the user again when he/she visits that particular functionality.
Developers simply need to ask the Framework to show a particular App Insight at the appropriate place in their code. The Framework knows to show the App Insight or not and will turn control back over to the developer code when done.
Pattern: Limit Work Unit to Single Tab per Browser Instance
When you cache data in the middle tier the complication arises when a user opens the same work unit in multiple tabs or windows in the same browser instance. In the stateless HTTP world, the server cannot distinguish between the two tabs so as to maintain each tab's cache separately.
If it is justifiable, you can opt to not allow the same Work Unit from being opened multiple times in the same browser instance. There are other alternatives like generating and sending tab identifiers to the server to be able to track the separate tabs' cache, but stopping a user from opening the same Work Unit in more than one tab or window is the route we took with the relevant apps.
The developers just have to populate a hidden control (injected there via the mater page or shared view) with the appropriate Work Unit identifier.
Pattern: Prevent Post Back Hack Attempts
With the modern-day SPA application approach where the user views a list of entities and then identifies the entity the user wants to work with, there is the tendency to ship the entity's identity column to the client.
When the user selected task 8, the system made sure task 8 still exists and that the user gets the latest version of task 8. It then shows the above dialog to the user. When the user is done making the necessary changes, the dialog's Update will send the task data to the server. The server now needs to know which task to update AND won't be concerned with AJAX hack attempts.
Typically the developers tend to send the internal identifier of the entity (task 8 in this case) to the client/dialog. On update, the idea then is for the AJAX post back to echo the identity, and for the server side to use that identity to know which entity to update. This creates an opening for hackers to change the identity in the post back and try to update an entity the user does not have access to.
Additional security code (with a resource consumption and performance penalty of course) is then needed to detect and avoid such hack attempts. The Prevent Post Back Hack Attempts pattern provides an alternative to sending identities to the client and ensures that a hacker can only get to its own entities without the need for additional (expensive) security code and checks.
Pattern: Work Unit Cache
For performance, security, and other reasons an application will remember given data for the work unit in the Middle Tier. When a user jumps from one Work Unit to the next, the previous Work Unit's data should be removed from server cache. The only way to do that is to keep each Work Unit's cache separate. Cache needs to get cleaned up at appropriate times or else server memory gets stressed which leads to performance problems.
Pattern: Cross Work Unit Cache
The cache common across multiple Work Units should be cached in the Framework's Cross Work Unit Cache. Typically this cache will only get cleaned up after the user logs off. Developers will access the common cache items from the Framework and not roll their own solutions.
Pattern: Layer Integration
Layer Integration involves particular return structures between the layers. Based on the content of the return structures, the developer code will know that a request was successful or not. In case of failure, the Framework takes over handling of the response.
These return types are there to accommodate the following challenges:
• Concurrency conflicts
• Updates with no changes made by the user
• Attempts to create a duplicate entity
The above items are covered on their own later in the article where you will get better insights into what this is all about.
Pattern: Concurrency Conflicts
The approach taken here is the pessimistic locking approach - we want to know when an entity got updated since a user pulled a copy of it for update and inform the user of such at the time when the user tries to update the entity. The entity could also have been deleted in the meantime by another user.
This pattern describes to the developers how to detect and capture concurrency conflicts and how it is integrated into the Framework's Layer Integration.
Pattern: Update and No Changes
A user can hit the "Submit Changes" button when the user made no changes to the entity. Which tier should be used to perform this check and how should it be done? Layer Integration is the key to this pattern.
Pattern: Prevent Duplicate Entry
Logically it is the responsibility of the Business Rules layer to prevent a user from adding a duplicate entity into the system. Such a solution is a chatty and expensive solution, plus it leaves a small room for duplicate entry anyway. This pattern describes how duplicate entry attempt prevention is done. The Framework's Layer Integration plays a major role with this pattern.
There are three types of logging:
Of course it is unnecessarily difficult to add logging after the fact. It should be part of the Framework from the start.
Pattern: Dropdown Controls
Dropdown controls need to be handled in a standardized fashion in terms of:
• Empty lists
• Adding combo box capabilities
• Prompting the user to select an item
• Dealing with default selections.
Pattern: Adapt Validation Field
When the system detects, at the client or sever end, invalid data in a form field, that field has to be styled as being in error. The pattern describes what the developers should do to get the advantage of the Framework's adaptation of validation fields.
Pattern: Session Expiration
Your system relies on an active session, but if the user is inactive after bringing up an entity for update in a dialog, then later returns and attempts to update the entity, the AJAX call arrives at the server end that does not have the session data any longer. There is no need for developers to code for session expiration during any type of HTTP call. The Framework takes care of that even taking to the user the Login page if re-login is appropriate.
Pattern: Handling of Unsupported Browsers
Developers should be assured that by the time a browser reaches their pages, the browser is a supported browser. Leave the rest up to the Framework.
Pattern: Cookies Configuration
Checking to see if browser cookies are turned on or off should not be a developer task. It should solely be the responsibility of the Framework.
The Framework has many more components than mentioned so far. No Framework is complete without a full set of extension methods and a number of other helper type functions either. Examples of typical programming language types one would extend are URI, String and Date/Time.
This article is only aimed to touch on a few patterns and not to provide complete documentation on the entire Framework.
Reusability of the Framework
First be reminded that this Framework is specifically for Web based applications. The emphasis is also on development of Web Applications (interactive systems) rather than on development of "static" Web Sites.
Conceptually, with the Framework being a set of design patterns, it is applicable to any Web based software project. The server side code of this Framework is currently based on Microsoft's technology stack. For different technologies, some of the Framework's server-side code will have to be ported into the programming language of choice. However, being written in C#, it is not a complicated task. The UI side's implementation of this Framework depends on jQuery.
There are other places in the code where the Framework relies on given capabilities, like that of the underlying technology stack's Object Relational Mapper, and such code will have to be rewritten for a different set of technologies.
Appendix A: Definition of Terms
The quickest way to explain a Work Unit is to look at a few examples of Work Units.
In a Project Management app one of the Work Units is the Project Work Unit. (This Project Work Unit follows the Single Page Application pattern) In this Work Unit the user starts off by being presented with a list of tasks under a particular project. The user can select a task and, via a dialog, modify the task. After modifying the task, the dialog closes and the user is back at the task list with the relevant task now containing the updates. The user can search for tasks, delete a task (via dialogs again), change the view of the list data, and much more. The Project Work Unit encompasses all these project related functions.
Similarly, in the same app, there is a Projects Work Unit where the user initially gets shown a list of projects. Again, the user can perform all kinds of project related functions while in the Projects Work Unit. These could be to add or modify projects, assign or remove users from a project, and more.
An example of a Wizard Pattern Work Unit is where you change to a different subscription package. From the first to the last page in this work flow constitutes the Work Unit.
In looking at the Work Unit related patterns you will come to the realization why establishing Work Unit boundaries is very important.
Tiers and Layers
The following Tiers and Layers:
UI Tier: Comprised of the UI layer running in the browser on a client machines.
Middle Tier: Comprised of the Business Fašade, Business Rules and Data Access layers running on the Web/HTTP servers.
Database Tier: Comprised of the Database layer running on the database servers.
The following statement does not necessarily apply to a J2EE environment but it does apply to a.NET environment: More than one Tier can run on the same machine but Layers of the same Tier should not be spread out to run on different machines. (It is not the intent to explain the J2EE versus.NET statement as it involves.NET's virtual processes, where the HTTP.SYS driver resides and more - a topic for another article).