This Apress imprint is published by the registered company APress Media, LLC, part of Springer Nature.
The registered company address is: 1 New York Plaza, New York, NY 10004, U.S.A.
The forces of light shall overcome the forces of darkness.
The topic of design patterns sounds dry, academically dull, and, in all honesty, done to death in almost every programming language imaginable – including programming languages such as JavaScript that aren’t even properly object-oriented programming (OOP)! So why another book on it? I know that if you’re reading this, you probably have a limited amount of time to decide whether this book is worth the investment.
I decided to write this book to fill a gap left by the lack of in-depth patterns books in the .NET space. Plenty of books have been written over the years, but few have attempted to research all the ways in which modern C# and F# language features can be used to implement design patterns and to present corresponding examples. Having just completed a similar body of work for C++,1 I thought it fitting to replicate the process with .NET.
Now, on to design patterns – the original Design Patterns book2 was published with examples in C++ and Smalltalk, and since then, plenty of programming languages have incorporated certain design patterns directly into the language. For example, C# directly incorporated the Observer pattern with its built-in support for events (and the corresponding event keyword).
Design patterns are also a fun investigation of how a problem can be solved in many different ways, with varying degrees of technical sophistication and different sorts of trade-offs. Some patterns are more or less essential and unavoidable, whereas other patterns are more of a scientific curiosity (but nevertheless will be discussed in this book, since I’m a completionist).
You should be aware that comprehensive solutions to certain problems often result in overengineering, or the creation of structures and mechanisms that are far more complicated than is necessary for most typical scenarios. Although overengineering is a lot of fun (hey, you get to fully solve the problem and impress your co-workers), it’s often not feasible due to time/cost/complexity constraints.
This book is designed to be a modern-day update to the classic GoF book, targeting specifically the C# and F# programming languages. My focus is primarily on C# and the object-oriented paradigm, but I thought it fair to extend the book in order to cover some aspects of functional programming and the F# programming language.
The goal of this book is to investigate how we can apply the latest versions of C# and F# to the implementation of classic design patterns. At the same time, it’s also an attempt to flesh out any new patterns and approaches that could be useful to .NET developers.
Finally, in some places, this book is quite simply a technology demo for C# and F#, showcasing how some of the latest features (e.g., default interface methods) make difficult problems a lot easier to solve.
I use public fields. This is not a coding recommendation, but rather an attempt to save you time. In the real world, more thought should be given to proper encapsulation, and in most cases, you probably want to use properties instead.
I often allow too much mutability either by not using readonly or by exposing structures in such a way that their modification can cause threading concerns. We cover concurrency issues for a few select patterns, but I haven’t focused on each one individually.
I don’t do any sort of parameter validation or exception handling, again to save some space. Some very clever validation can be done using C# 8 pattern matching, but this doesn’t have much to do with design patterns.
You should be aware that most of the examples leverage the latest version of C# and generally use the latest C# language features that are available to developers. For example, I use dynamic pattern matching and expression-bodied members liberally.
At certain points in time, I will be referencing other programming languages such as C++ or Kotlin. It’s sometimes interesting to note how designers of other languages have implemented a particular feature. C# is no stranger to borrowing generally available ideas from other languages, so I will mention those when we come to them.
As I write this book, the streets outside are almost empty. Shops are closed, cars are parked, public transport is rare and empty too. Life is almost at a standstill as the country endures its first “nonworking month,” a curious occurrence that one (hopefully) only encounters once in a lifetime. The reason for this is, of course, the COVID-19 pandemic that will go down in the history books. We use the phrase ”stop the world” a lot when talking about the Garbage Collector, but this pandemic is a real “stop the world” event.
Of course, it’s not the first. In fact, there’s a pattern there too: a virus emerges, we pay little heed until it’s spreading around the globe. Its exact nature is different in time, but the mechanisms for dealing with it remain the same: we try to stop it from spreading and look for a cure. Only this time around it seems to have really caught us off guard, and now the whole world is suffering.
What’s the moral of the story? Pattern recognition is critical for our survival. Just as the hunters and gatherers needed to recognize predators from prey and distinguish between edible and poisonous plants, so we learn to recognize common engineering problems – good and bad – and try to be ready for when the need arises.
Design patterns are, for me, a subject of continuous research. Even though the core set of patterns remains more or less unchanged (though I did include a new one, Value Object, in this edition), the exact implementations keep varying as new framework and language features are introduced. With C#, the language has recently made an effort to focus on conciseness: getting more done with less. On the other hand, features such as Source Generators also simplify some of the approaches where code repetition is inevitable. Sadly, we’ve not yet reached the stage where we have a fully functioning metaprogramming system, so we have to make do with what’s essentially plain-text code generation.
This edition also includes a lot of new material related to pattern interactions. Normally, when using patterns, you’re likely to use more than one anyway, and sometimes these patterns interact in weird and wonderful ways. Sometimes it’s difficult to determine exactly what pattern is represented by a particular code because it seems to be covering so many at once. I’ve made explicit in the names of sections which patterns are involved in an interaction.
Patterns are a fun topic to experiment with and delve into those “what if?” questions regarding how an implementation can be improved – whether in terms of maintainability, testability, thread safety, or some other criterion. On the other hand, comprehensive solutions often result in overengineering, which can weigh down implementations and make them more difficult to understand and maintain. I encourage you to consider carefully how much engineering embedded into patterns you actually need for your purposes. Do not be afraid to cherry-pick, experiment, and adjust things to your needs.
Oh, and if you find some interesting approach that this book does not cover, be sure to let me know!
18.217.8.82