Most developers are very skeptical about design patterns. It is very common to hear that patterns are academic nonsense and useless. But anyway it is not true, and patterns can be very pragmatic and highly useful in everyday work.
Main advantages
- Patterns are good abstractions that provide building blocks for system design.
- Patterns take away focus from code reuse and move the focus to knowledge reuse.
- Patterns aren't invented, but rather harvested or distilled. It's about proven solutions.
- Patterns are described in three pieces: the context, the problem, and the solution.
- Patterns are very important part of the language within a development team.
Instead of having to describe each and every design idea in minute detail, it's often enough to say a pattern name and everybody in the team can evaluate whether or not it's a good idea for that particular problem.
- Ability to utilize patterns is a long-lasting skill. Patterns are language-/product-/platform-agnostic.
Also patterns are very much in line with the principles (treat like a guide, not like “the only truth”) of good object-oriented design.
Single Responsibility Principle (SRP)
An item such as a class should just have one responsibility and solve that responsibility well. If a class is responsible both for presentation and data access, that's a good example of a class breaking SRP.
Open-Closed Principle (OCP)
A class should be closed for modification, but open for extension. When you change a class, there is always a risk that you will break something. But if instead of modifying the class you extend it with a sub-class, that's a less risky change.
Liskov Substitution Principle (LSP)
Assume that you have an inheritance hierarchy with Person and Student. Wherever you can use Person, you should also be able to use a Student, because Student is a subclass of Person. At first this might sound like that's always the case automatically, but when you start thinking about reflection (reflection is a technique for being able to programmatically inspect the type of an instance and read and set its properties and fields and call its methods, without knowing about the type beforehand), for example, it's not so obvious anymore. A method that uses reflection for dealing with Person might not expect Student.