1

I'm a self-taught programmer, and like many other noobs I once struggled with the classic "passing data between forms" problem. When I was building my first chat application in Java around a year ago, I came across this problem again. I solved it by passing a reference to the running GUI to the constructor of another class so it could directly manipulate the GUI. For example:

class GUI {
    Writer w;
    void initGUI() {
        w = new Writer(this);
    }
}

class Writer {
    GUI ui;
    public Writer(GUI ui) {
        this.ui = ui;
        ui.textBox.write("Yo");
        // now, instead of the "this" keyword, I can say "ui.w" or "ui.w.ui.w.ui.w..."
    }
}

I am certain that this is one of the most efficient ways to transfer data "upstream", but I was given grief (and some praise) for suggesting this on a forum for someone having the same problem. So what kind of chaos could arise from doing this on larger applications? When I look at my applications that use this, I notice that they're very pragmatic and efficient, but also a bit too organic (if you will) and complicated (especially when there are threads accessing the GUI at different times, etc).

Is there a better way of doing this? I took a different approach out of necessity while using Qt, but it was more complicated since it used signals, slots, and threading. Please point me in the right direction. Thanks.

ICoffeeConsumer
  • 882
  • 1
  • 8
  • 22

3 Answers3

4

The main problem with this approach is coupling. The Writer class, instead of being a generic, reusable Writer class, able to do its job and, if needed, send events about what it does, is tightly coupled to this specific GUI.

If it accepted listeners, like all the Swing components do, you could reuse the Writer in several different GUIs, or choose to update a text area instead of a text field for example.

Another solution would be touse pull rather than push. Instead of having the writer update the GUI, you could have the GUI call methods of the writer in order to query its state. It's not always possible, but it often is.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • I assume the "pull" method requires threading? Nothing wrong with that, but (as you said) it's not always possible. I guess a GUI app should generally be programmed with concurrency if it's more than a basic calculator or something, anyway – ICoffeeConsumer Dec 09 '12 at 19:20
  • 1
    Not necessarily. Suppose that the Writer's job is to take an OrderItem object, save it in a database, and return the cumulated Order amount. The Writer could write the result to the "cumulated order amount" text field of the GUI, or it could just return it to the GUI, and the GUI itself would update the text field. – JB Nizet Dec 09 '12 at 19:25
  • Oh, that makes sense. I guess I do that all the time, but generally not in the context of GUI programming. – ICoffeeConsumer Dec 09 '12 at 19:27
1

I solved it by passing a reference to the running GUI to the constructor of another class so it could directly manipulate the GUI.

You want to decouple your business logic from your presentation layer. You want to be able to test your business logic in isolation, independent of the gui. You want to be able to change your frontend gui, without having to modify the backend. This is known as Separation of Concerns.

There are many design patterns to help you achieve this.

One of which is Model View Controller (MVC).

Instead of interacting directly with the GUI, you interact directly with the model.

Dan
  • 552
  • 2
  • 6
0

Yes, Java is pass-by-reference with objects, definitely.

But what you're doing is that your Writer assumes that your GUI is written in stone, meaning that it will always have a property called textBox.

Just by the look of things, Writer should not be responsible for accessing and manipulating the properties of GUI. If you change what's in your GUI, you will also need to recode the Writer class.

You could recode your Writer to merely send a message to the GUI because its responsibility is just to tell GUI what to write, not how to write it. Then leave it upto the GUI to process that message:

class GUI
{
     Writer w;
     void initGUI()
     {
        w = new Writer(this); 
     }

     void ReceiveMessage(string message)
     {
         this.textBox.text = message;
     }
}

Your Writer class:

class Writer
{
    GUI ui;

    public Write(GUI ui)
    {
         this.ui = ui;
         // Don't send the message in the constructor.
    }

    // The program calls this function with whatever text the user enters.
    public void SendMessage(string message)
    {
        ui.ReceiveMessage(message);
        // Writer no longer depends on ui to have a textbox Element.
    }
}

The above code could be improved with an interface. This way, you can pass anything to your writer that needs to know how to receive a message:

interface IMessageReceiver
{
    void ReceiveMessage(string message);
}

Now, your GUI becomes an IMessageReceiver:

class GUI implements IMessageReceiver
{
     Writer w;
     void initGUI()
     {
        w = new Writer(this); 
     }

     public void ReceiveMessage(string message)
     {
         this.textBox.text = message;
     }
}

Writer class now:

class Writer
{
    IMessageReceiver receiver;

    public Write(MessageReceiver receiver)
    {
         this.receiver = receiver;
         // Don't send the message in the constructor.
    }

    // The program calls this function with whatever text the user enters.
    public void SendMessage(string message)
    {
        receiver.ReceiveMessage(message);

    }
}

The difference here is that Writer will no longer care whether or not it's a GUI. It will take any object as long as it is an IMessageReceiver that knows how to receive messages.

If you're not familiar with interfaces, do some reading/research on it. They are extremely handy, if not, vital!

Mickael Caruso
  • 8,721
  • 11
  • 40
  • 72