Design patterns capture proven object-oriented experience,address issues of change in software. Once you know the pattern, a lot of design decisions follow automatically. Each design pattern lets some aspect of system structure vary independently of other aspects, making a system more robust to a change. Each pattern describes a repeatable problem and solution for it. Every object-oriented architecture is full of different patterns.Design Patterns give you a shared vocabulary with other developers and that allows you to say more with less. It also elevates your thinking about architectures by letting you think at the pattern and not the object level.
All patterns could be put in to three categories:
- Creational – concerns the process of object creation
- Structural – addresses a composition of classes or objects (uses inheritance to compose classes and describe ways to assemble objects)
- Behavioral – looks at the ways in which classes or objects interact and distribute responsibility (uses inheritance to describe algorithms and flow of control and how a group of objects cooperate to perform a task that no single object can handle)
Also we could categorize them by application area:
- Class patterns deal with relationships between classes through inheritance (static—fixed at compile-time)
- Object patterns deal with object relationships (changed at run-time)
Patterns help to avoid:
- hard-coded requests
- dependence on hardware and software platform
- dependence on object representations or implementations
- algorithmic dependencies
- tight classes coupling
- extending functionality by subclassing
- inability to alter classes conveniently
Most popular patterns are:
- Abstract Factory
- Factory Method
- Template Method
Let’s start from most common Strategy pattern. Official description says, that it defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
- encapsulate parts that vary
- program to interface, and not to implementation
- favor composition over inheritance
Let’s explain them with vehicle example.
Our abstract class Vehicle has two common methods for all vehicle types – beep() and turnFrontLightsOn(). But different vehicles can have very different movement behaviors. To separate these behaviors we’ll take go() method out of the Vehicle class and put to a new set of classes. These classes implement MoveBehavior interface. Now actual implementations of the movement behaviors are not locked into the Vehicle subclasses. Vehicles delegates their movement behaviors. Instead of inheriting these behaviors, vehicles get their behaviors by being composed with the right behavior object (for example special purpose, futuristic car can ride on wheels, then sale and if there is a need, even start to fly).
Creating systems using composition gives a lot more flexibility. It lets you encapsulate a family of algorithms into their own set of classes. Also it lets you change behavior at run-time, as long as the object you’re composing with implements the correct behavior interface.