J
TL;DRObject Guidance (OO), regardless of language and how much it adheres to theory, can bring the same benefits, being the main ones related to code organization, ease of maintenance and capacity for extension of functionality.You are sure to assume that OO is better for libraries and APIs, since code made for third-party use needs to have a well-defined interface, extensibility and encapsulation, while too much flexibility generates headaches to handle code evolution.However, this paradigm, in itself, does not solve any of the problems and, if misused, can cause both or more damage than help. You don't need OOThat's what you read. You can do everything you do with objects using only procedural programming. A good programming language needs basically 3 components:Basic data structures: vectors, maps, classes, simple types, etc.Control structures: if, for, etc.Code organization structures: subroutines, blocks, functions, methods, etc.Now think about what class is, for example. These are a composition of organized code excerpts (methods), which contain control logic, and a set of data or attributes (the object state) that we call in PHP of $this. Well, you can get the same result by using a set of functions and a shared data structure between them. Who knows how to program a bit in C, for example, knows very well about it.procedural example:$this = criar_estrutura();
fazer_algo($this);
Example OO:$objeto = new Classe();
$objeto->fazer_algo();
The two codes do the same thing, however in the first the state is explicit and needs to be passed on each call to the set of functions that are analogous to the methods of a class.However, as I intend to show ahead, OO brings other benefits and is not a clue or coincidence or a simple fashion that makes it the most used paradigm in the world.You can find good materials here in the SO about OO advantages, being an example https://pt.stackoverflow.com/q/55493/227 .What distinguishes Object OrientationA great differential of OO in relation to the other paradigms is that it goes beyond the abstraction and encapsulation of the data.Sure, nothing's free. With the benefits come the side effects, namely, greater ceremony, complexity and restrictions, plus impacts on memory management and performance (not wanting to say that necessarily a system will stay slow or heavy only by using OO).However, what is seen in general is that the gain can be much higher than the price paid per play within the rules of objects.When you have private attribute encapsulating the state of an object, it can be "called" to have to be accessing everything by methods. In theory (but it has to be very theoretical even), a good programmer does not need to hide their data structures, only take care not to be tempted to stir in what should not.However, if we look at real projects involving real products, we will find that it is illusion to think that humans will simply follow all rules and good practices correctly. In a complex project, it can be humanly impossible to be aware of the functioning of everything. Someone can argue that just study the PHP API to learn how to use the functions, but in real projects you also need to learn the API of the system in particular and other systems with which you integrate, sometimes more complex and disorganized than the language itself (if it is possible :P).Given this chaotic scenario, the restrictions, standards and rules imposed by OO are very welcome in many cases.Abstraction and reuseHow do you reuse code without OO? With functions. And how do you do to add variations? Parameters.The problem is that over time you will need to change the parameters. Not unusual in codes PHP out there find functions with 10 parameters, some optional, others unnecessary. And if you need to remove a parameter, you will have to go out sweeping the code by searching for the calls. An alternative to this is to receive vectors, maps or other generic structures as parameters. This can work well to a certain extent.When using objects, you can different builders and have methods to define specific properties that are usually optional. We will now go to a scenario where you have a routine that processes an entity with various attributes, being 2 of them mandatory and the other optional. Implementation OO could be:$cliente = new Cliente($cpf, $nome);
$cliente->setEndereco($end);
$email->enviar($cliente);
A procedural simplistic version would be:enviar_email_cliente($cpf, $nome, null, null, $end); //demais parâmetros omitidos
A more flexible version:enviar_email_cliente($cpf, $nome, array("endereco" => $end));
Also consider that this system is a large ERP, consisting of modules developed by different teams, and there are hundreds of calls to the routine and none of them are static values as in the example.Now imagine it was necessary to remove the attribute endereco of the entity Cliente and move to another entity, since now customers can have multiple addresses. Now go the question: what happens when you change? In which case will the IDE or the interpreter warn you that the attribute no longer exists? This is a trade-off between flexibility and security. Much flexibility, as it is allowed and even encouraged in PHP, is also a frequent cause of difficult errors to find.EncapsulationI can imagine that in your system you have a handful of variables, some global ones, to exchange information between functions. If you don't, congratulations, but this is not the rule.The problem with shared variables is that you incur the great risk that a piece of code interferes with others inadvertently. Let's go to another example. You want to monitor the time of a very time-consuming routine. A naive procedural approach using a global variable would be:iniciar_timer();
foreach ($planetas as $planeta) {
popular($planeta);
}
echo "Total: " . finalizar_timer();
Well, obviously that works, but it doesn't help much. The routine takes a long time and it is not known which planet we are having more difficulties in popular. We want to know the total time, as well as the individual time.We could improve as follows:$inicio_geral = iniciar_timer();
foreach ($planetas as $planeta) {
$inicio_parcial = iniciar_timer();
popular($planeta);
echo "Planeta $planeta: " . finalizar_timer($inicio_parcial);
echo "Parcial: " . (time() - $inicio_geral);
}
echo "Total: " . finalizar_timer($inicio_geral);
Okay, now we don't have the global state anymore and we can reuse the routine.However, it was found that the process is leaking a lot of memory and if you want to investigate when it occurs. For this, we need to recover the amount of memory allocated before running the routine and the amount of memory after it runs and add this information to a log file.How can we change the routines to do this without messing with the code? Keep in mind that these routines are being used by other people who contribute to the colonization of the galaxy.If we don't want global variables, we can change the return of inicar_timer to return an array containing both time and memory and thus recover both values in finalizar_timer. However, you are potentially breaking the implementation of several people, since now the echo will not print what you expected. All this because smart programmers, knowing you were returning one timestamp, directly accessed the value.Someone can argue that it is possible to correct this simply. For example:$inicio_geral = iniciar_timer();
foreach ($planetas as $planeta) {
$inicio_parcial = iniciar_timer();
popular($planeta);
imprimir_tempo("Planeta $planeta", $inicio_parcial);
imprimir_tempo("Parcial, $inicio_geral);
}
imprimir_tempo("Total", $inicio_geral);
Excellent. Now explain to the world because once again changes have been implemented incompatible with previous versions and now all will have to be using an old version of your code or stop what they are doing looking for all the uses of the affected routines, adjusting and testing everything again.OO does not solve all problems, but in this example (and in several others I see in day to day) a correct encapsulation of the state could have solved the whole problem of beginning. On the other hand, the lack of encapsulation, tend to cause this kind of problem.At that point, OO's restrictions and bureaucracies would have allowed all changes to be made without interruption and without incompatibilities: $geral = new Timer();
foreach ($planetas as $planeta) {
$parcial = iniciar_timer();
popular($planeta);
$parcial->logTime("Planeta $planeta");
$geral->logTime(Parcial);
}
$geral->logTime("Total");
In short: the restrictions imposed by OO increase the reuse because they limit the way the client code can interact with the data.In functional programming, on the other hand, we could still have a method that automatically counts. Example:monitorar_execucao("Total", function() {
foreach ($planetas as $planeta) {
monitorar_execucao("Planeta $planeta", function() {
popular($planeta);
});
}
});
In the above example, start and end logs are done automatically and we can add any desired monitoring mechanisms.And if we want to give a little more flexibility, we can still provide an object as a parameter:monitorar_execucao("Total", function($timer_global) {
foreach ($planetas as $planeta) {
monitorar_execucao("Planeta $planeta", function($timer_parcial) {
popular($planeta);
$timer_global->log("parcial");
});
}
});
Note how we use functional programming and OO together to get a more flexible, safe and consistent API. We can change internal implementation without affecting who uses routine.EntitiesYou probably have some entities that you save in a database.Without object orientation, it is not unusual if you use a set of variables or even a map or array to store such data structures.For example:$carrinho = ["Pão", "Leite"];
Unless your system is too trivial, you must have found some problems while creating routines to recover, insert or change data. Now you forget which attributes a map loads, so you need to pass a lot of values per parameter, etc. Adding fields can turn a nightmare further forward.Now with quantity:$carrinho = [
["desc" => "Pão", "quantidade" => 2],
["desc" => "Leite", "quantidade" => 1]
];
Now with unit value$carrinho = [
["desc" => "Pão", "quantidade" => 2, "valor" => .50],
["desc" => "Leite", "quantidade" => 1, "valor" => 2.0]
];
Excellent, you don't need anything else to have the me shopping cart. Let's calculate the total:$total = 0;
foreach($carrinho as $item)
$total += $item['valor'];
If there are multiple locations in your application where you need to go through, add, remove, search elements and so on, you have basically two options. One is to access everything manually as in the example above and, when changes occur (such as new fields), you go out changing everywhere. Another is to create multiple functions to manipulate the data structure, such as adicionar_item($carrinho, $item) and consultar_item($carrinho, $descricao).Notice how all this sounds exactly like my first OO vs. procedural example. We can solve all this without OO, but we convince, we are reinventing the wheel and manually doing what we could delegate to the PHP interpreter to do more securely.Going further, but let's imagine that now you have carts with many items and often need to go through all the elements to total or query the items. First you think of changing the structure to something like:$carrinho = [
"total" => 2,
"items" => [
"Pão" => ["quantidade" => 2, "valor" => .50],
"Leite" => ["quantidade" => 1, "valor" => 2.0]
]
];
Solve the problem, but it will break the entire current code once again if you do not do all accesses via own functions. But of course you don't need objects. Simply that each procedural module has written in the documentation something like "do not access data structures directly!" And from there obviously you can trust everyone, including you, never will respect this desire and properly use your API. Good luck! Again:The lack of encapsulation is a common source of problems. It is difficult to maintain consistency with just a set of routines.This also makes it difficult for others to create new routines and create new features on top of theirs, because it doesn't get clear how they can or not use their data structure, while with classes that would be more obvious. Objects that represent complex entities are very surely manipulated by the various system routines with a well-defined interface in a class. Using the validation example, a well-encapsulated object guarantees the internal consistency of the data, which is not so simple with isolated functions. Of course you can create a validation routine and call in each function, but it is not so efficient in many cases preventative having to analyze in each routine the entire structure received to determine if the state is valid. This leads many libraries not to perform sufficient validation, resulting in difficulties in identifying problems when unexpected values appear. The potential of PHP in different paradigmsYou can solve a range of problems using only a specific paradigm, but almost always, for a non-trivial application a single paradigm is not the best choice or enough.When we talk about procedural, functional and object-oriented programming, we should think about how we can use all paradigms in harmony for a better solution.Object orientation is important and all major languages have some level of support for it or have variations to support the paradigm, such as C++. Functional programming is another paradigm that all languages seek to seize to a certain extent. Even languages like Java, traditionally nothing friendly to this paradigm, vehemently embraced it in its version 8.procedural programming is something that, to some extent, inevitable. We are not in the stage yet (and I am not sure we will arrive) where everything can be solved by functional expressions. The good and old control structures such as if and for will still exist within the methods and functions.The interesting thing about PHP is that it has reasonable support for all these paradigms, although some are better explored than others.The language is very well known for being easy to learn, where someone can have some code successfully running on their first day, otherwise in the first hour.Because of this, part of the community finds difficulties with the use of OO and even functional programming since they are concepts that take longer to digest, in addition to what PHP has a very strong procedural heritage and practically all modules have a procedural API, sometimes supporting OO at the same time or mixing objects with isolated functions.I see a movement of the PHP community towards the use of other paradigms, but less for functional programming, which is a shame.Finally, it would be good to see the three paradigms working together allowing better solutions in all areas.Examples where Object Guidance is badPlaces where in general OO is not legal or is used only indirectly include:Templates, for example, like Wordpress HTML templates where you mix control logic with HTML code. Even in other languages it is worth. Although auxiliary objects (helpers) be used, a text template is intrinsically procedural in its nature.Unstated functions (stateless) do not store or share variables, nor do they have prerequisites. For example, even in a purely OO language, a routine to convert an integer into text does not necessarily need to be in an object. Java, for example, uses static method for these cases.High performance rollers work better with simple data structures.Project StandardsSeveral free code projects die for lack of maintenance due to poor structuring, because no one can or has the courage to move. Regardless of the paradigm used, it is important to learn best practices.Project patterns are not exclusive to the OO Programming paradigm, but are known solutions to specific problems, in addition to a common language for you not to reinvent the wheel and call it "round cube".Knowing design patterns can teach you how other people have solved certain types of problems and since you learn some you will realize a good and other bad thing:The good thing is that you will learn other ways of thinking about the problem that you would not have done alone and will soon be adjusting and composing these different patterns in new ways. So you use the experience of others to grow faster.The bad thing is that you start to want to apply the patterns at all and end up creating some monsters until you learn (hope) to use these new powers responsibly.ConsiderationsNot only in libraries or frameworks, the use of Object Guidance is welcome in any functionality of your program, especially when you crave reuse and communication between different parts of the code while aiming for certain encapsulation to avoid further problems.Use the paradigm that is best to solve every problem, including all together. But for this you will need to know everyone besides the good and bad practices of each.