Design patterns – design patterns are reusable design solutions to problems or features you might want your code to have. You can have the same design pattern in multiple languages or programs, though their implementation details might be different. But the basic concepts are always the same. The point of design patterns is to solve problems that come up again and again. Design patterns are reusable and can be used in various programs or languages. Two examples of design patterns include singleton and factory.
Singleton design pattern – a class that can only have one instance.
Factory design pattern – an easy way to instantiate objects. A factory might involve instantiating classes that inherit from a superclass. With a factory, the user specifies input, and based on the input, one of many subclasses is chosen. If someone says they want a pet that purrs, a factory would deduce that they want an object of the Cat subclass of the Pet parent class. They didn’t have to specify Cat because the factory figured it out. But the Pet superclass is the parent class for many different subclasses, such as Cat, Dog, and Fish.
Abstract factory design pattern – a factory for factories.
Builder design pattern – a builder simplifies the creation of complex objects. A class that uses the builder design pattern will have a Builder object set it up, rather than the object itself doing everything.
Adapter design pattern – lets two different interfaces work together. Without an adapter, two interfaces might not be compatible.
Composite design pattern – puts multiple things together so that they can all be used as a single entity. Think of how you can zip up a bunch of files in a .zip archive. That’s kind of what a composite design pattern is like.
Decorator design pattern – used for modifying objects at runtime. A decorated object’s modifications will not affect other objects of the same class. If I decorate my desk, it doesn’t change what your desk looks like, because I was only decorating my individual desk object, not the design of all desks.
Visitor design pattern – a visitor lets you perform operations on many different objects of the same kind. The point of a visitor is to remove algorithms from object representations to another class.
Command design pattern – store commands that will be used later. Think of a server and a user in a client/server relationship. One is requesting things (by sending queries) and the other is responding. A command design pattern contains commands to be send from the querier (called an invoker) to the responder (called the receiver, because it receives commands).
Mediator design pattern – used for allowing multiple components of a program to communicate with one another in a mediated way. A waiter or waitress is a mediator between a customer and a cook.
Observer design pattern – an observer observes something called the subject. When the subject changes, the observer takes notice. The observer design pattern can be useful for when you want things to happen after a certain object changes.
Prototype design pattern – a way to make new objects by copying other objects.
Flyweight design pattern – makes it easier to make a lot of objects that are similar to one another. Instead of always creating new objects, you can make things share memory if they’re similar.
Once you start coding, after you’re familiar with classes, objects, data structures, and algorithms, then you might want to learn design patterns more in-depth. There are many other design patterns I didn’t mention here. But it doesn’t make much sense for a beginner to get right into them. There is a well-known book on the topic of design patterns called Design Patterns: Elements of Reusable Object-Oriented Software. I recommend reading it, but only after you have at least a few projects under your belt.
SOLID principles – 5 different design principles. It includes the following:
Single responsibility principle – classes should only have one responsibility each. If you have a superhero class that tries to do everything, you’re doing it wrong.
Open-closed principle – open for extension, but closed for modification. So you can add additional features to a class by making a subclass, but that doesn’t modify the parent class. Extensibility means someone can add extra features they need, but a lack of modification means that people can expect the original thing to behave the same way it did before.
Liskov substitution principle – you should be able to swap out an instance of a parent class for a child class and not have any problems.
Interface segregation principle – just like how programs can benefit from being broken up into smaller microservices, interfaces can benefit from being small and specialized rather than huge and multi-faceted. Let’s say you have an interface with methods A, B, C, D, E, and F. But for a class you’re making, you only need methods A and B. But you’re using an interface that also has C, D, E, and F, which are not things you need in that class. That’s bad. Instead, you should have smaller separate interfaces. Maybe one interface has A and B, another has C and D, and a third one has E and F. That way, you’re not as likely to be putting in stuff that isn’t needed for that particular interface use.
Dependency inversion principle – high level code shouldn’t do low level stuff.
GRASP – General Responsibility Assignment Software Patterns. GRASP is concerned with how you assign responsibility to things within your code. Examples of GRASP patterns include pure fabrication, creator, low coupling, controller, high cohesion, indirection, polymorphism, protected variations, and information expert.
High cohesion pattern – cohesion means how much a class’s stuff is related. If a class is focused on a single responsibility and all of the stuff within the class is related, it’s highly cohesive. Multi-faceted classes are not high cohesion.
Low coupling pattern – low coupling means things aren’t super dependent on each other. This pattern means you should make classes that are relatively independent.
Pure fabrication pattern – if you want to obey high cohesion and low coupling, there can sometimes be situations where it’s hard to figure out where something should go. In that case, you can make an additional class. Let’s say you can’t figure out whether to assign a certain responsibility to class A or B. Maybe instead of putting the responsibility in either one, you can make a third fabricated class called C.
Creator pattern – how an object is used determines where it’s created. If a class needs an object and depends on it in many ways, that class should create that object.
Controller pattern – controllers figure out how to deal with input from the user.
Indirection pattern – an intermediate object is used for low coupling between two different things.
Polymorphism pattern – just like object-oriented polymorphism, the polymorphism pattern allows for different behavior based on inheritance and overriding from a parent class.
Protected variations pattern – making it so that variations in one thing don’t negatively affect other things.
Information expert pattern – if an object knows about something, that’s the object that gets assigned a responsibility that’s related to it.
Antipatterns – design patterns are good kinds of reusable patterns, but antipatterns are the opposite – harmful patterns that come up as a result of bad design. I consider getters and setters in Java to be antipatterns. Some antipatterns are from the language itself, and others are from the bad code you write within it.
Let’s say you’re a painter. You paint walls for a living. If you are continually choosing to paint yourself into a corner, you might be frustrated. Maybe you’re also an inventor and make a jet pack so you can fly out of the corner without stepping on the paint. That is an antipattern because it’s a ridiculous solution to an unnecessary problem. A better alternative would be not to paint yourself into a corner to begin with, rather than trying to come up with a weird fix for it. Just because a jet pack could get you out of that situation doesn’t mean it’s the right choice. Antipatterns are similar to yak shaving and the XY problem.