What good is a builder?

Asked

Viewed 13,840 times

109

In general classes have constructor methods.

What is the usefulness of the class constructor method? Why should we create it? It can work without it?

6 answers

103


Not always a builder is necessary. There are languages that turn out well without one where it is even possible to create a constructor.

Builders must give atomicity in the creation of the object. That is, it either creates the object in a valid state or it does not create.

When to use

Validation

If you need to validate the data and if some validation fails the object should not be created, then a constructor helps a lot. This is especially true if to validate one member another must already be in a valid state. It becomes difficult to control whether members can be initialized independently.

Processing

The same is true if some processing is necessary with the initial parameters before creating the object. Among these processes we can include the acquisition of external resources such as database, file system access, networks, etc., but it can only be simple calculations.

Enforcement order

The constructor also allows you to determine the order in which each member is initialized, either automatically or manually. Without a constructor the compiler or the Runtime of the language will determine the boot order of the members, which may or may not be documented, even because it may not be deterministic. Obviously it is not possible to handle complex forms of initialization automatically. This can be useful not only to determine the correct processing flow but also to validate correctly.

Competition

Atomic creation is especially important when we have concurrent processing. Under no circumstances can we make available the state of an object that can be shared with other processing paths without the object being fully initialized. Understand "completely" as the minimum to generate a valid state, it does not mean that all members need to be initialized, even because some may be optional even.

Which data should be passed to the constructor?

It only makes sense to pass to a constructor data that must exist in a valid form object or that must be processed before being inserted into the object.

If no data is required to have a valid status or it is possible that all data has default values (default) valid in its creation, the builder is not necessary.

This is not to say that it cannot exist, but it makes little sense to create a constructor that accepts data that is not mandatory from the zero moment of its creation. Although it may make sense in some languages that do not have more convenient ways of booting members.

In general it is even better to initialize members by their properties so you can use them and document the code better. This is also possible in a constructor if the language has named arguments, as is the case with C#, for example.

Standard builder

Some languages have a default constructor with no parameters that is used for the standard boot, but in general it is created by the compiler and the programmer does not need to worry. Of course, languages that do not have the constructor mechanism end up initiating the new object in some simplified way or leave memory junk available.

Initialization without constructor

Some languages provide other ways to initialize objects analogously or at least approximate to what the constructor allows. Of course probably with limitations. The most common forms are initialization by default (by default) and initialization during object creation. The second must have a syntax and semantics of its own in the language or at least an infrastructure that helps the atomicity, the order of execution, etc. C# and C++11 have (in the end they end up having a constructor, after all only functions can have execution).

Builders are not mandatory.

A common mistake is to think that every class must have a constructor. Of course there may be some language that requires its creation (beyond the default constructor) for some reason, but I don’t know any case. Usually it is possible to create an object and initialize its members in other ways, and except for the cases cited above, without any problem or difficulty.

If you don’t know why you’re creating a builder you’re probably creating it unnecessarily. On the other hand, if you don’t understand all the implications of creating an object partially, you won’t know when the constructor is needed. Also if you haven’t even thought about whether your object needs a builder, you need to study the subject better.

Note that creating an object without having all members initialized together is not a problem per se in most languages, especially those that initialize members with some value default. It can be a little more problematic in languages that do not initialize members and may contain "junk" as their content. It depends on the class context what actually needs to be initialized by a constructor and what can be left to be solved or by the value default or after the creation of the object, either by an initializer or through individual properties. The default creation will occur in some form of constructor, even if invisible to the programmer.

Ideally the explicit constructor should only exist if it is actually necessary and should only receive the minimum parameters to generate a valid state by manipulating as few members as possible. Preference, whenever possible, is that members are either initialized by default (or default) or individually in the code that will consume it.

Also be sure to create a builder when it brings advantage, convenience or even readability.

Construction functions

Even when a language has no constructors formally it is still possible to create functions that do the same job. This is common even in languages that have the mechanism explicitly. Often this form is known as Factory Method Pattern but may take other forms.

Term

The term is somewhat incorrect, deep down if it were called initializer would be better understood. For me the construction is given by the allocation plus the initialization.

  • 9

    The guy asks, and the guy answers :)

  • 5

    I saw that there are many people who do not understand well about this and already had places to link this canonical answer. I’ll make it better but I’m on a tight schedule.

  • 4

    I do not know if it is worth including in the answer because it is not a feature of constructors in general, but in languages that support overloading of methods, it is very useful to have multiple constructors with different signatures. Avoid having to create multiple derived classes, or a structure that manufactures different instances according to variable parameters in type and quantity.

  • @mustache what would be this memory junk? There is an answer that talks about?

  • @drmcarvalho I imagine not. This is taking anything that is there in the memory. Allocating memory is not putting anything in it, whatever is there. That’s why you need to boot.

25

Yes, you can work without implementing code in the class constructor.

The main utility of implementing the object constructor is to require parameters without which the object cannot live without, without which it makes no sense.

If to do your job an object requires some data or has some dependencies, it makes sense to require that it be passed to you already in the constructor, in order to make these explicit dependencies and in order to ensure that the object is in a valid state before asking it to perform any work or before passing it as parameter to another object or service.

Example of where a constructor falls well

Consider an entity repository object. This object offers business methods that deliver and persist entities.

To function, this repository relies on an object that provides access to the database.

So this is an option to implement this repository (without implementing the constructor):

class ClientesRepo {
    
    Dao dao;
    
    void setDataAccessObject(Dao dao) {
        this.dao = dao;
    }
    List<Cliente> clientesAtivos() {
        return dao.executeQuery("select c from Cliente c where c.ativo = 'S'");
    }
}

Using the above object, I can only consume the method clientesAtivos after setting the property setDataAccessObject, then the dependence that the object has on a Dao is not explicit, the abstraction was broken: I’ll have to look at the code within the class to understand what it needs to work.

Implementing the constructor helps make dependency explicit:

class ClientesRepo {

    Dao dao;
    
    ClientesRepo(Dao dao) {
        this.dao = dao;
    }
    List<Cliente> clientesAtivos() {
        return dao.executeQuery("select c from Cliente c where c.ativo = 'S'");
    }
}

Now, when the consumer instantiates a client repository, he will immediately notice the dependencies of this repository, and is required by the compiler to report this dependency.

The code became safer, more expressive.

  • By setting this constructor with parameters the default constructor is already overwritten or it remains accessible even without being set?

15

A constructor serves to instantiate objects of the class in which this constructor was defined.

Initialization of objects via constructors is necessary to avoid null reference error when using objects that have been declared but not initialized.

The code below, written in C#, creates a class with a constructor and uses this constructor to initialize an object of the data type defined by this class:

// Define uma classe.
public class Classe
{
    // Define uma propriedade nessa classe.
    public int Propriedade { get; set; }

    // Construtor padrão.
    public Classe()
    {
        // código de inicialização da classe aqui, caso o construtor padrão for utilizado na criação do objeto.
    }

    // Define um construtor simples para a classe, recebendo um parâmetro numérico.
    public Classe(int parametro)
    {
        this.Propriedade = parametro;
    }
}

public class Main()
{
    // Cria um objeto utilizando o construtor padrão.
    Classe objeto = new Classe();

    // Cria um objeto utilizando o construtor de parâmetro numérico.
    Classe outroObjeto = new Classe(10);
}

It is essentially a method that returns an object of the class type itself, and can receive parameters just like any other method.

You can work without builders. If no constructor is defined, usually the programming language will create a default constructor implicitly, without parameters, as shown in the above example.

  • The point is that if you don’t have these builders, this code will work smoothly. It will have a disadvantage when you need to initialize the member with 10 and this is really the problem, which you did not mention. The question is about your usefulness and not what it is.

5

The constructor can be used to instantiate an object...

// Define uma classe.
public class Classe
{
    // Define uma propriedade nessa classe.
    public int Propriedade { get; set; }

    public IColletion<Classe2> Classe2 { get; set; }

    // Construtor padrão.
    public Classe()
    {   
        Instanciando uma lista através do contrutor
        Classe2 = new List<Classe2>();
    } 
}
  • 2

    What do you mean, "too"?

  • The parameter constructor can be used to pass values and the empty constructor can be used to instantiate an object within the @bigown class

  • But I don’t understand the "too". O construtor pode ser usado para instanciar um objeto. Point. That’s all it can be used for. Anyway, note that the question is a little more complex than that.

  • @bigown I understood what you meant... but to be honest, I didn’t find many explanations about this way of using the builder, nor the best time, maybe that’s why I didn’t explain myself right in the post... Anyway, thanks, thanks!

2

In a simple way, it is a method that is executed at the instantiation of the object. The interesting thing about it is that you can ensure that a new object will have a set of values filled.

-1

Every code goes with an empty constructor embedded in it, but when you need to use data in different orders in the main class you will need constructors.

Browser other questions tagged

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