Have you ever wondered how to interact with code written by other programmers without intruding on their code?
There is something called metaprogramming. Thanks to it you can achieve it.
Two objects will help you: Proxy and Reflect. Today you will learn how, when, and where you can use them to master a powerful technique.
Metaprogramming is not invented by Meta company 🤡.
To start talking about metaprogramming, you need to answer the question: what is data and code? Every programming language has both of these but what do they mean, exactly?
Data can be for example:
Code is an expression - an instruction to be executed on this data. It can be: const result = 1 + (2 * 3).
At first glance, you can say that it is a mathematical expression, and this is indeed the case if we are talking about the field of mathematics. However, according to the presented approach, we will consider it in two ways:
by data: '1 + (2 * 3)' - is a string!
by code: '1 + (2 * 3)' - is it code snippet? Then it is 7!
From this it follows that the same representation of an expression can have different interpretations. This approach is what metaprogramming is all about.
Knowing this is a powerful tool for building data and expressions. It allows us to create functions that multiply data which means we can write programs that write programs. It is a certain level of abstraction.
To illustrate this more easily, let's discuss it with this example:
According to the data it is a plain text, but according to the code it is something to be interpreted as an instruction, so:
Congratulations, you have just successfully interpreted a piece of code through metaprogramming!
2. Proxy pattern explained
A proxy is a structural design pattern that allows you to create a proxy object in place of another object. A proxy is a kind of guardian that protects access to the original object, or changes its behavior.
How do you translate this into a simple example from life?... Cash! Cash is the physical representation of the capital placed in your bank account. But what about it? Have you ever used a credit card?
Well, think about it. Credit card is an actually proxy for your cash in the bank. You don't need to physically have paper money with you to buy something. Both of these entities implement the same interface (you can perform an action with them - pay). At the same time, you are actually operating an object that protects another object (in the bank).
Let's start with a simple example for reference:
As you can see, we have overwritten the get mechanism for reading the properties for the ProtectedObject without interfering with its implementation. Now reading a property separated by the symbol "_", you are able to transform several properties into a string of characters.
Even more can be achieved by overwriting set behavior. Just look at this:
We added validation which makes sense. Who normally tries to add something to an object with a certain shape?
When does a proxy make sense? The proxy should totally replace the target object everywhere. No one should ever reference the target object after it got proxied. Otherwise it’s easy to mess up.
4. Proxy use cases
Logging access to an object:
Custom error handling/validation:
Protect the object field:
5. What is Reflection?
Reflection is a closely related concept to object-oriented programming. Among other things, it is used to check the properties of a class/object/method. This technique allows building more dynamic functionality that is native to a particular programming language.
Languages that use this functionality are able to modify their own environment. So the use of reflection should be thoughtful and used only in specific cases to avoid messy effects.
So which approach should I use? In my opinion: both.
Proxy provides us with a number of options for changing an object's behavior during particular actions, while Reflect gives us access to their default behavior.
It is a modified example "Protect the object field":
Simpler? Clearer? Without an additional condition? Of course it is.
Besides, the Reflect object provides us with a lot of functionality, which can be categorized as "syntax sugar".
7. Reflect use cases
Need to find out if an object has a field?
Need to return all the keys of an object?
Want to add a new field? No problem!
I hope you now understand Proxy and Reflect 🧑💻. I must say, they are tricky, yet useful and powerful.
Next time when you see an ugly, old legacy code remember that you have these API's and there may be a good use case for them.
Feel free to contact me if you have any questions/proposals. Have a nice day and good health!