Jerry Yeh
Jerry Yeh

Reputation: 61

Design a Java interface: a method with variable numbers of parameters

I just came into a problem with designing an interface whose methods may have variable numbers of input arguments.

public interface FoobarSerialization<T> {
    Foobar serialize(T obj); 
}

The problem is, for the classes that implement this interface, they require different numbers of input arguments.

public class FoobarA implements FoobarSerialization<FoobarA> {
    @Override
    public Foobar serialize(FoobarA obj, int bar) {
        //...
    }
}


public class FoobarB implements FoobarSerialization<FoobarB> {
    @Override
    public Foobar serialize(FoobarB obj, Date date, String str) {
        //...
    }
}

Is there a good design or any genuine way to solve this problem? I know the method in the interface can be declared as:

Foobar serialize(T... obj); 

But I'm not sure if this was a good practice to design an interface like this.

Any thought?

Update: My intention of using an interface came from the collection of classes that need to be serialized and deserialized for different purposes. They serve as components under the same domain. But their serialization methods are quite different, especially considering their dependencies on objects and services that don't share any common features nor classes.

I guess the right question to ask here is: in terms of design, what's the best approach when there exits a set of classes which share the same behaviors (serialize, deserialize, doSomething, etc) but have different input args?

Upvotes: 4

Views: 10304

Answers (2)

Dmitry Zaytsev
Dmitry Zaytsev

Reputation: 23952

Composition pattern to the rescue.

In your particular case I would create interface which accepts just 1 parameter:

public interface Serializer<T> {

    Foobar serialize(T object);

}

Now, if you need to serialize several fields, you just create an object which has all fields you need to serialize:

class FoobarBundle {

    String stringField;
    int intField;
    byte[] arrayField;

    /* ... */

}

And write bunch of serializers: FoobarBundleSerializer, StringSerializer, IntegerSerializer, ByteArraySerializer. In the end combine all serializers in FoobarBundleSerializer like that:

class FoobarBundleSerializer implements Serializer<FoobarBundle> {

    StringSerializer stringSerializer;
    IntegerSerializer integerSerializer;
    ByteArraySerializer byteArraySerializer;

    /* constructor here */

    @Override
    public Foobar serialize(FoobarBundle bundle) {
        Foobar foobarString = stringSerializer.serialize(bundle.stringField);

        Foobar foobarInteger = integerSerializer.serialize(bundle.intField);

        Foobar foobarByteArray = byteArraySerializer.serialize(bundle.byteArrayField);

        return combineFoobarSomehow(foobarString, foobarInteger, foobarByteArray);
    }

}

Upvotes: 5

Kedar Mhaswade
Kedar Mhaswade

Reputation: 4695

Your mileage may vary, but usually confusing use (e.g. same number, but different types of arguments) of methods with the same name should be avoided. Though one can take help of method overloading, it is considered less than desirable. If the list of parameters is manageable, you should name the method differently to avoid ambiguities. See Item 26 in Effective Java 2.

The vararg methods are alright, but in Java, the best practice is to specify at least one concrete argument followed by a variable number of arguments of the same type. This is perhaps not applicable in your case, since there is no vararg syntax for a method like public Foobar serialize(FoobarB obj, Date date, String str);. It might be acceptable to use a syntax like (Object ... objects), but this practice is not considered generally applicable.

Contrast this with a method like printf which can and should be able to output a variable number of arguments of any type (including primitives) to an output stream.

Upvotes: 0

Related Questions