
Most of the literature devoted to patterns in OOP (object-oriented programming) is usually explained by examples with the code itself. And this is the right approach, since OOP patterns are already intended by default for people who know what programming is and the essence of OOP. However, it is sometimes necessary to be interested in this topic by people who do not understand anything at all, for example, “non-programmers” or simply beginners “computer scientists”. It is for this purpose that this material was prepared, which is designed to explain to a person of any level of knowledge what the OOP pattern is and, perhaps, will attract new “adepts” to the ranks of programmers, because programming is actually very interesting.
The article is intended exclusively for beginners, so that the "old-timers" will not learn anything new for themselves. The main article describes the known patterns from the book Object-Oriented Programming Techniques. Design patterns. ”, But more popular and simple language.
What is a pattern in OOP at all?
Pattern (from the English. Pattern) - a sample pattern.
Imagine that you want to make a new car, but you have never done it. How many wheels and why will you design for it? Now you probably already say that 4, but why not 3, 5, 10, 20? Because the practice of using it has already been found that ordinary cars are best done on 4 wheels - this is a design pattern shaped by time. It is the same approach that patterns in OOP serve and you will not encounter them in development until you need to “make a car”. However, sometimes it happens that you create a "tricycle", and only then, having filled several cones with its stability and unsuccessful fit into the track on the road, you will learn that there is a "car" pattern that would greatly simplify your life, you know about it before .
Note:Patterns are not tied to any particular programming language. This is just an approach to designing something. If you look deeper, then many OOP patterns were created based on real life situations in designing quite tangible objects of our world. It is on such metaphors and descriptions that the further presentation will be constructed.
')
Generating patterns
Patterns that create new objects, or allow you to access existing ones. That is, those templates by which you can create a new car and how best to do it.
Singleton
One of the most famous and perhaps the most controversial patterns.
Imagine that a city needs to organize communication between residents. On the one hand, we can connect all residents among themselves by stretching telephone line cables between them, but I suppose you understand how wrong such a system is. For example, how costly it will be to add another resident in connection (to stretch one more line to each resident). To avoid this, we create a telephone exchange, which will be our “loner”. She is alone, always, and if someone needs to contact someone, then she can do it through this telephone exchange, because everyone only addresses her. Accordingly, to add a new resident, you will only need to change the records at the telephone exchange itself. Once having created a telephone station, everyone can use it and only one, in turn, this station remembers everything that happened to it since its inception and everyone can use this information, even if it just arrived in the city.
The main point of “loner” is that when you say “I need a telephone station,” you would be told “It has already been built there”, and not “Let's do it again”. "Single mother" is always alone.
Note:Despite the ease of use of this pattern, it is one of the most controversial in development and it is recommended to use it only if there is no other solution, because it creates considerable difficulties in testing the code, but this is a separate topic.
Registry (registry, journal entries)
As the name suggests, this pattern is designed to store the records that are placed in it and, accordingly, the return of these records (by name) if they are required. In the example of a telephone exchange, it is the registry in relation to the telephone numbers of residents.
Patterns of "loner" and "roster" are constantly found in our everyday life. For example, accounting in a firm is a “loner”, because it is always alone and remembers what happened to it since its inception. The firm does not create every time a new accounting department when it needs to issue a salary. In turn, accounting is also a “registry”, because it contains records of each employee of the company.
Note:The “registry” is often a “loner”, but this should not always be the case. For example, we can start several journals in accounting, in one employees from "A" to "M", in the other from "H" to "I". Each such log will be a “registry”, but not a “loner”, because there are already 2 logs. Although the “registry” is often used for storing “singles”.
The “registry” pattern itself is not a “generating pattern” in the full sense of the term, but it is convenient to consider it in conjunction with them.
Multiton (lone pool)
As is clear from the name of the pattern, it is in its essence a “registry” containing several “singles”, each of which has its own “name” by which it can be accessed.
Object pool
By analogy with the “pool of singles”, this pattern also allows you to store ready-made objects, but they do not have to be “singles”.
Factory (factory)
The essence of the pattern is almost completely described by its name. When you need to receive some objects, such as juice packs, you absolutely do not need to know how they are made at the factory. You just say “make me an orange juice bag” and the “factory” returns you the required package. How? All this is decided by the factory itself, for example, it “copies” an already existing standard. The main purpose of the “factory” is to change the process of “appearance” of a juice package, if necessary, and the consumer himself doesn’t need to say anything about it, so that it requests it as before.
As a rule, one factory is engaged in the “production” of only one kind of “products”. It is not recommended to create a “juice factory”, taking into account the production of automobile tires. As in life, the “factory” pattern is often created by a “loner”.
Builder
This pattern is very closely intertwined with the “factory” pattern. The main difference is that the “builder” inside himself, as a rule, contains all the complex operations of creating an object (juice package). You say “I want juice”, and the builder launches a whole chain of different operations (creating a package, printing images on it, filling it with juice, taking into account how many packages were created, etc.). If you need another juice, for example, pineapple, you will also say only what you need, and the “builder” will already take care of everything else (some processes will repeat, some will redo, etc.). In turn, the processes in the “builder” can be easily changed (for example, changing the pattern on the package), but the consumer of the juice does not need to know this, he will also easily receive the package of juice he needs by the same request.
Note:To better understand the difference between a factory and a builder, you can use the following metaphor.
“Factory” is a vending machine for drinks, it already has everything ready (or “it remains to warm up”), and you just say what you need (press the button). “Builder” is a plant that produces these drinks and contains all complex operations and can collect complex objects from more simple ones (packaging, label, water, flavors, etc.) depending on the request.
Prototype (prototype)
This pattern is somewhat reminiscent of a “factory”; it also serves to create objects, but with a slightly different approach. Imagine that you have an empty package (from under the juice), and you need a full one with orange juice. You “say” to the package “I want an orange juice package”, he in turn creates his own copy and fills it with the juice you asked for. A bit of a “fabulous example”, but in programming it often happens. In this case, an empty package is a “prototype”, and depending on what you need, it creates on the basis of the objects you need (juice packages).
Cloning does not necessarily have to be done on the “package” itself, it can be some other “object”, the main thing is that this “prototype” allows you to receive its copies.
Factory method (factory method)
This pattern is quite difficult to explain in metaphors, but still try.
The key difficulty in explaining this pattern is that it is a “method”, so the method metaphor will be used as an action, that is, for example, the word “I want!”. Accordingly, the pattern describes how this “I want!” Should be executed.
Suppose your factory produces bags with different juices. Theoretically, we can make our own production line for each type of juice, but this is not effective. It is more convenient to make one line for the production of base packages, and to introduce a division only at the stage of pouring juice, which we can define simply by the name of the juice. But where to get the name?
To do this, we create a basic department for the production of base packages and warn all sub-departments that they must produce the right juice package for a simple “I want!” (I.e., each sub-department must implement the “factory method” pattern). Therefore, each sub-department manages only its own type of juice and responds to the word "I want!".
Thus, if we need an orange juice package, we will simply say to the orange juice production department “I want it!”, And he in turn will tell the main juice packaging department, “Make your regular package and here’s the juice that you need to pour ".
Note:As you may have noticed, the “factory method” is the basis for the “factory”, the “builder” and the “prototype”. In development, this is often the case, first they implement the factory method, and as the code becomes more complex, they choose what to convert it to, which of the above patterns. When using the “factory method”, each object is itself a “factory”.
Lazy initialization (delayed initialization)
Sometimes you need to have something at hand, just in case, but you don’t always want to make every effort to get / create it. For such cases, the “pending initialization” pattern is used. Suppose you work in accounting and for each employee you must prepare a “payout report”. You can do this report on all employees at the beginning of each month, but some reports may not be needed, and then most likely you will apply “deferred initialization”, that is, you will prepare this report only when it is requested by the boss (superior) However, the authorities can at any time say that he already has this report, but he is ready or not, he does not know and should not know. As you already understood, this pattern is used to optimize resources.
Dependency injection (dependency injection)
The introduction of dependency allows you to shift part of the responsibility for some functionality to other objects. For example, if we need to hire new personnel, we can not create our personnel department, but introduce dependence on the recruiting company, which, in turn, at our first demand “we need a person”, will either work as human resources department or find another company (using the service locator) that will provide these services.
“Dependency injection” allows shifting and interchanging individual parts of a company without losing the overall functionality.
Service Locator
by
VolCh“Service Locator” is a method for implementing “dependency injection”. It returns different types of objects (companies) depending on the initialization code. Let the task be to deliver our juice package, created by the builder, the factory or something else, where the buyer wanted. We ask the locator “give us a delivery service”, and he connects us to the delivery service by the phone number that the director gave him (because they
receive a rollback they give us a discount as regular customers), and we are already asking the service to deliver the juice to the right address. Today, one service, and tomorrow may be another. It doesn’t matter to us what kind of service it is, the director makes the decision and informs the service locator about it, all we need to know is that they can deliver what we tell them where we say, that is, the services implement the “Deliver <item> interface to <address > ".
Structuring patterns
These patterns help to bring order and teach different objects to more correctly interact with each other.
Adapter or wrapper (adapter, wrapper)
This pattern fully corresponds to its name. To make the "Soviet" plug work through the euro socket, an adapter is required. This is exactly what the “adapter” does; it serves as an intermediate object between two others that cannot work directly with each other.
Bridge
Imagine a situation where you need to work on different cars, but getting into a new car you already want to know how to drive it. In this way, you encounter a “bridge” pattern. On the one hand, you have a lot of different cars (different models and brands), but among all of them there is a general abstraction (interface) in the form of steering, pedals, gearboxes and so on. Thus, we define, as it were, the rules for the manufacture of automobiles according to which we can create any of their types, but by keeping the general rules of interaction with them, we can control each of them equally. The “bridge” in this case is a pair of two “objects”: a specific car and the rules of interaction with this (and any other) car.
Composite (linker)
Quite an interesting pattern whose essence is to minimize differences in the management of both groups of objects and individual objects. For example, consider the management of soldiers in the ranks. There is a parade regulations, which determines how to control the system, and according to this charter it does not matter who the order is given (for example, “march step”) to one soldier or a whole platoon. Accordingly, the charter (if it is considered purely as a “linker” pattern) cannot include a command that only one soldier can execute, but the group cannot execute, or vice versa.
Decorator (decorator, designer)
As the name implies, this pattern is most often used to expand the original object to the desired form. For example, we can conditionally consider a “decorator” of a person with a brush and red paint. Thus, whatever object (or a certain type of objects) we pass into the hands of the “decorator”, we will receive red objects at the output.
Facade
The facade pattern is used to make complex things simple. Take for example the car. Imagine if the car was driving a little differently: press one button to supply power from the battery, another to supply power to the injector, the third to turn on the generator, the fourth to light the light on the panel and so on. All this would be very difficult. To do this, such complex sets of actions are replaced by more simple and complex ones like “turn the ignition key”. In this case, turning the ignition key will be the very “front” for the entire abundance of internal actions of the car.
Front controller (single entry point)
If we draw analogies with the real world, the “single point of entry” is what you are currently reading this article (for example, a browser). It serves as a “single point of entry” for the entire Internet space. That is, you use one interface (browser) to gain access to different objects of a large system (sites on the Internet). This pattern is generally very similar to the “facade”.
Flyweight
The best example (which I was able to find in real life) for a metaphorical comparison of the pattern “fitter” is a theatrical production. Imagine that we need to put a play. However, according to the scenario, several dozens of people are involved in this play, who in essence perform the same actions, for example, they participate in crowd scenes of different scenes at different intervals, but there are still some differences between them (for example, costumes). It would cost us a lot of money to hire an individual actor for each role, so we use the “fitter” pattern. We will create all the costumes we need, but for each crowd we will dress a small group of actors in the costumes required for this scene. As a result, we have the opportunity at the cost of small resources to create the appearance of managing a large number of seemingly different objects.
Proxy or surrogate (proxy, deputy, surrogate)
This pattern allows you to create any special mechanisms for access to the object, which is most often aimed at improving the performance of individual parts of the program. In real life, you can give the following example: employees of one of the company's divisions regularly need to receive information about how many bookkeeping plans to pay salaries. On the one hand, each of them can individually and regularly travel to the accounting department to clarify this issue (I think this situation is often found in many organizations). On the other hand, when the planned date is approached, the division can choose one person who will find out this information from the accounting department, and later everyone in the division can find out this information from him (which is much faster). Exactly this person will be the realized “proxy” pattern, which will provide a special mechanism for accessing information from the accounting department.
Behavior patterns
This group of patterns allows you to structure approaches to processing the behavior and interaction of objects. Simply put, how should the processes in which there are several options for the occurrence of events.
Chain of responsibility
The simplest example of a chain of responsibilities is the receipt of any official document. For example, you need to get a certificate from an account from a bank. One way or another, you should receive this certificate, but who exactly should give it to you is not yet clear. You come to the local branch of the bank, you are told that “we are now busy, go to another branch”, then you go to another, they answer “we don’t do it”, you go to the regional branch and get the necessary information there. Thus, the pattern implements a “chain of duties” which individual objects (bank branches) should process your request. Accordingly, your request can be processed in the first branch, or in several, depending on the request itself and processing objects.
Command or action (command, action)
The “team” pattern is very similar in real life to the light switch buttons in our apartments and houses. Each switch in its essence makes one simple action - it disconnects or connects two wires, however, what is behind the wires to the switch is not known. What is connected, it will happen. The “team” pattern also works in the same way. It only defines the general rules for objects (devices), in the form of a connection of two wires for executing a command, and what exactly is executed is determined by the device itself (object).
Thus we can turn on one type of switches as the light in the room, and the vacuum cleaner.
Interpreter (interpreter)
You can compare this pattern with the way you put frequently used actions into an abbreviated set of words, so that the “interpreter” itself would then turn this set into more complex meaningful actions. In fact, every person is constantly an "interpreter". Want to conduct a life experiment? If someone from your family (husband, wife, child) comes out of the house, tell him a simple set of words “Liter of milk, half white, 200 grams of cottage cheese”. In fact, you didn’t say anything special, just listed a set of products, but there is a great chance that the “interpreter” transmits it to the team “go on the way to the grocery store and buy the following ... and bring it home”. The “interpreter” pattern is designed to reduce frequently performed actions to a shorter description of them.
Iterator (iterator, pointer)
Everyone remembers the school "for the first second pay off!"? It was at this moment that the rank of your class was the implementation of the “iterator” pattern, although in programming it is certainly a more functional concept, but the essence is about the same. The iterator provides rules for accessing a list of any objects, regardless of what the objects are. That is, it does not matter which class is built and from which students, there should be general rules for counting and circulation as each student on the list, like "13th, fail." Often the “iterator” pattern is used to access the “registry”. Links that you see on many sites to navigate through the pages, like "next", "previous", "to the top", etc. in essence, they are also access to the "iterator" which is responsible for the pages of the site.
Mediator (Mediator)
Recall an example from the loner pattern. So the telephone exchange in that example was in fact also a “intermediary” pattern, that is, it ensured the interaction of a group of objects without the need to ensure that each object communicates with each other.
However, the additional responsibility of this “pattern” is also the management of this group through an “intermediary”. That is, if we take an example with the army system, the mediator will be the squad leader, that is, we do not need to interact with each soldier individually, it is enough to give orders only to the squad leader, and he will decide what actions should be performed within his squad.
Memento (keeper)
Never asked a friend with a cell phone to remember (write to yourself) that number, which they dictate to you by phone, because you cannot remember it yourself (the phone is busy)? At this point, your friend was implementing the keeper pattern. It serves for those cases when any object needs to save its state (state of knowledge of the number) in another object (your friend), and if necessary restore it later (ask the friend for the number and thereby restore the state when you knew it). It is also appropriate analogue with how the games work saving. The “save” file will be the very same “keeper” pattern.
Observer or Listener (observer, listener)
A very common pattern in real life. For example, if you subscribe to any email (or SMS) distribution, then your email (or cell phone number) starts to implement the “observer" pattern. As soon as you subscribe to an event (for example, a new article or message), everyone who subscribes to this event (observers) will be sent a notification, and they, in turn, can choose how to respond to this message.
Blackboard (bulletin board)
This pattern serves to ensure the interaction between a large number of objects. It is an extension of the “observer” pattern and allows you to centrally serve both “observers” and “event creators”. In the analogy of a subscription to email notifications, this will be the subscription site itself, which serves many subscribers and those who create information (messages) for them.
Servant
As the name suggests, this pattern is used to provide a group of objects with some common functionality. For example, a telephone exchange is a “servant” pattern for residents of a city if it comes to how to find out the exact time (dial the number 100).
State
In real life, each person can come in different states. In the same way, sometimes it is required that the objects in the program behave differently depending on any of their internal states. By analogy with real life, for example, you can give the following example:
If you are tired of the phrase “Go to the store”, you will issue “I will not go”, if you need to go to the store (for a beer?), Then you will give out to “Go to the store” “Already running!”. The person (object) is the same, and the behavior is different. It is for these purposes that the “state” pattern is used.
Strategy
Used to select different ways to get results. Recall the example of obtaining rights. A person who will implement the “strategy” pattern will act as follows: you say “I want rights, little money” in return, you will get rights after a long time and with a great waste of resources. If you tell him “I want rights, a lot of money,” then you will get rights very quickly. What exactly this person did, you have no idea, but you specify the initial conditions, and he himself decides how to behave (he chooses the strategy himself).
Accordingly, various “behaviors” are stored within the “strategy”, and in order to choose, it needs certain parameters, in this case, the amount of money. How the “strategy” itself is arranged and what algorithms inside it you actually need to know.Specification (specification, definition)
The specification specification allows us to describe whether this object is suitable for us based on any criteria. For example, we have several containers for loading onto a ship. However, in order to determine whether to load a container or not on a particular vessel, we need to choose a method to determine this. The implementation of such a method is the “specification” pattern. In the simplest case, for each container we can determine in the “specification” pattern whether the country of destination of the ship matches the country of destination of the container. Accordingly, we once introduce the rule “compare two countries of destination” and apply it to all containers for inspection.Subsumption (categorization)
This pattern is a direct follower of the "specification" pattern. It allows you to categorize objects based on any conditions. Accordingly, by analogy with the example of ships and containers, this is a categorization according to which containers are sent to which countries.Visitor (visitor)
This pattern can be compared with the examination at the hospital. However, the “visitor” in terms of patterns here will be the doctors themselves. To make it clearer: we have a patient who needs to be examined and treated, but since different doctors are responsible for different examinations, we simply send doctors to the patient as “visitors”. The rule of interaction for the patient is very simple “invite a doctor (visitor) to do his job,” and the doctor (“visitor”) comes, examines and does everything necessary. Thus, following simple rules, doctors can be used for different patients using the same algorithms. As already mentioned, the “visitor” pattern in this case is a doctor who can serve different objects (patients) in the same way if he is called.Single-serving visitor (one-time visitor)
It is a special case of using the pattern "visitor". If in the case of the usual “visitor” we have a doctor whom we can send to different patients (and several times if desired), then in this pattern we can draw an analogy that we hire a doctor, send him to one patient and after the examination firedHierarchical visitor (hierarchical visitor)
The same “visitor” pattern, but in this case it is sent to not only one patient, but to the whole hospital and bypasses all patients there.Conclusion
That's all the main patterns that I wanted to describe in this article. As you can see, they all have a lot in common with real life and allow you to make the code as simple as it is to read and understand, like what we see in real life. Programming is not an “alien language” (and programmers themselves are quite earthly creatures), it’s just another form of interaction and description of the existing world.A lot of articles and books on the Internet have been written about how to directly apply these patterns in practice, they are very easy to find. However, I hope the information provided in this article will allow you to quickly navigate if suddenly a familiar pattern appears on the horizon of the code.I hope you found this material useful for you and thank you for your attention.UPD:additional materials that may also be interesting on this topic:From Markel userwww.cours.polymtl.ca/inf3700/divers/nonSoftwareExample/patexamples.htmlFrom NikoMThe book Freeman, Freeman, Sierra: Design Patterns