When to use instance variables vs parameters?

Asked

Viewed 1,163 times

7

Assuming that a class A use a class B (directly or through an interface C to uncouple A of C).

Making it clear that A and B are independent parties, i and.., the relationship between the two classes is not of obvious composition or aggregation, but there is a "weak" association in which A uses B (in that case, by means of the contract established by C).

Consider the two examples (in Java):

Example 1 - C as Instance Variable:

public class A {
    private C c;

    // Não estou discutindo injeção através de construtores vs. getters and setters
    // Apenas assuma que `c` foi inicializado de alguma maneira
    public A(C c) {
        this.c = c;
    }

    public metodo1() {
       // faz algo com c   
    }

    public metodo2() {
       // faz algo com c   
    }

    public metodo3() { 
      // não faz nada com c
    }
}

Example 2 - C as parameter:

public class A {

    public metodo1(C c) {
       // faz algo com c   
    }

    public metodo2(C c) {
       // faz algo com c   
    }

    public metodo3() { 
       // não faz nada com c
    }
}

My question is in what situation should I get A has an instance variable of type C vs. when I’m supposed to pass an instance like C as a parameter for the methods of A?

In other words: In what situations the API exposed in Example 1 would be "better" than the API exposed in Example 2 and vice versa? What would be the reasons to support this decision?

After many years as a developer I still make this decision on the basis of feeling. I also noticed that over time the construction of the example 2 (which was rare, and readily refactored to 1 in my code) began to become more acceptable and even preferable in most situations. However, until today I have difficulty formalizing the reasons that lead me to choose one construction or another.

  • Part of the program more questions for the Beta

  • Personally, I consider this question a little "too broad" to mean "it is not clear what you are asking". Before reading his own answer, I thought it was a conceptual question (and I formulated the first part of the answer); after reading it, I saw that it was more about practical aspects (i.e. the "natural relationship between A and B is clear, but implementing it in this way brings negative consequences") - and I formulated the second. I suggest you edit the question if possible to make it more specific.

  • 1

    Hello @mgibsonbr, suggestions for better question are welcome. But I’m unfortunately not working on a "concrete code" example. The problem is that, in my view, there is a slight difference in certain situations. A conceptual gap and space for opinion when deciding between associations vs passing parameters repeatedly in the API. For me an association is stronger and "continues", while the passage of parameters is weaker and "discrete" (I don’t know if it made sense).

  • 1

    Well, my main suggestion is to make it clearer on what the question is not. For example, when you write "Assuming a class A use a class B" is ambiguous whether there is a relationship between the two [outside the context of the method]. If for the purposes of this question it is assumed that nay, that A and B do not relate conceptually, it would be interesting to have this explicit in the question. Likewise, its main motivation for what I understand is the creation of a good API, not the modeling of entities in classes. Seen through this focus, more targeted responses can be provided.

  • Much better now! + 1, and I will edit my answer to make it more concise.

2 answers

5


In fact, none of the options are ideal. I will briefly discuss each of them and then propose an alternative.

Option 1: save status to A

This solution, although inelegant, may be feasible at least in cases where there is no parallelism (i.e. only an algorithm running on a single thread will have access to A). In this case, the use of a variable representing the "C current" greatly simplifies its use.

However, when it comes to a third-party API, it is difficult to predict how it will be used. Taking as an example a question that I recently answered, the fact of the library matplotlib use a "current image" and a "current subplot" to direct all your operations makes it complex to work with multiple images and subplots at the same time (even in the absence of parallelism).

Option 2: pass C as parameter always

The biggest advantage here is flexibility, the biggest disadvantage is the verbosity of the code (always have to keep repeating the passage of c as a parameter). Most of the time this is just an inconvenience, so that this solution is valid without reservations. It is not ideal, but it is valid.

Option 3: "currying" of objects

The concept of currying, when supported by a programming language, usually applies to a single function. For example, given A.metodo1(C, D, E):F could be fixed the C in the form of curry(a.metodo1, c) -> fn(D, E):F. However, this is of little help to client code, since it will need to combine two or more methods of A to carry out its function.

However, if we extrapolate this concept to the whole class A, we can create an auxiliary class X which "fixes" the instance of C [beyond the very A] and exposes only the methods of A involving the class C. For example:

class X {
    private A a;
    private C c;

    public X(A a, C c) {
        this.a = a;
        this.c = c;
    }

    public metodo1() {
        // faz algo com a e c   
    }

    public metodo2() {
        // faz algo com a e c   
    }

    // Sem "metodo3"; somente os métodos em que A e C interagem
}

If convenient, A can serve as a factory for X:

class A {
    public X curry(C c) {
        return new X(this, c);
    }
}

This way you simplify the interface (i.e. create a facade) without having to "pollute" the class A with the introduction of a property that does not correspond to the conceptual relationship [static] between the entities A and C.

  • Currying gives me a mad longing for Scala and Haskell :). The idea of creating an auxiliary class to tie up A and C without entering an instance variable is very useful. You can expose an API in which an instance of A is "linked" to an instance of C without imposing this model to A. Thank you very much!

4

Some interesting references on the subject:

SOE - Parameter vs. Member variables

Main Points:

  • Class variables are considered object state
  • Using an instance variable implies maintaining state between two method calls. If the value stored in C do not need to live between two calls so the instance variable should not exist
  • The lower the lexical scope and lifespan of a variable, the lower the possibility of misuse and the better for the disposal of resources.

SOE: Instance variables vs Parameter Passing? Is there an argument?

Main points:

  • On the positive side, the use of instance variables prevents the proliferation of parameters in methods. The readability of methods with many arguments is impaired. The book Clean Code argues that methods should not have more than three parameters.
  • On the negative side, using instance variables only to avoid passing them as a parameter is not a good idea and swells the class.

Programmers: Ruby - when to use instance variables vs Parameters between methods?

Main points:

  • The decision between one style and another depends on the role of C before the whole class A. If the information carried on C is relevant to most of the class, so it makes sense to have an instance variable. Example: An object representing a bank account that needs the holder for almost all shares.
  • On the other hand, if the data are specific to a given method (and auxiliary methods) they must "travel" as parameters or intermediate objects.

Browser other questions tagged

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