CHAPTER 4

image

Design Patterns

A design pattern in programming is the equivalent of a recipe in cooking. Can you make chocolate chip cookies without following a recipe? Of course, you can. However, you will be investing a lot of time and effort into trying to solve an issue that’s already been solved. You will basically reinvent the wheel, we mean, the cookie.

Not only do design patterns assist developers in solving common problems when developing software, they also establish a ubiquitous language to use when discussing a particular implementation. Design patterns are written in such a way that there are universal terms associated with the implementation details of the solution. This will promote efficiency among your development team. For instance, if your entire team is well versed in design patterns and the vocabulary that is used to describe the problems and solutions, it’s much easier to communicate ideas or problems without resorting to using a whiteboard. This absence of ambiguity can benefit junior developers too. If the team speaks the same language, then experience levels are irrelevant.

If you know what a design pattern is, then chances are you’ve heard of the Gang of Four. Just in case you haven’t, the Gang of Four is a group of authors who collaborated to write Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional, 1995), the de facto book on design patterns. This is a must-have reference for any software developer. There are 23 design patterns in the book. However, the book you are currently reading is not about design patterns, so we will cover only the patterns that are relevant to the application that you’ll be building throughout the rest of the book. Before we cover the design patterns that you will use, it’s important for us to discuss the different types of architectures used in modern domain-driven applications.

Architecture Types

If you are a software developer writing a line-of-business (LOB) application, chances are that you are familiar with the concept of n-tier application design. This style of design describes software that separates common application components into libraries, which allows different parts of your application to reside on multiple computers or servers.

In the past, it was common for line-of-business systems to keep all application resources on the same machine as the application’s executable. These resources include INI files, Access databases, trace logs, and other pieces of data required for the application to solve a business problem. As technology prices have dropped and computer literacy has increased, there is now more data than ever, and that data is growing exponentially. It’s no longer practical to keep an application’s data on a single user’s workstation.

These days, most businesses have full departments dedicated to information technology. In a typical office environment, nearly all employees have their own PC, which they use to run the applications that allow them to do their day-to-day work. This creates the need for centralized databases and other shared resources that can handle access from multiple users.

Most applications communicate with some sort of server. These servers include relational database management systems (RDBMSs), web servers for web applications and services, Active Directory domain controllers for authorization and authentication, and many other types of servers.

Since most LOB applications use more than one machine to accomplish a task, the software development community has created several architectural designs to take advantage of these distributed resources. Two of the most common architectural designs include the Layered architecture and the Hexagonal/Ports and Adapters architecture. These architectures are similar in that they both work to separate the domain model, also known as business objects, from the standard boilerplate code used to handle common tasks, such as data access or file system access. We’ll discuss these two architecture types now and illustrate the pros and cons associated with each type.

Layered Architecture

The older, and more common, architectural style is the Layered architecture. When using the Layered architecture design, you separate your application into distinct logical boundaries. Each layer is often created as a class library (DLL); however, this is not always the case. One of the benefits of this separation of similar components is the ability to execute application programming interface (API) calls on multiple computers. This distributed execution promotes scalability and, in some circumstances, provides performance gains for the end user. One thing to understand is that you aren’t required to use multiple computers to implement the Layered architecture. There are many applications in which the code from all layers resides on the same machine as the application’s executable. Still, the logical separation of each layer promotes loose coupling between application components and code reuse through the class libraries. An application that uses the Layered architecture typically consists of the layers shown in Figure 4-1.

9781430267768_Fig04-01.jpg

Figure 4-1. Typical Layered architecture

User Interface Layer

As you can see, there are five layers. In Extensible Application Markup Language (XAML) applications, the user interface layer consists of the XAML-based views of your applications. A view can represent a window, page, user control, or any other file that consists of XAML markup to define the visual aspects of your application. Generally, the user interface layer is light, with very little logic. So, where do you put the code that handles input from the user? This is the job of the presentation layer.

Image Note  There are some implementations of the Layered architecture in which the user interface layer and the presentation layer are combined. This is often found in WinForms applications and some web applications. You could even take this approach with XAML-based applications; however, as you will see, the awesome features that were introduced with Windows Presentation Foundation (WPF) and XAML made way for a new paradigm for creating unit test–friendly applications with rich user interfaces and components that can be built with no event handlers in the code-behind. This separation of the user interface from the user interface logic makes XAML-based applications a prime candidate for the five-layer approach.

Presentation Layer

The presentation layer is used to handle any logic that occurs as a result of an action in the user interface layer. This includes the user clicking a button, a window resizing, a finger tap, reorientation of a device, and so forth. In WinForms development, event handlers in the code-behind of a Form or UserControl class handle this logic. As you will see later, XAML’s data-binding capabilities, coupled with the Model-View-ViewModel (MVVM) design pattern, provide new and interesting ways to handle logic in the presentation layer.

Service Layer

The service layer acts as a gateway between the presentation layer and the domain layer. It’s important to put some consideration into the application service layer’s interfaces that you want to expose as part of your API. The design of the service layer will vary greatly, depending on your choice of the physical location of the service layer logic. For instance, the service layer interface design will be much different for services that are exposed via web service technologies than for a service layer that is meant to run on the same machine as the rest of the application.

Domain Layer

Next, the domain layer is the most important part of any application. The domain layer contains your domain model, which should reflect the business processes that belong to your problem domain. It’s important that your domain layer be ignorant in regard to all other layers in your application. You don’t want your domain layer to be dependent upon the user interface technology or the data access library that you are using because you need to be able to use your domain model in other implementations, such as web or mobile applications. By the same token, you don’t want to have to change your domain layer to be able to introduce a new database system into your organization. Your design should ensure that the domain layer is pure and consists only of classes that represent the business domain in which you are modeling.

Infrastructure Layer

Finally, the infrastructure layer is the “low-level” code that you will find in most applications. This is where you keep the “plumbing” of the application. The infrastructure layer will contain data access, logging, exception handling, caching, MSMQ, performance metrics, code to send e-mails, and classes to support interaction with other software systems and API libraries. Code found in the infrastructure layer should support all of the mechanisms required that aren’t specific to the business domain but are required in order for your application to function properly.

Once again, the domain layer shouldn’t care about any other layer in the system. It should be completely self-sufficient.

The Layered architecture approach works great for separating your code into logical units that are related. One of the problems with this design is that if you need to make a change to the lower levels (which is often the case), then you may need to change the levels above to account for the dependencies on the lower levels. The Layered architecture’s strict rules for layer access can prove to be problematic regarding changes in requirements. The Hexagonal architecture, also known as the Ports and Adapters architecture, provides a different way to view layers and solves the issues regarding extending existing applications.

Image Note  A common practice associated with layered architectures is that a layer can use any layer below it, but no layer can reference any layer above it. The only exception to this rule is the domain layer.

Design Patterns Used Throughout the Book

We will now review the design patterns that will be used throughout the reference application. The patterns covered include Repository, Adapter, Model-View-ViewModel, and Command.

The Repository Pattern

The Repository pattern is related to data access. When you use the Repository pattern, you create classes called repositories, which serve as abstractions to how your data is retrieved and stored in the database. To the user of the API, a repository is like an in-memory collection of domain entities. A repository class has no database-specific implementation details, which allows you to switch your database server easily, with no need to modify the repository’s code. This also allows you to create special repository implementations for unit testing.

The test repository implementations allow you to write unit tests in which you can hard-code method-return values, which gives you complete control over the execution of your unit tests. This will ensure that your unit tests won’t break because of a database server that’s down or because of unexpected data in your database. In fact, when you create a test repository in which you hard-code method-return values, you remove the need for a database schema to exist, which allows a team to work on different parts of an application at the same time. Long gone are the days in which you had to wait for a database administrator (DBA) to create a database before you could proceed with development. By using the Repository pattern and Test-Driven Development (TDD), you can write code without any concern for unfinished dependencies. By definition, unit tests should be controlled, predictable, and fast. By removing the need to connect to a database to run your unit tests, the Repository pattern will assist you in writing unit tests that reflect these conditions (see Listing 4-1).

The Adapter Pattern

The Adapter pattern is a well-known pattern in the .NET Framework. If you’ve ever used ADO.NET to fill a DataSet, chances are that you’ve used an implementation of the Adapter pattern. Just as the name suggests, the Adapter pattern involves creating a class that serves as an adapter, which coordinates work between two objects with different interfaces.

To understand the Adapter pattern, it helps to imagine a real-world adapter. One author of this book enjoys recording music when not programming. The headphones that he uses in his studio as well as his laptop have a 1/8-inch connector that most computer sound cards support. The mixer has a 1/4-inch jack for headphones. Obviously, the 1/8-inch connector is too small to use with the 1/4-inch headphone jack on the mixer. Thus, should he buy a new pair of headphones to record? No, he simply needs to buy a 1/8-inch to 1/4-inch adapter. This allows him to plug his headphones into a 1/8-inch female port on one side of the adapter, which then converts the signal and has a 1/4-inch jack on the opposite side. This is exactly how the Adapter pattern works.

As mentioned previously, ADO.NET utilizes the adapter design pattern to coordinate the work between an IDbConnection interface and an IDbCommand interface to fill a DataSet with results from a query. Listing 4-2 shows an example.

The MVVM Design Pattern

The Model-View-ViewModel (MVVM) design pattern is known as a User Interface design pattern. MVVM is based on the Presentation Model (PM) design pattern and was created specifically for XAML and WPF by John Gossman. The MVVM pattern allows you to separate your domain model’s business logic (the model) from the user interface (the view) by adding a layer of abstraction that encapsulates all of the logic required by the user interface, and it processes requests from the user to perform actions on or retrieve data from the model.

When using MVVM, the view is represented by XAML. The view is directly dependent on the view model; however, the view model is view agnostic. You should not have any user interface elements or other dependencies related to the view in the view model. The view model’s public interface should be well defined. Any changes to the view model’s public interface will usually require that any views associated with the view model be changed as well.

The view model will work with the application service layer to modify or retrieve data from the model. The model consists of domain entities, which should only contain code that is relative to the business domain at hand. It’s important not to allow any of the user interface–related code from the view or view model to “leak” its way into the domain model. The domain model should be isolated from any code that does not relate to the business domain. See Figure 4-2, which represents the communication between the different parts of the MVVM design pattern.

9781430267768_Fig04-02.jpg

Figure 4-2. The flow of communication when using the MVVM design pattern

As you can see, the view will declaratively create an instance of the view model. The view model’s properties will be data-bound to controls declared in the view. The data binding will allow for automatic updating of view control display values as well as to bind user interface control events and commands related to user interaction to methods and properties of the view model. The view model will respond to user interaction events to interact with the model. The view model will communicate with the model to make changes and report updates to the model. This allows a complete separation of the user interface, presentation layer logic, and domain model of your application.

Basic MVVM Implementation in WPF

In this section, we will show how to create a bare-bones MVVM implementation using WPF and C#. This example will illustrate the key concepts of the MVVM design pattern. The view will be represented as a WPF window. The view model will contain properties that provide data to display in the view using data binding. Notice that there is no code-behind needed, only XAML, the view model, and the model that represents a class called PersonModel. There will be more examples and explanations throughout the book. Listing 4-3 shows the WPF application markup.

As you can see, the Application class can hold application-level resources such as styles and templates. The main purpose for the Application class is to provide the main startup object of the application, which is specified in the StartupUri attribute. Next up is Listing 4-4, which shows the definition of MainWindow.xaml, which represents the view.

Here you have a WPF window with two Grid objects for controlling the layout of the TextBox and TextBlock elements. You add a new XML namespace (xmlns:vm), and instead of referencing a URI, you reference the namespace of the view model. Now you can access any class defined under that namespace in XAML. In the Window.Resources element, you add a resource to the view model, and you give the resource a key to "viewModel" so you can access the resource in XAML. You set DataContext equal to the viewModel resource. Now when you are using data binding on other controls in the Grid, the binding will reference a property on the view model by the same property name. Now let’s take a look at Listing 4-5, which is the source code for the model class.

As you can see, the model is simple. It represents a Person entity. There are two properties exposed: FirstName and LastName. The class implements the interface INotifyPropertyChanged, which allows the model to alert the view model of any property changes. Finally, Listing 4-6 shows the source code for the ViewModel class.

The view model’s source applies the same techniques as the model. You implement the INotifyPropertyChanged interface to alert the view of changes to the properties (via data binding). You also include a new property named FullName, which will return the first and last names when either of those values changes. The first and last names are represented as TextBox controls in the view, so their values can be changed. The FullName property is represented by a TextBlock, which means that the user can’t modify the value. However, because of the data binding to the view model, you can instantly see the FullName TextBlock’s Text property value change when you modify either the first or last name TextBox.

ICommand: The Cure for the Common Event Handler

We will wrap up this chapter on design patterns by discussing Microsoft’s implementation of the Command design pattern. At first glance, the ICommand interface seems simple, which it is. However, don’t let the simplicity fool you. The way that the commands are used in XAML-based user interfaces provides a powerful alternative to adding event handlers in your view’s code-behind file. Table 4-1 describes the ICommand interface members.

Table 4-1. ICommand Interface Members

Member Name

Description

CanExecute

The method that indicates whether the specified command’s state is valid for execution. For example, WPF implements a command to support the Paste command when something is copied to the clipboard. This method will allow all user interface elements that are bound to the command to be enabled and disabled based on their return value. In the case of the Paste command, this means that any paste button element will be enabled or disabled based on the method’s return value.

Execute

The method that contains the logic to execute when the command is triggered and the command’s CanExecute method returns true.

The ICommand interface provides a test-friendly alternative to event handlers in your code-behind for handling user events. There are several controls that have properties called Command and CommandParameter. For this example, you will use a simple WPF button control. The CommandParameter property should be set before the Command property. This is because both the CanExecute method and the Execute method have a single parameter argument of type object. When the XAML parser processes the Command property, it will check the CanExecute method to determine whether the button should be enabled or disabled. If the parser processes the Command property before the CommandParameter property, the parameter argument of the CanExecute method will be null, which, depending on your implementation, will either throw an exception or, at the least, return false.

So, what value do you use for the CommandParameter property? This depends on the nature of your command; however, we typically use the same strategy for nearly all ICommand implementations. Ninety-nine percent of the time, we will store the view model that contains a property that represents the ICommand implementation in a window-level ResourceDictionary. Next, we will bind the view model reference to the CommandParameter attribute of the Button control. Next, we will bind the view model’s ICommand property of interest to the Command attribute of the Button control. Then, in the CanExecute method, we will check the parameter argument for null. If it’s null, then we return false. If not, we cast the object parameter as the view model type and store the view model in a private field in the ICommand implementation. Since both CanExecute and Execute have the same object parameter, we will add a null check for the parameter of the Execute method. If the parameter is null, we’ll then check the private field that should already contain the view model by way of the CanExecute method for null. If they are both null, then there is a problem, and we throw an exception.

Otherwise, we have a valid reference to the view model to work with in the Execute method of the ICommand implementation. With the view model handy, we perform the expected command in the Execute method, which usually involves calling a method or changing properties on the view model reference. This will trigger the data-binding mechanism by way of the INotifyPropertyChanged interface in the view model, and any modified properties will then be updated in the view. Perhaps an example will clear up any confusion that you may have.

The following example is a WPF window with two TextBox controls and a Button control. Each TextBox is supposed to hold a number, and the Button will execute a command that will add the two numbers together and store the answer in a property called Sum. Sum is data-bound to a TextBlock, so when the command executes, the Sum property is updated, and this will update the view.

Image Note  It may be helpful to set a breakpoint in the CanExecute method, as well as the Execute method in the ICommand implementation. We also suggest that you set a breakpoint in the view model’s ICommand property implementation. This will give you some insight on how the ICommand bindings are used.

As you can see, Listing 4-7 illustrates a view that defines the user interface of the example. Listing 4-8 provides an ICommand implementation that provides the logic to add two numbers on the view model. The implementation will also provide change notification to all bound user interface controls. Finally, in Listing 4-9, you have a view model, which provides properties to be bound to the view. The “Add numbers” button is bound to the command implementation in the view model. The Command property of the button is bound to the AddNumbersCommand property of the view model. The CommandParameter property of the button is bound to the view model. This property will use the bound object as the parameter argument of the ICommand, CanExecute, and Execute methods. Figure 4-3 shows the running example.

9781430267768_Fig04-03.jpg

Figure 4-3. Output of Listing 4-9

Image Note  The XAML markup for Listing 4-7 has the CommandParameter attribute set before the Command attribute. This order is required. If you specify Command before CommandParameter, ICommand is bound before the view model is passed to the CanExecute and Execute methods.

Summary

Design patterns are extremely valuable to any software development team. The Repository pattern allows you to develop your data access code with no need to wait for the database to be created. You will use the Adapter pattern to create an architecture that will make changes to your application extremely easy to implement for years to come. We’ve covered the MVVM design pattern, which we consider to be a XAML application’s bread and butter. You also learned how to use the ICommand interface to eliminate code-behind event handlers by creating Command classes that are extremely simple to unit test. In the following chapters, you will analyze a business problem and use what you’ve learned to create a complete, multiplatform line-of-business application using domain-driven design.

The next chapter will introduce you to unit testing. The chapter will cover popular unit testing frameworks and provide examples on testing the MVVM calculator example code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.197.95