简体   繁体   中英

What's a suitable return type for this Java method?

we are implementing a system with Strategy pattern.

Some callers to a specific methods require a Hashmap of data while in some case it would be a user-defined DTO. Some need both.

How do we decide which return type to use, instead of an Arraylist containing Hashmap and DTO? Is there anything more approporiate?

Example:

Some caller needs only 2 strings and prefers a Hashmap.

Some other caller to the same API needs 12 values which are suited to put in a DTO. 1st caller does not need all the elements in the DTO.

3rd caller needs all 14 values and hence both structures.

If some callers require Map (BTW don't use concrete types like HashMap in your interfaces) and others an object, there might be two reasons for that:

  • the method returns completely different things depending on the arguments (eg if the flag is set, return a map of privileges whereas if it's not, return object representing user himself )

  • you are returning different view of the same entity

In the first case your interface is seriously flawed and should be redesigned. Probably you need two methods performing different tasks. What do you mean by " different callers "? Depending on who calls the method, you decide what to return?

In the second case think of a more general model and return it. Then let the clients transform it to appropriate format. There are several ways to do this:

  • return format-agnostic type and provide methods converting to map and to object so the client can decide. For instance if you are not sure whether the client prefers weakly-typed map with properties or strongly-typed object having the same attributes/fields, return simple wrapper having toMap and toObject methods. Note that in this case the internal representation is not important, client always uses a view and you can always add new format in the future

  • return generic Model class having two subclasses: MapModel and ObjectModel , exposing appropriate getters in subclasses. Do not use instanceof but Visitor pattern instead. In this model you always return a single value and the client has to be able to deal with every format. If you add a new format (like XmlModel ), all the places where Model is used need to be customized to deal with new type. However in this scenario you don't need to transform the internal representation to different formats .

Note that returning an Object or any variation of that is simply landing in dynamic languages land where you no longer have any static-typing help.

I would create a strategy specific return type class that encapsulates both options to provide a clea, typed interface rather than juggling Object instanceof/casts throughout the implementing code. The tiny bit of boilerplate is acceptable in my opinion:

SomeClass {
    private final Map values;
    private final Object dto;

    SomeClass(Map values, Object dto) {
        ...
    }

    <D> D getDTO(Class<D> dtoType);
    Map getMap();..
}

Or generic the return type if you know DTO type upfront (you shouldn't, it would imply you made a poor design choice)

create a data class with getter and setters and return the same Object..

I think the simplest solution is to put 2 methods on your Strategy interface, one for each return type.

public interface Strategy {
    Map<String, String> executeReturningMap();
    DTO executeReturningDTO();
}

The callers can then choose for themselves which return type they want by calling the appropriate method.

No fuss, just deviating from the purist idea that a Strategy can only have one single method.

Why don't you simply return an Object. Ideally, I would do that as the last option. But you can consider returning a generic as well

public Object myMethod1(){}

public T myMethod2(){}

In the second case, it is obvious you may have to define your generic object.

What about generics?

public <T> T getPojo(final Object o, final Class<T> returnType)
{
    if (o instanceof HashMap)
    {
        return (T) getPojoHash((HashMap) o);
    }
    return (T) getPojoDTO((DTO) o);
}

private HashMap getPojoHash(final HashMap map)
{ // mount map
    return newMap;
}

private DTO getPojoDTO(final DTO d)
{ // mount dto
    return newDTO;
}

You can use reflection to avoid the returnType parameter. But I prefer to type.

Assuming you are returning Generic types and the list can be heterogeneous, returning reference to an ArrayList is not a bad idea. You could encapsulate everything inside another class, but at the end of it all, you would need to maintain some kind of list somewhere.

You could use generics, but there lies a problem in your need to return both things at the same time. The best solution is to make a class that contains the two return types, and simply using generics to create a method that returns what you need:

public <T extends SomeReturnObject> List<T> doSomething(Object someObject, Class<T> classToReturn) {
   // Do something here
}

Without having more information on your approach, I'd try and use an interface that provides the needed methods. Then you can provide a wrapper for HashMap , your DTO's could implement that interface directly and you could also have a wrapper that contains a HashMap and a DTO.

The caller shouldn't really know what is returned as long as the needed methods are provided.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM