fuin.org

Small Open Source Java Tools and Libraries

Optimized View and Logic (Controller) Separation with Swing


Part 1: Clear Separation of View and Controller

As a rule, the controller functions can be easily described via a interface.
A simple example:
package org.fuin.jaddr.part1;

import java.util.List;

/**
 * Address handling.
 */
public interface AddressController {

    /**
     * Load a list of addresses.
     */
    public List<Address> loadAddresses();

    /**
     * Delete an address.
     *
     * @param address
     *            Address to delete.
     */
    public void deleteAddress(Address address);

}
This interface allows the surface to load the required data very easily. The return value of the Load function does, however, pose a problem in relation to asynchronous processing. For this, the installation of a Listener function does prove helpful:
package org.fuin.jaddr.part2;

import java.util.List;

/**
 * Address handling.
 */
public interface AddressController {

    /**
     * Loads a list of addresses and notifies a listener.
     *
     * @param listener
     *            Gets informed about the result.
     */
    public void loadAddresses(LoadAddressesListener listener);

    /**
     * Listens to the results of the
     * <code>loadAddresses(LoadAddressesListener)</code> method.
     */
    public interface LoadAddressesListener {

        /**
         * Sets the address list.
         *
         * @param list
         *            Address list.
         */
        public void setAddresses(List<Address> list);

    }

    /**
     * Delete an address.
     *
     * @param address
     *            Address to delete.
     */
    public void deleteAddress(Address address);

}
While the Delete method does not require a direct return value, it appears to be expedient that the requestor is alerted to the termination of the deletion process. For instance, the surface could update itself accordingly:
package org.fuin.jaddr.part3;

import java.util.List;

/**
 * Address handling.
 */
public interface AddressController {

    /**
     * Loads a list of addresses and notifies a listener.
     *
     * @param listener
     *            Gets informed about the result.
     */
    public void loadAddresses(LoadAddressesListener listener);

    /**
     * Listens to the results of the
     * <code>loadAddresses(LoadAddressesListener)</code> method.
     */
    public interface LoadAddressesListener {

        /**
         * Sets the address list.
         *
         * @param list
         *            Address list.
         */
        public void setAddresses(List<Address> list);

    }

    /**
     * Deletes an address.
     *
     * @param address
     *            Address to delete.
     * @param listener
     *            Gets notified about deletion result.
     */
    public void deleteAddress(Address address, DeleteAddressListener listener);

    /**
     * Listens to the results of the
     * <code>deleteAddress(Address, DeleteAddressListener)</code> method.
     */
    public interface DeleteAddressListener {

        /**
         * The address was deleted.
         *
         * @param address
         *            Successfully removed address.
         */
        public void addressDeleted(Address address);

    }

}
Listeners have yet another advantage: In practical applications, a controller request frequently produces more than a single result and does so during the progression through a function. It could, for example, be helpful to once again select - on the surface - the address chosen during the request after the list has been loaded.
package org.fuin.jaddr.part4;

import java.util.List;

/**
 * Address handling.
 */
public interface AddressController {

    /**
     * Loads a list of addresses and notifies a listener.
     *
     * @param selectedAddress
     *            Currently selected address or null if no address is selected.
     * @param listener
     *            Gets informed about the results.
     */
    public void loadAddresses(Address selectedAddress, LoadAddressesListener listener);

    /**
     * Listens to the results of the
     * <code>loadAddresses(LoadAddressesListener)</code> method.
     */
    public interface LoadAddressesListener {

        /**
         * Sets the address list.
         *
         * @param list
         *            Address list.
         */
        public void setAddresses(List<Address> list);

        /**
         * Select the given address.
         *
         * @param id
         *            Address to be selected.
         */
        public void selectAddress(Address address);

    }

    /**
     * Deletes an address.
     *
     * @param address
     *            Address to delete.
     * @param listener
     *            Gets notified about deletion result.
     */
    public void deleteAddress(Address address, DeleteAddressListener listener);

    /**
     * Listens to the results of the
     * <code>deleteAddress(Address, DeleteAddressListener)</code> method.
     */
    public interface DeleteAddressListener {

        /**
         * The address was deleted.
         *
         * @param address
         *            Successfully removed address.
         */
        public void addressDeleted(Address address);

    }

}
Subsequently, it is possible to have the controller accompany the surface (in this case a simple JPanel) in the constructor, where it can be readily used:
package org.fuin.jaddr.part4;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JPanel;

public class AddressListPanel extends JPanel implements
        AddressController.LoadAddressesListener, AddressController.DeleteAddressListener {

    private static final long serialVersionUID = 1L;

    private AddressController addressController;

    private JButton buttonDelete;

    private JButton buttonLoad;

    public AddressListPanel(AddressController addressListController) {
        super();
        this.addressController = addressListController;
        // TODO Create controls...
        init();
    }

    private void init() {
        buttonDelete.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                final Address addressToDelete = getSelectedAddress();
                addressController.deleteAddress(addressToDelete, AddressListPanel.this);
            }
        });
        buttonLoad.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                final Address selectedAddress = getSelectedAddress();
                addressController.loadAddresses(selectedAddress, AddressListPanel.this);
            }
        });
    }

    private Address getSelectedAddress() {
        Address address = null;
        // TODO Find selected address
        return address;
    }

    public void selectAddress(Address address) {
        // TODO Reselect address in UI
    }

    public void setAddresses(List<Address> list) {
        // TODO Set addresses in UI
    }

    public void addressDeleted(Address address) {
        // TODO Remove address from UI
    }

}
As a result, we have achieved our first objective, i.e. the clear separation of View and Controller. However, the code is not yet ready for use in practical applications, given that the surface would freeze up during prolonged tasks because everything is running in the AWT Thread.
 
Next Page: Part 2: Single Thread Rule
 

 
Page: 1 / 2 / 3 / 4 / 5 / 6 / 7