Principle open / closed - how to understand this?

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

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

In these cases I can not see a way to resolve this with only inheritance. If what is changing is really how the method should work, I will need to modify it.

In this way, what does the open / closed principle really mean and how do you use it in practice?

    
asked by anonymous 13.05.2014 / 13:24

3 answers

16

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'll illustrate.

Suppose you have a system that processes payments. 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 you can understand his proposal.

Please note that the code is highly coupled and should be modified whenever a payment type is appended, removed, 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 multiple implementations.

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

In my understanding this is the most important concept, because when we do not mess with what already exists, the chance of you "breaking" what already exists is "infinitely" smaller.

In the example above, the IntegracaoBanco parameter is a Control Inversion (IoC) type, which is another SOLID principle. 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 messing with existing code. In other words:

  

We do not need to change class content, just create new interface implementations or override existing class methods.

For a much more complete example see this answer .

    
13.05.2014 / 14:25
8

The open / closed principle prizes basically for not spoiling what is already ready. That is, think of your preferred programming language, now suppose you have numerous applications developed in this language, however one fine day comes a newer version of this language and you are obliged to come back repairing all your applications developed to the present moment since the language simply changed the way we did something and did not give continuity to the old way of doing it. Now multiply this by all the developers of that language around the globe. Have you thought about the chaos this would generate?

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

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

Example: You made a code without the getters and setters, however you noticed that some of the attributes of your class need a validation, let's say any date that can not be defined as before the current date, since in fact it should referring to something in the future. That is, you should not allow something like this:

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

To fix this you resolve to get and set to the date field and change public access to private. By doing this all the codes that made 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 has always had the get and the set nothing prevents you from implementing the value check before changing your date, so you only have to improve your code without breaking the code of the other classes .

Code example using the set without thinking about validation:

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

Code when you first noticed 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 example above you have extended your code, but you have not changed it. Okay, in practice you made a change, but this modification is transparent to those who depend on your class, so you can change internal details as long as that does not affect who depends 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 within the set is not met, this example is just a good simplification of how not to hurt the principle, worth being quoted as being broadly known among developers.

More advanced solutions to ensure the open / closed principle would basically be a more elaborate modeling of your classes, such as the Factory Design Pattern, which is an intermediate between the class you want a new class instance and the class that will provide this new object. Inside the Factory you put the rules of creation of the object, in case one day you need to change these rules you can do without making them modify the classes that depend on it.

    
13.05.2014 / 14:24
3
The Open / Closed principle should apply when the scenario requires that the methods of a child class are not changed and function exactly as originally implemented.

In these cases when something new needs to be entered this is done through a new method (extension) and not through an override . p>

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

    
13.05.2014 / 15:58