Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What design approach to take when showing a list of heterogeneous items

Let's imagine I want to display a list of stock items in a table (using Java). The domain model consists of an abstract base class StockItem from which various other types of stock item derive. StockItem provides a minimal interface (getId() and getDescription()) but beyond that, the subclasses might have significant variations.

If I restrict myself to the methods defined on StockItem, I won't be able to present sufficient detail to the user so this means that some columns will refer to fields that are not applicable to some rows (for example, physical goods are countable and this count should appear in the table whereas service items, which are also expected to appear in the table, are not countable, in which case "N/A" or similar should be shown).

To stick with the example of "Countable", it seems to me that there are a couple of solutions (but bear in mind that Countable won't be the only interface involved).

  1. Make the countable interface part of the base class and force everything to be Countable even when they aren't. Classes for which this is meaningless would need to return some special value or throw an exception or otherwise indicate that they are breaking the contract of a StockItem.

  2. In my iterator, use lots of instanceof checks and cast appropriately. If I introduce a new subclass of StockItem or otherwise change the inheritence tree, I'll have to remember to change this code.

Both of these seem like antipatterns to me and I'd be interested to hear about any more elegent approaches that might be taken. I suspect there is no magic bullet to this but if other languages have features that make this type of thing easier, I'd be interested to hear them also (only out of general interest though, I won't be reimplementing this system any time soon:)

Thanks, Phil

like image 829
PhilDin Avatar asked May 20 '11 11:05

PhilDin


2 Answers

Adaptor pattern

In your StockItem class add a the method:

/**
 * Adapt the current instance to the type
 * denoted by clazz else return null
 * ...
 */
public <T> T adapt(Class<T> clazz){
    if( clazz.isInstance(this)){
        return (T)this;
    }
    return null;
}

This will be inherited by all sub-classes and will allow a caller to safely convert type. Don't be put off by the generics, it's just saying I want this method to return an instance that's the same type as the clazz parameter.

Now your table provider can implement something like this:

public String getColumnText(Object cell, int columnIndex) {
    if (cell instanceof StockItem) {
        StockItem item = (StockItem) cell;

        switch(columnIndex) {
            case ID:
                return item.getID;
            case DESCRIPTION:
                return item.getDescription;
            case COUNT:
            Countable countableItem = item.adapt(Countable.class);
                return countableItem == null ? "N/A" : countableItem.getCount();
        }
    }
    return "N/A";
}
like image 127
Jim Avatar answered Oct 11 '22 00:10

Jim


I'd start with a table model class, something like a bean with one field for each column in your table. An instance of that class can be displayed easily.

Then I'd introduce an interface

public interface TableModelAdapter {   // bad name - this is not the adapter pattern...
  public TableModel getTableModel();
}

and make StockItem implement this interface. StockItem will implement some default behaviour, all subclasses will add something to the fields in the table model:

public class Book {

  // ..

  @Override
  public TableModel getTabelModel {
    TableModel model = super.getTableModel();  // the initial one with default entries
    model.setTitle(this.title);                // like: a book can display a title
    return model;
   }
}
like image 28
Andreas Dolk Avatar answered Oct 10 '22 22:10

Andreas Dolk