Software Design Patterns and Principles
From Gang of Four to SOLID to TDD and BDD - assembling your code properly will help you now and into the future.
Lesson 1: Welcome!
Hello and welcome! In this video course we're going to get deep, deep into the weeds of programming principles, patterns and practices. My goal for you is that you learn enough to build better programs and ship things faster! More importantly, however, is that you build something that can be maintained easily over time.
Lesson 1: Constructor
Most OO languages have a built-in way of creating an in- stance of a class and it typically involves the keyword new. I will assume you know how this works if you’ve written code.
The Factory pattern can only do so much until it becomes too convoluted. This usually happens with very complex objects. Many developers consider this a “code smell” (when you find yourself needing it, it means there’s a simpler way). Overly complex objects are ripe for bugs and, typically, means you’ve probably over-thought your solution.
Premium Method Chaining
Instead of calling stringList.Add("...") or using a String- Builder directly, you can encapsulate what you’re doing into a class that uses a fluent interface, otherwise known as Method Chaining.
Lesson 5: Singleton
A Singleton is a class that only allows one instance of itself. It’s not an easy thing to do correctly and many blog posts have been written about the perils of Singletons and threading or multiple processes.
Lesson 1: Adapter
The Adapter Pattern is all about making one interface work with another. You see them used often with data ac- cess tools (ORMs) where the abstracted query interface needs to work against different databases, such as PostgreSQL, SQL Server, etc.
The Bridge Pattern is quite subtle and tends to look a lot like the Adapter Pattern, but it’s one step up the abstraction curve. You use the Bridge Pattern when your abstraction gets complicated enough that you need to split things out.
The Composite Pattern deals with parent-child relationships that are composed to create a whole object. They can grow and shrink dynamically, and child objects can move between parents.
In the initial versions of GroovyQuery we decided it would be very useful to introspect our database whenever a write needed to happen (insert or update query). We did this be- cause knowing more about each table (data types, primary key fields, column names, and default values) would be extremely helpful in crafting up a very usable API.
Lesson 1: Chain of Responsibility
We’ve decided to implement validations for our User and must orchestrate a bit of an approval chain. We can use the Chain of Responsibility Pattern for this, which is focused on moving data through a set of handlers. There is a better way! Moving objects through a process chain can be subject to many high-level patterns that are, frankly, much better than this one. I’m showing you this example because you should know the pattern – but when it comes to validations there are better ways to do this.
The Command Pattern formalizes requests from one API to the next. Our data access tool, GroovyQuery, is all about writing and reading data from the database. It does this by creating SQL statements that our adapter then executes. We could do this by passing in a SQL string and a list of parameters – or we could formalize it into a command.
We want to formalize our document storage capabilities, however adding methods and abstractions to our GroovyQuery will make the API more complex, which goes against some programming principles we’ll discuss in a later chapter. In short: simplicity is our goal. We want our class abstractions to do one thing and to do it well.
The State Pattern changes an object’s behavior based on some internal state. Often this is done by creating formalized state classes. You often hear an implementation of this pattern called a “State Machine”, which is a fascinatingly complex way of handling process flow.
The Strategy Pattern is a way to encapsulate “doing a thing” and applying that thing as needed. Code is the easiest way to explain this pattern, as it’s quite simple and useful. Our document query capability is working well, but it turns out that SQL Server has excellent support for XML, and some users have asked that we support that along with JSON storage. We can do this using the Strategy Pattern.
Premium In The Real World
Many of you will likely notice that I left a few patterns out of the above list – namely the Visitor Pattern, Memento, Template, etc. These are useful patterns to know about, but their use is rare. Now that you know the theory behind the Gang of Four patterns - let's get into when they're actually useful.
Lesson 1: Coupling and Cohesion
You’ve likely heard these terms before, they’re thrown around a lot and have straightforward definitions. Cohesion applies to how well you’ve thought out the concepts (and concerns, for that matter) of your application. Coupling is the opposite of cohesion. When you couple two or more things, their separate notion becomes one. You want high cohesion, low coupling. Your classes and modules should make sense for isolating ideas, and not rely on each other to exist. This is the goal, at least.
Premium Separation of Concerns
Separation of Concerns is about slicing up aspects of your application, so they don’t overlap. These are typically thought of (by developers) as horizontal concerns (they apply to the application as a whole): such as user interface, database, and business logic. The term can equally (and confusingly) be applied to more abstract ideas, such as authentication, logging and cryptography. Finally, there are vertical concerns, which deal with more business-focused functionality such as Content Management, Reporting, and Membership.
Premium YAGNI and DRY
I remember when I started learning Ruby. I loved the simplicity of the language as well as its dynamic design which, I know, many people dislike. You had to have some rigor and much care when building programs with Ruby because you didn’t have a compiler and static type checking. This was freeing, and it was also a little scary.
Premium Tell, Don't Ask
Another Rubyism that I quite like came from Ruby’s inspiration: Smalltalk. Whenever you invoke a method on a Ruby class you send it a message. You tell that instance that you need it to do something, or that you need some data back of some kind.
Premium Law of Demeter
The Law of Demeter (LoD, or “Deep Dotting”) is an off- shoot of loose coupling. In short: you shouldn’t have to “reach through” one object to get to another. This can be further nuanced to mean you shouldn’t have to reach deeply into one object to do the thing you need to do.
Premium Test-driven Design
I am, strictly speaking, not a practitioner of Test-driven Design (TDD) or Behavior-driven Design (BDD). I know what these things are and I feel why they are useful. I also know that people like to argue about what they think it is and what they think it is not.
Premium Behavior-driven Design
Behavior-driven Development (BDD) is basically the same process as TDD, but you have a specific focus: behavior of the application. It’s a subtle shift, but an important one.
Lesson 1: A Change In Thinking
Some people love it and claim it’s the only way to write software. Others see it as a fad and roll their eyes. Let’s come away from those extremes. Functional Programming is based on a foundational computational model called Lambda Calculus, which all programming languages (OO or FP) are based on . It’s not magical, nor is it something you should ignore because you’re amazing. It is simply something you need to understand.
Lesson 2: Immutability
The first word that comes to mind whenever you hear “functional programming” is usually “immutable”. As I am sure you know, it means “not changeable” in plain English, but how does that translate to programming? Moreover: who cares?
It takes a while to get into the functional groove and if you're new to FP then you might be wondering why all of this matters to any one! You're about to find out because everything we do in FP is all about transforming data.
Premium Purity and Side Effects
There are two terms that you often hear in discussions about functional programming: purity and side effects. Both terms stem from the idea of immutability. The whole notion of interacting with a system outside the scope of the function you’re in is called a “side effect” - something that happens as a result of your function being invoked. Working with a database, for instance, is referred to as a “necessary side effect” because you’re changing the state of something outside the scope of your function.
Functions, functions, functions. They’re everywhere! Organizing a program full of them can feel overwhelming, especially when you’re just starting out with functional programming. Let’s look at one of the very first practices you’ll want to take advantage of: currying. Currying is the act of using smaller, single arity functions in a chain rather than a larger function with multiple/complex arguments.
Premium Functors and Monads
Well here we go - let's see if we can tackle this extremely difficult subject in a simple, human way! By now you're hopefully seeing that we can wrap data with a little more structure as we push it through a transformation... and off we go...
Premium Thanks and Farewell!
Of all the things you learn as you go on in your programming life - the foundational patterns, principles and concepts are going to be the main thing you come back to time and time again. Good luck!