How to create/maintain "global variable" in java? To log in

Asked

Viewed 14,217 times

16

I’m making a desktop application in Java. This application has a login screen (Jlogin) and one with the main application (Jprincipal) and I have a Login class with methods for database query and login data validation. My problem is: I need to keep the user logged in somewhere (in order to get the profile and control access: screens, executions etc) and be able to access it on different screens. I thought of some ways:

  • Create a Static and public attribute in Jlogin or Login to save the user and access it outside Jlogin (which prompted a Login object for validation). I saw that it is a bad practice "global variables"

  • Create an internal class Sessao Static within the Jlogin or Login class (hence I still have the problem of "global" access because the Login class will be instantiated) where I refer to that user.

  • Access the Instantiated Login class in the Jlogin class in the Jprincipal class or whatever else is needed (I don’t know if it’s possible)

  • The Jlogin class I could access from Jprincipal but Login would not... or would have?

I don’t know how is the best way to do this: work with "sessions" in Java.

Remembering that it is a desktop application.

  • 1

    A suggestion would be to use the design pattern Singleton. It involves creating a single object of a class, and this object is accessed statically by the application classes.

  • Are you using any patterns? MVC for example?

  • You can control by the User object itself whether it is logging in or not by setting its permissions.

  • Our @mutlei liked your suggestion expensive, quite interesting I will test and see if it will work as expected!

  • @Leocbs I’m not using any standard but I’m (trying and I’m managing for now) keeping the interface separate from the rest of the application so I didn’t want to create anything in Jlogin but in the classes! Maybe let a class Sessao manage it there ;)

  • Because it is @Luishenrique, I don’t know if I can access the instance of the Login class created in Jlogin in other classes like Jprincipal... If so, it is simple: I create a classSsession only with the user, profile and get/set for query and I leave the Login only for validation and query of login data in the database and Seto so (Login) the Sessao class? Q think?

  • I think the best idea is to create the class Sessao applying the design Pattern Singleton even, so you instantiate it at the time it ralizes the login and makes the appropriate queries in its internal classes.

Show 2 more comments

4 answers

11

The easiest way you ever know, which is your first option:

"Create a Static and public attribute in Jlogin or Login to save the user and access it outside Jlogin."

Although you’ve seen "it’s a bad practice to use global variables," believe me, there’s a lot of systems developed more or less like this and making millions a year.

On the other hand, the people who made this code now regret it and see this kind of practice ("strong coupling") a constraint to earn even more millions, so they spend some of the millions they earn developing a new generation of their systems where these problems will no longer exist, and so walks humanity.

Since you came looking for a solution that does not fall into historical errors, here is a line of reasoning that will help you with this and everything else.

Think of your system as layers

It doesn’t have to be as complex as sometimes it seems! Continue reading.

The layers of the simplest design would be as follows::

    Aplicativo ------> Negócios
        |
        |------------> Banco de dados

The above diagram shows three layers, of which App depends on Database and app depends on Business, that depends on no one.

These layers are conceptual, not physical. You don’t necessarily need interfaces, or object/relational mapping, or dependency injection frameworks, or MVC, or advanced software architecture knowledge, or blah-blah-blah...

In the first instance, you just need to keep in mind the responsibilities of each layer and schedule accordingly. Keeping this separation of concepts will help even the simplest applications.

Application Layer

The code on this layer knows the mechanics of the system: the navigation between the screens, the user permissions (which screens and buttons it has access to), the details of connection to the database, etc.

This layer knows what the system does and how the user should interact with the system; so when the user gives an order (click a button, for example), this layer knows which business object to invoke and what information to pass on to him to do his job. But she doesn’t know as the business object does what it has to do. Who knows this is the layer of Business.

Business layer

The business logic is implemented by the classes in this layer.

In this layer there may be an object that, when changing information, registers the user you changed, but this object does not know where to get this name or user id - it needs to be informed to it.

Database Layer

The less layer Business knowledge of the database, better. But this is a rather complex concept and if the layer Business at least not need to know the details of connection is already a great start.

Creating a physical constraint to prevent the layers from mixing

This is very simple to do: distribute your code in Packages where one of the names next to the root identifies the layer. For example:

com.padaria.aplicativo.estoque
com.padaria.negócio.estoque

The bakery’s stock screens are in the first package and the stock business rules are in the second. And all Packages in the system will have the name or starting with com.padaria.app or starting with with.bakery.business.

So if you try to reference a package com.padaria.app from a package with.bakery.business, you know you will be breaking your layer design once a reference in this direction is not allowed. See diagram: the layer App can refer to itself and can also refer to the layer Business. Already the layer Business can refer only to itself.

The layer Database is the responsibility of the bank server, so there is no package.

Eventually, in the future, you will no longer be able to frame all your artifacts into these Packages, so there must be others like with.bakery.below, with.bakery.reports., etc., and then you will need to evolve the design beyond what I am proposing here. But it will be an evolution and not a revolution and there will be at most Refactoring and do not rework.

Framing the user problem in this layer solution

The main object of the layer App obtains and holds an instance of the user, and passes this instance as reference to the objects of the Business or for other forms that need it.

Take this pseudocode as an example:

public class FormPrincipal {
    
    private String nomeUsuario;
    
    public void main() {
        FormLogin formLogin = new FormLogin();
        formLogin.Show();
        if (formLogin.usuarioCancelou()) {
            return;
        }
        ArgumentosConexao argumentosDb = ArgumentosConexao.fromConfiguracoes("config.properties");
        ConexaoDb conexao = ConexaoDb.get(argumentosDb);
        if (!AutenticacaoUsuario.loginValido(formLogin.nomeUsuario(), formLogin.senha(), conexao)){
            Mensagens.show("Usuário ou senha inválidos.");
            return;
        }
        nomeUsuario = formLogin.nomeUsuario();
        Show(); // libera o usuário para interagir com este formulário principal
    }
    public void botaoProcessa_click() {
        Processamento processamento = new Processamento();
        processamento.processaTudo(nomeUsuario, conexao);
    }
}

In this code, the class Processamento (unlikely that someone has a class with this name, but you got the idea) resides in the layer of Business and the other classes reside in the layer App.

I like this code too because the dependencies are explicit - Each class exposes the resources you need to do your job. This facilitates the reuse of objects in different contexts, automated testing and system maintenance.

7

Yes, keep global state is not very recommended but I wouldn’t say it’s a terrible practice. In almost everything you will find a reason to use.

First you need to see if you have any way to eliminate that global state, if there’s a way to not need this information in any way. I may have to restructure the application but I can not say just with the information provided and I think that is not the focus of the question.

If you need the global state, there is no solution, you will have to store the global state. No matter how it will encapsulate it or how you will access it.

The first two options are quite feasible. The ideal is not to access the attribute directly. It may not be important for your application but it may be that in the future you have to modify a little the way to access this attribute, there would complicate. The access method, a getter would be useful.

Testability fanatics will say they can’t do it. But you probably don’t even know what they’re talking about. I mean, that’s bad for people who do Unit test in everything, yet still has solution, despite a little more complicated.

You can even use a normal class and instantiate it to access the global state, but it doesn’t make much sense unless you have a good reason for it. An example where it would be useful is if you do tests, because there is the ease of replacing the class with another one just to run the test. You might not have to. But this class will still have to access the global state, you will not be able to instantiate the class and have access to a state unless it is static in some way. Without the static attribute the value is lost when the instance goes out of scope.

One solution to this is to keep this instance alive all the time and pass it as a parameter to all the methods that need it. This technique is called inversion of control. It’s a lot of work, but it’s a possibility. It has the advantage of being more flexible - you may not need this flexibility - and it makes it more testable - that you may not be interested.

Eventually this class can be a Singleton. There are advantages and disadvantages to doing this, in your case it may be more advantageous. This way you can have a state that officially is not global, but in practice is.

5


Using the Pattern design Singleton.

This pattern ensures the existence of only one instance of a class, maintaining a global access point to your object.

Can create the class Sessao:

public class Sessao {
    // Variável estática que conterá a instancia da classe
    private static Sessao instance;

    // Construtor privado (suprime o construtor público padrão).
    private Sessao() {}

    // Método público estático de acesso único ao objeto!
    public static Sessao getInstance() {
        if (instance == null)
            instance = new Sessao();
        return instance;
    }

    /*
    Pode criar outros métodos que precise aqui, como getters e setters.
    */
}

So in your class JLogin, you instance the object Session:

Sessao sessao = Sessao.getInstance();
// Aqui popula os atributos da sessão que você irá utilizar

And later in the internal classes, when you instantiate the Session object you will receive what was previously created in login.

  • 2

    I loved all the answers, but yours is good for those who are too lazy to google (I looked, alias!! (And in books)). And Patterns design are 'beautiful'!

  • I love that they already posted, I thought that, here. I was also in trouble and it helped me. + 1

4

Avoiding global variables or maintaining a global state to store information as a logged-in user is good for a number of reasons, but requires care with the application architecture.

A great advantage is to facilitate automated testing, avoiding gambiarras. Another advantage is allowing system changes independently, without one screen possibly affecting all others when accessing shared data.

One way to achieve this is by reversing control. Instead of each screen accessing a global login object, the screens should get this object ready. With two screens it seems silly, but if the system grows it will make things much easier.

The class design looks something like this:

public class Principal {
   public static void main() {
       Login login = JLogin.efetuarLogin();
       if (login.isAutenticado()) {
           JPrincipal principal = new JPrincipal(login);
           principal.show();
       }
   }
}

public class JLogin extends JFrame {
    public static Login efetuarLogin() {
        //mostra modal solicitando usuário e senha
        //valida dados
        return new Login(dadosDoUsuario);
    }
}

public class JPrincipal extends JFrame {
    public JPrincipal(Login login) {
        String mensagem = "Bem-vindo(a) " + login.getNomeCompleto();
        List<Permissao> permissoes = login.getPermissoes();
        ...
    }
}

The architecture proposed above allows each screen to be independent of the others. It would be possible for two screens to receive different users. This would allow, for example, an administrator user to "impersonate" another user on a given screen, i.e., use a screen as if it were another user.

Browser other questions tagged

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