What you will learn in this course
Reuse solutions, not just code. Code Smarter with Design Patterns helps you identify problems that occur repeatedly in your code, and solve those problems in a standardized way. Wrap your head around the concept of the design pattern; a programming solution or template that can be used in many different situations, and discover new tools for creating successful software.
You'll get answers to these questions:
What constitutes a design pattern?
How do I write code that can adapt to change?
How can I identify code that needs changing, and which patterns to apply to anticipate the next stage of development?
How can I best implement the Gang of Four design patterns with .NET technology?
How to approach unit testing and test driven development, utilising design patterns
- Understand the rationale behind design patterns
- Understand the basics of Unified Modeling Language (UML)
- Understand how best to apply design patterns to .NET to effectively leverage the framework
- Learn how to write code that is testable by leveraging design patterns
- Refactor code to use the most appropriate pattern
- Implement and code the following patterns, by solving puzzles and performing various tasks:
o Singleton Pattern
o Factory Pattern
o Decorator Pattern
o Observer Pattern
o Strategy Pattern
o Template Pattern
o Command Pattern
o State Pattern
o Proxy Pattern
o Iterator Pattern
o Visitor Pattern
o Composite Pattern
o Repository, Unit of Work
o Adapter and Facade
Course outline and topics
This chapter discusses the reasons why you should study design patterns. Design patterns offer the ability to reuse solutions not just code. By reusing already established designs, you get a head start on problems and avoid gotchas; you benefit by learning from the experience of others; you do not have to reinvent solutions for commonly recurring problems.
Design patterns establish a common terminology allowing developers to use a common vocabulary and share a common viewpoint of the problem. Design patterns provide a common point of reference during the analysis and design phase of a project.
The course will use UML as a means to communicate pattern intent, and this chapter will introduce key UML concepts.
Some solutions require the use of a single object instance across the whole solution, for example naming services, cached objects. This chapter will introduce the singleton pattern as a solution, along with variations for thread safety, and other varieties of single instance based on thread affinity.
Strategy and Template
What we can be 100% sure of with software is that constantly needs to evolve. What we also know is every time we change existing working code there is a risk that we will break it. What we need is an approach that will allow the software to evolve without having to modify existing working code. The strategy and template patterns allow us to build solutions that can evolve without the risk of effecting existing well tested code.
Test-Driven Development and Unit Testing
Unit testing is now considered by most developers to be an essential part of writing high quality software and it is now compulsory in many organisations. Test-driven development (TDD) is the next logical progression from unit testing, which delivers demonstrably higher levels of code quality than simple unit testing. This module takes a detailed look at unit testing and TDD, from the real-world benefits to how to craft good unit tests (and how to avoid writing bad ones). It also explains the AAA pattern, the red-green-refactor approach to TDD, the continuous test runner and how to manage large suites of unit tests so that the right tests can be run frequently and quickly.
There are occasions when you want to decouple the knowledge of which type to create from the client code. This is essential for effective unit testing. There are a variety of creation pattern that will allow you do this by encapsulating the necessary knowledge of how to create the object thus allowing the actual implementation used to vary. In this chapter we will look at the Factory, Builder and Prototype patterns.
The ability to notify interested entities of changes to an object state is a fundamental requirement of most Object oriented solutions. There are many ways to do this but there is a danger that we will build a tightly coupled system, we prefer to build a loosely coupled system. The typical way of implementing the observer pattern is to use interfaces, but we will show that delegates and events are a far more flexible and efficient way of implementing the pattern on the .NET framework.
Iterator, Composite and Visitor
There is often a need to access every object inside an object hierarchy, user interface control objects, documents, business entities, file systems. etc. The iterator pattern provides a standard means to achieve this, .NET provides a standard implementation of this; further the C# language simplifies the implementation further through language extensions. The visitor pattern gives us the ability to layer behavior onto the hierarchy without the need to change the underlying implementation of the hierarchy. Continuing the theme closed for modification open for extension.
One of the key design pattern goals is to write code that is closed for modification and open to extension, this pattern will show that an object's behavior and responsibilities can be extended at runtime, as opposed to design time using inheritance. This will allow us to combine a variety of behaviors far more efficiently that normal inheritance. Examples of decorators in the framework are BufferedStream, SynchronizationWrappers, and XmlValidatingReaders.
The command pattern allows us to encapsulate invocation, allowing the invoker to be decoupled from the client and the recipient, this enables us to build a variety of different invokers to deliver custom thread pooling, and invocation logging to build fault tolerant solutions. We continue to extend the command pattern to not only encapsulate forward invocation but also undo invocation, allowing us to build a complex undo sequence through a series of simple undo commands.
The proxy pattern manages the invocation of an object, by hiding the true nature of the location and invocation mechanism of the object, typically used to make remote method calls. Proxies have a host of uses from the conventional RPC style to more elaborate uses such as virtual proxies, security proxies and caching proxies. Proxies can be built manually, using the CLR or using third party libraries that can be used to automate the process. We will look at all three approaches to building effective proxies.
In many cases object behavior will depend on the state an object is in, we typically model the internals of objects through the use of finite state machine. This pattern provides a means to map a finite state machine into a series of classes where each class represents a different state, and thus providing different behaviors. This approach allows us to add new states and transitions without effecting existing code, continuing the theme closed for modification, open for extension.
Dependency Injection and Inversion of Control
Many developers have heard about the clear advantages of adhering to the SOLID principles of good design, but not all are fluent in applying them. Dependency injection is not only key to writing SOLID code, but it can also help implement late binding, improve extensibility, assist large teams practicing parallel development and improve maintainability. Importantly, it can also significantly improve the testability of code. This module not only looks at the different forms of dependency injection and at how to use dependency injection correctly, but also explains how to use an Inversion of Control (IoC) container to handle dependency management and complex construction automatically. Configuring the IoC container in code and in the application configuration file, which allows the application to be reconfigured without the need for recompilation, are both covered.
Doubles and Mocking
When unit testing code, it is frequently necessary to test code that uses external resources, such as the file system, databases and remote services. To make this possible, a substitute for the Depended On Component (DOC) is injected into the System Under Test (SUT) that provides the necessary interface but which doesn't perform any external operations. These substitutes come in a number of forms, including fakes, stubs, spies and mocks. Although all of these forms can be written by hand, doing so is both tedious and time consuming. This module explains the different forms and shows how to use mocking frameworks to create and configure these substitutes with very little effort.
Data-Driven Unit Testing and Code Coverage
Developers who practice unit testing and TDD often find themselves needing to test a behaviour in a number of different ways. While this can be achieved by writing a number of very similar unit tests, a better way is to create a data-driven unit test, which allows a single test to execute many times with different test values. This module examines how to create and configure data-driven unit tests. It also looks at code coverage, which is a way to determine easily how effective the unit test suite is and to identify any areas in the software which could benefit from the creation of additional tests.
ORM's and Repository Pattern
Object Relational mappers provide a good separation of business logic from persistence logic; however using a given ORM often leads to code being coupled to a particular ORM. This has two effects, one making it hard to test application logic without a physical database, two results in your code being coupled to a given provider. The Repository pattern creates an anti-corruption layer to allow you to take advantage of an ORM whilst still maintaining your ability to test, and change your choice of ORM simply and cleanly. Finally we round of this module looking at Unit Of Work pattern to aggregate many repositories and provide centralized change tracking across all the repositories. In this chapter we will use MS Entity Framework (EF) as an ORM, evolving code from using EF directly to utilizing the patterns thus taking full advantage of the ORM whilst keeping our code flexible and testable.
The Model/View/Controller design pattern provides guidance on how to separate the various component parts of a UI application. Building UI applications without clear separation often leads to the inability to unit test the application, as a tester is required to drive a user interface. In this chapter we will show how to separate the various concerns to build a testable Web UI application.
An anti-pattern (or antipattern) is a pattern that may be commonly used but is ineffective and/or counterproductive in practice. This appendix describes developmental, architectural, and managerial anti-patterns.