7

Using an OO language with static typing (like Java), what are good ways to represent the following model invariant without large amounts of duplication.

I have two (actually multiple) flavours of the same structure. Each flavour requires its own (unique to that flavour data) on each of the objects within that structure as well as some shared data. But within each instance of the aggregation only objects of one (the same) flavour are allowed.

FooContainer can contain FooSources and FooDestinations and associations between the "Foo" objects BarContainer can contain BarSources and BarDestinations and associations between the "Bar" objects

interface Container()
{
   List<? extends Source> sources();
   List<? extends Destination> destinations();
   List<? extends Associations> associations();
}

interface FooContainer() extends Container
{
   List<? extends FooSource> sources();
   List<? extends FooDestination> destinations();
   List<? extends FooAssociations> associations();
}

interface BarContainer() extends Container
{
   List<? extends BarSource> sources();
   List<? extends BarDestination> destinations();
   List<? extends BarAssociations> associations();
}

interface Source
{
   String getSourceDetail1();
}

interface FooSource extends Source
{
   String getSourceDetail2();
}

interface BarSource extends Source
{
   String getSourceDetail3();
}

interface Destination
{
   String getDestinationDetail1();
}

interface FooDestination extends Destination
{
   String getDestinationDetail2();
}

interface BarDestination extends Destination
{
   String getDestinationDetail3();
}

interface Association
{
   Source getSource();
   Destination getDestination();
}

interface FooAssociation extends Association
{
   FooSource getSource();
   FooDestination getDestination();
   String getFooAssociationDetail();
}

interface BarAssociation extends Association
{
   BarSource getSource();
   BarDestination getDestination();
   String getBarAssociationDetail();
}
3
  • 1
    It's difficult to say without seeing some code that uses these classes. Can you post a bit of your unit test code? In general, any code with parallel naming is somewhat suspect. Commented Mar 20, 2011 at 2:01
  • So, a more concrete example is a network. A network consists of, say, clients and servers each with an "address". However, a network also has a protocol associated with it. All addresses within a Foo network have to be FooAdresses, and all addresses within a Bar network have to have BarAddresses. I never want a FooClient in a BarNetwork. However, some operations (e.g.) drawing a diagram of the network in a GUI do not really care about that rule as they maybe don't even render the values of the addresses. So you have an abstract use but still need to enforce the business rule for the protocol. Commented Apr 5, 2011 at 9:54
  • 2
    I have since learned that this problem has a name in the programing languages literature "the expression problem". Commented Mar 4, 2013 at 22:13

3 Answers 3

2

Generic programming is your friend:

interface Association<S extends Source, D extends Destination> {
   S getSource();
   D getDestination();
}
interface FooAssociation extends Association<FooSource, FooDestination> {
   String getFooAssociationDetail();
}
interface BarAssociation extends Association<BarSource, BarAssociationDetail> {
   String getBarAssociationDetail();
}

interface Container<S extends Source, D extends Destination, A extends Association<S, D>> {
   List<? extends S> sources();
   List<? extends D> destinations();
   List<? extends A> associations();
}
interface FooContainer extends Container<FooSource, FooDestination, FooAssociations> {}
interface BarContainer extends Container<BarSource, BarDestination, BarAssociations> {}    

I am not a Java programmer, so please excuse any mistakes. It should give you an idea however.
Please note that the type parameter constraints are not necessary to build up your type hierarchy, but they better communicate the purpose of the generic base interfaces.

1
  • 2
    I have used this solution in a few cases, and it does work in the somewhat simple example I have given here. But the number and complexity of generic parameters grows very quickly if a few more classes are involved. It seems to me to end up with the "Container" class having a generic parameter for every type that can vary in the graph. For example an actual model I have built has about 7 classes that vary by protocol. The topology of the object graph is the same, but the attributes of each object vary by protocol and "foo" objects cannot mix with the "bar" objects. Commented Jul 20, 2011 at 20:19
0

I have see and applied, similar code like these, sometimes called "dual hierarchy", but its the same, with the only difference that uses classes directly, not thru interfaces. I actually had the same question, because looked complex, and tought I was coding something the wrong way.

I usually not code in java, but should be something like these:


containers.j

package containers;

/* put base classes in single file */

class JSource
{
   String getSourceDetail1();
}

class JDestination
{
   String getDestinationDetail1();
}

class JAssociation
{
   Source getSource();
   Destination getDestination();
}

public class JContainer()
{
   List<JSource> sources();
   List<JDestination> destinations();
   List<JAssociations> associations();
}

foos.j

/* put extended subclasses in single file, independant from base classes */

import containers;

package foos;

class JFooSource extends JSource
{
   String getSourceDetail2();
}

class JFooDestination extends JDestination
{
   String getDestinationDetail2();
}

class JFooAssociation extends JAssociation
{
   JFooSource getSource();
   JFooDestination getDestination();
   String getFooAssociationDetail();
}

public class JFooContainer() extends JContainer
{
   List<JFooSource> sources();
   List<JFooDestination> destinations();
   List<JFooAssociations> associations();
}

bars.j

/* put extended subclasses in single file, independant from base classes */


import containers;

package bars;

class JBarContainer() extends JContainer
{
   List<JBarSource> sources();
   List<JBarDestination> destinations();
   List<JBarAssociations> associations();
}

class JBarSource extends JSource
{
   String getSourceDetail3();
}

class JBarDestination extends JDestination
{
   String getDestinationDetail3();
}


class JBarAssociation extends JAssociation
{
   JBarSource getSource();
   JBarDestination getDestination();
   String getBarAssociationDetail();
}

The main difference is that the base classes are in a separate file / package, indicating the relation among those classes. Then, each case when subclassing the base classes are required, I use another file / package for the subclasses, "foos.j" and "bars.j".

I guess there is no reason to mix "foos" and "bars" in the same code. With difference of using classes instead of interfaces, and separate files, there is no other way to simply this "Design Pattern".

Some examples of the "parallel inheritance hierarchies" are mentioned in the "Design Patterns" book. Altought not as a design pattern itself. Personally, I see it as a separate, new pattern, different from the "Composite Patern".

1
  • I agree, they should probably be in different packages, but I left the packaging/file arrangement out for simplicity. I don't think it really affects the underlying design issue, which is that the underlying structure of the object model is repeated for each new flavour (here foo and bar, but in reality often many more times). Commented Mar 16, 2011 at 9:59
0

An approach that works with any OO language that supports reflection is to use the "Factory" pattern. The way it would work (using Java code) would be to establish a naming convention for your classes. For example, if XXXSource had a specialized display code you would have a corresponding XXXView in an appropriate package. Your TreeWalker would have simple logic looking something like this:

class TreeWalker
{
    public void displayContainer(Container container)
    {
        for(Source source : container.sources())
        {
            DisplayFactory.display(source);
        }
    }
}

The DisplayFactory class would have a predetermined package it's looking in and would work something like this:

class DisplayFactory
{
    String displayPackage = "com.mycompany.source.views.";

    public void display(source)
    {
        String sourceName = source.getClass().getName();
        String viewName = displayPackage
            + sourceName.substring(0,
                sourceName.length() - "Source".length())
            + "View";

        IView view = null;

        try
        {
            Class viewClass = Class.forName(viewName);
            view = viewClass.newInstance();
        }
        catch(Exception e)
        {
            // no specialized view, using default
            view = new DefaultView();
        }

        view.display(source);
    }
}

The end result is that you only have to create specialized views for the sources you want to, and fall back to the default implementation in all other cases. The naming convention saves a bunch of configuration magic, and is actually a fairly common way to solve the problem.

The downside is that you may have performance penalties due to more objects, and using exception handling to determine when no class exists. Assuming the Views hold no state, you can optimize a bit by caching the View instance to the source class name. If the quick lookup fails, you fall back to the heavier approach and store the view instance you found for next time.


Original answer

Based on your description and your comment, it sounds like you want to do a little rendering based on the data. Have you considered having your rendering code work with the base Container class? That would be the whole purpose behind an inheritance hierarchy like this.

As long as your base Container class has the methods you need to navigate the objects within properly, you don't need to know anything about the more specific implementations. If you need to render some data more specifically, you might take advantage of the fact that the data elements have different types.

class TreeWalker
{
    public void displayContainer(Container container)
    {
        for (Source source : container.sources())
        {
            displaySource(source);
        }
    }

    private void displaySource(FooSource foo)
    { /* display foo specific source info ... */ }

    private void displaySource(BarSource bar)
    { /* display bar specific source info ... */ }
}

The core displayContainer method knows how to work with the base classes/interfaces you are using to define your more specific collection of objects. You can use a factory to create the more specific renderers, etc. The basic idea is that you are working with the base class/interface to handle the more generic algorithms.

2
  • I think that this solution requires a language with dynamic dispatch? Although something similar is possible in Java, etc, using a visitor or double dispatch pattern. But I am thinking of the question more from the point of view of using OO to modell the domain. I want to capture the rule that "Destinations are associated with Sources" as well as the rule that "FooDestinations are associated with FooSources" and "BarDestinations are associated with BarSources" with as little duplication as possible. Commented May 10, 2011 at 12:50
  • It's more of parameter variance. In Java, for instance, the methods that vary by parameter type are all stored separately. The compiler can bind to any one of them, but always chooses the most narrow method available. In the above code, your FooSource would be handled by the displaySource() method that accepted FooSource objects. If you had a BazSource it would be handled by the displaySource() method that accepted generic Source objects. Commented May 10, 2011 at 13:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.