Objective Ways

Design Patterns

Intro

Design pattern as a term is well-known to practically every developer. Among SOLID, DRY, architectures, and other essential things in OOP, we always get familiar with design patterns.

However, I believe it is a tricky topic for many people, including me. The problem is that it is easy in theory, but there are a lot of practical questions which everyone answers differently. Some people say that a pattern may look exactly how it was described in the book (design patterns), while others allow minor changes. Some think the patterns are a part of the fundamental knowledge, the basics everyone should know, some others try to build their code only combining patterns, other people could say that it is an overvalued and even harmful thing because you obfuscate business logic expressed in your code.

And I, as a beginner among such conflicting opinions, did not understand how to trait them the right way. And now, writing this post, I sum up all the opinions by these questions:

  • How to use them properly? Should I memorise them and implement the way they are described in the book? When should I not use patterns?
  • Can I write good code without knowing about design patterns?
  • Why are there disputes like “what exact pattern is used in this piece of code?”? Why can you often classify a single piece of code as one of several design patterns?
  • Why only those patterns? Are there any other ones non mentioned in the book?
  • Are they good or evil, after all?!

I am not here to argue against other people’s approaches; I am here to explain my personal opinion, and how it answers these questions. I hope it will be useful for somebody.

Construction Set

Let me start with a little example. When I was a child, my parents gifted me with a construction set. It was a really great one because it contained a lot of different pieces allowing me to create various things, even with moveable parts; the only limit was my imagination. And there was also a guide with several examples, and one of those examples was a big car with a functioning wheel!

Why had the authors spent money designing and printing that guide? The absence of the guide would not restrict the flexibility and amount of pieces, so, technically, I could put all the pieces together and get the same car even without their advice, just thinking and experimenting. The reason was to show me examples. Examples what I could build using that set, what was possible with all those pieces lying in a carton box; poetically said, to nourish my imagination. They literally told me, “Look what cool results you can get combining those pieces!”

And their plan worked out! Of course, I was pretty excited about this car, and built it, and then used the original scheme to build a slightly different vehicle, and later often reused fragments of this car in other models. The guide helped me understand the potential and power I had while playing with this construction set. Would I do the same without examples? In theory, yes, I had everything to do that. However, it would probably take much more time to realise what beautiful things I could build, and possibly I would get bored soon and focus on other toys before I understand the hidden power of a big pile of coloured plastic pieces.

Back to Design Patterns

Maybe you have already caught the meaning of that analogy. Your programming language is a construction set; objects you create are its pieces; and design patterns are examples what you can get combining objects different ways.

The only difference is, unlike guides for toys, design patterns are more about local solutions or snippets of code which solve a particular problem or achieve a concrete goal (customisation, for instance). However, they still work on the same mission: to show you useful ideas and principles, to show how flexible and powerful your OO language is. Each pattern offers us one or more ideas that can make our code better. Sometimes one principle can be found in several patterns, but it is just because the same principle can help when applied in different contexts, and the authors wanted to show as many possible applications as they could.

Returning to the questions in the beginning, can we write good maintainable code without knowing design patterns at all? Sure. Similarly to that construction set, anyone can formulate all the principles themselves by experimenting and trying various solutions. However, it will take time to figure them out, to try different approaches, see whether they work or not, choose the best option, etc. The authors of the book give us the result of their own experiments, to save our time.

Examples

Let’s look at some principles. I suppose you have read about design patterns already, so there will not be explanation of them, only ideas.

Abstract Factory

This pattern shows one of the most universal techniques in OOP: if you need to do something, create an object that will do that. In the given example, we create an object-factory that takes responsibility of building something for another object. Literally, this is how any object-oriented program is built: code consists of multiple objects each doing its own job and helping others in their needs. It is often better to create a new object for something instead of complicating an existing one.

State

This pattern demonstrates one important thing: the real structure of an object does not have to match its public interface. In the given example, a state looks like a single object with a particular responsibility at an external glance, but it, in fact, only manages multiple objects which do the very job. It helps us think of objects as of black boxes which may look not what they really are.

Visitor

This pattern shows not what objects can do but how they can communicate. In the given example, the main code does not try to guess what to do with each of objects; the code lets those objects choose the best option themselves. In other paradigms, it is often said that things are plain, and we ourselves decide how to manipulate them the right way. For instance, we check the type of a value, and for a number put it in one bucket and for a string put it in another one. In OOP, the only things we have are objects, and these things are pretty smart and know better how to work. This is their main feature — being smart. With this principle in mind, we can keep your code simpler in practically every task.

Usage

So, what to do with design patterns? The most obvious way is, of course, simply using them to solve particular problems. Despite being written many years ago, they still work as expected.

The next step is to start thinking in terms of OOP. It is great when we remember how to implement one or another design pattern, so we can use it in case we encounter a certain problem. However, it is an energy-consuming activity, constantly checking whether our code meets the standards of high-quality code or not. It is much better if our brain produces thoughts based on these standards.

Remember how you learn a foreign language. It is not enough to mechanically memorise words, phrases, and rules. Even knowing a cool phrase, you are unlikely to use it in your speech because it simply will not appear in your mind at the right time! And usually the main goal of learning is to somehow put foreign words into the mind’s internal dictionary and associate each of them with images or emotions to let the brain use them subconsciously, as a foundation of every emotion or thought. The same thing should happen with the object-oriented principles.

For instance, thinking of object’s public interface and its implementation as of separate things. It helps practically in any task we are working on at the moment. Modern programming languages usually mix them together, thus giving the impression that making something public or private is just a little tweak to slightly improve the safety of code. When something is needed outside a class, we can even just make it public by changing its visibility attribute. You do not want something to be visible outside a class — you make it private. The same code, the same types, but with “public” or “private” attribute in its declaration; that is the only difference.

However, it is like two different worlds. Public interfaces are intended to provide other objects with a clear, convenient, easy-to-use, and safe contract (or API); on the contrary, implementations are about getting the job done, usually the most effective possible way. Public interfaces are the stablest parts of code because they are what other objects rely on, whereas implementations are hidden and thus can be very dynamic. Public interfaces require thorough thinking while designing them because any change in future is likely to cost a lot (in terms of time and the amount of changes), whereas implementations may be created just to work somehow, even highly ineffectively, and can easily be improved step by step, not even touching other objects. Finally, public interfaces usually operate in terms of high-level (or domain-specific) entities, whereas implementations often work with low-level things such as numbers, strings, etc.

So, the design patterns demonstrate us that it can be worth making class’ interface different than how it is really implemented. Let’s take an iterator, mentioned before, which looks like you are working with a linear list of objects while it is actually iterating over multiple tricky-structured objects. You, as a user of such the iterator, get a convenient interface and do not clutter your mind thinking how to iterate over all needed things. At the same time, the real code is implemented so as to work efficiently. Keeping this idea in mind, we will be able to use the upsides of all two worlds to our advantage in any task, not only related to iterating over sequences.

More Patterns!

Answering the question “when should I not use patterns?”: I believe this is the question which demonstrates a typical misconception about design patterns. It looks that the asking person separates code into “regular code” and “patterns”. There is just code. Even when we are not thinking about factories and iterators, we write our code with the same language features and principles which are used in design patterns.

Are there any other patterns? Yes, sure. Returning to that construction set, the fixed number of examples given by the guide does not mean that you have a finite number of possible things you can build. There are much more beautiful and grandiose things. The same works with design patterns. The authors make it clear that it is good to experiment and find other patterns.

What is a pattern? A solution of a common problem with a catchy descriptive name. Therefore, even within one project you can find a frequent problem specific for this exact project, invent a good solution, and name it to help other developers be on the same page while talking about the project. And you have just invented your own design pattern!

And the final question, whether they are good or evil. Well... I guess there is no need to answer, the post itself shows my opinion. And I hope I have convinced in that you as well.