Open/closed principle - how to understand this?

Asked

Viewed 1,131 times

13

In object orientation there is SOLID and one of the principles is the open/closed one that I learned as follows: "software components must be open for extension and closed for modification" and components include classes, methods, etc. Although it seems like a good idea, I can’t understand how it actually works in practice.

What happens is that I can imagine several reasons that may require modification of a class, among them changes in the requirements of the system being developed. Although I am aware of this idea, my client is not and he can very easily change his mind on some points that require revision of the logic itself within the classes.

In those cases, I can’t see a way to solve it solely by inheritance. If what’s changing is really how the method should work, I’ll need to modify it.

In this way, what the open/closed principle really means and how it is used in practice?

  • Relevant: http://qualityisspeed.blogspot.com.br/2014/08/why-i-dont-teach-solid.html

3 answers

17


First of all, didactic examples are not applicable to all real situations. It’s up to you to understand the principle and apply wherever it brings real benefits to your project.

On the Open/Closed principle, I will illustrate.

Suppose you have a payment processing system. You can implement a method as follows:

class Pagamento {
    void efetuarPagamento(String tipo, Integer codigo, Double valor) {
        if ("BOLETO".equals(tipo)) {
            new IntegracaoBoletoBanco().pagarBoleto(codigo, valor);
        } else if ("CARTAO".equals(tipo)) {
            new IntegracaoCartaoBanco().pagarCartao(codigo, valor);
        } else if ("DINHEIRO".equals(tipo)) {
            new IntegracaoContaBanco().pagarDinheiro(valor);
        }
    }
}

The code above is fictitious, but I think I can understand his proposal.

Note that the code is highly coupled and should be modified whenever a payment type is added, withdrawn or modified. That is, it is open for modification.

We can refactor this code so that the algorithm becomes more generic. Let’s see:

class Pagamento {
    void efetuarPagamento(IntegracaoBanco integracaoBanco, DadosPagamento dadosPagamento) {
        integracaoBanco.pagar(dadosPagamento);
    }
}

Consider that IntegracaoBanco and DadosPagamento are interfaces and can have several implementations.

This time our code has become much simpler and allows you to create new payment implementations through the interfaces or extending classes that are already part of the system without tampering with existing code.

In my understanding this is the most important concept, because when we do not touch what already exists, the chance for you to "break" what already exists is "infinitely" less.

In the above example, the parameter IntegracaoBanco is a type of Control Inversion (Ioc), which is another principle of SOLID. Usually they end up relating to each other. The other parameter encapsulates the data used.

In short, the Open/Closed principle could be understood as an implementation that allows adding new features without tampering with existing code. In other words:

We don’t need to change class content, just create new interface implementations or overwrite existing class methods.

For a much more complete example see this response.

  • 1

    Researching a little on the subject I came to the conclusion that I almost did not give a wrong answer, however I think I can technically base it on Meyer's OCP. Your answer is mt good, and is based on Polymorphic OCP which is more modern. I still need to study to have ctz that I didn’t say anything stupid because I haven’t been able to confirm by principles everything I said. About exemplos didáticos não são aplicáveis a todas as situações reais, worth a lot to be quoted, I myself was very worried when creating an example, while just adding this text could have made me more free.

  • @Math To tell you the truth I didn’t know about this distinction, but I just read a little and saw that I actually made a mix of the two. Thank you.

  • 1

    For the record, I posted my comment before your question was accepted, rs.. About the terms, I also did not know, however I started researching because after reading your answer I suspected that I had spoken zucchini, but now I came to the conclusion that the part I talk about new Lps is based more on polymorphic, already the rest is based only on Meyer. That is, despite my confusion my answer is valid =). However the concepts were not clear to me at the time I was writing, I probably edited my answer by exposing it. Anyway, thank you for the reply.

  • @Math As soon as you learn!

  • 1

    @utluiz his answer raised the question whether it is possible to add new classes to the architecture without editing existing code to be able to instantiate/access these new classes, and without using reflection to find these new classes automatically. I’ve been talking to Piovezan about it in the comments of that answer, Could you please clarify this matter in your answer to answer that question to whoever reads your answer and perhaps has that same doubt? Thank you in advance. :)

  • @Douglas I added a very detailed answer to the other question: https://answall.com/a/281385/227

Show 1 more comment

9

The open/closed principle basically values not spoiling what is already ready. That is, think of your preferred programming language, now suppose you have numerous applications developed in that language, however one fine day comes out a newer version of this language and you are forced to go back to fixing all your applications developed so far because the language simply changed the way to do something and did not continue the old way of doing it. Now multiply that by all the developers of that language around the globe. Have you thought about the chaos that this would generate?

In smaller proportions we also develop code that can be used by other classes of ours in other applications or even used by others.

An excellent (simplified) example of avoiding hurting this principle are getters and setters.

Example: You made a code without the getters and setters, however you realized that some of the attributes in your class need a validation, say some date that cannot be defined as before the current date, because in fact it must refer to something in the future. I mean, you shouldn’t allow something like this:

material.dataPrevistaDeChegada = new DateTime("01/01/2014");

To fix this you decide to put the get and set to the date field and change the access from public to private. Doing this all the codes that made the access to the attribute directly will be broken, because it is no longer possible to access the attribute the way it was done before.

However, if the attribute has always been private and always had the get and set nothing prevents you from implementing the value check before changing its date, for this you just need to improve your code without breaking the code of the other classes.

Code example using set without thinking about validation:

public void setDataPrevistaDeChegada(DateTime dataPrevistaDeChegada) {
    this.dataPrevistaDeChegada = dataPrevistaDeChegada;
}

Code when you realized that validation was important:

public void setDataPrevistaDeChegada(DateTime dataPrevistaDeChegada) {
    if(dataPrevistaDeChegada.isBefore(new DateTime(DateTime.now()))) {
        return;
    }
    this.dataPrevistaDeChegada = dataPrevistaDeChegada;
}

The return is just an example, if you want you can assign a default value as long as this is acceptable for your application.

In the above example you extended your code, however you didn’t change it. Ok, in practice you made a modification, but this modification is transparent to those who depend on your class, so you can change internal details as long as it doesn’t affect those who depend on your class, this would be called extension rather than change.

By modifying the set, you have ensured that the date attribute will not change if the condition placed inside the set is not met, this example is just a good simplification of how not to hurt the principle, worth being cited for being widely known among developers.

More advanced solutions to ensure the open/closed principle would basically be a more elaborate modeling of its classes, such as the Factory Design Pattern (Factory Design Pattern) which is an intermediate between the class that wants a new class instance and the class that will provide that new object. Inside the Factory you put the rules for creating the object, if one day you need to change these rules you can do without having to modify the classes that depend on it.

3

The beginning open/closed should be applied when the scenario requires there to be assurance that the methods of a child class have not changed and function exactly as originally implemented.

In such cases when something new needs to be introduced it is done by means of a new method (extension) and not by means of a override ().

It makes more sense when analyzed and applied in the context of the other SOLID principles and is not necessarily something to be followed. The Override of methods is in many cases something useful, and in these scenarios should yes, be used.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.