简体   繁体   中英

Generalize third-party classes with same getters without using reflection

Here's a sample code:

class A {
    private Header header;
    private Data data;
}

class B {
    private Header header;
    private Data data;
}

class C {
    private Header header;
    private Data data;
}


class G {
    private Class messageType;
    private Header header;
    private Data data;

    public G(Object message) {
        messageType = message.getClass();   
        if (message instanceof A) {
            header = ((A) message).getHeader();
            data = ((A) message).getData();
        } else if (message instanceof B) {
            header = ((B) message).getHeader();
            data = ((B) message).getData();
        } else if (message instanceof C) {
            header = ((C) message).getHeader();
            data = ((C) message).getData();
        }
    }
}

Model classes A , B and C cannot be changed since they come from various libraries (note that the attributes are only simplified). So when I receive these instances, I'd like to treat them in a generic way that's why I created a G model class. Is there a way to assign attributes to G instance without G being closely tied-up to A , B and C without using reflection? It is not an option since it is really slow. Java 8 solution is ok.

Single Responsibility Principle: G is a message, not a message factory:

class G {
    private Class messageType;
    private Header header;
    private Data data;

    public G(Class<?> klass, Header header, Data data) {
        this.messageType = klass;
        this.header = header;
        this.data = data;
    }
}

class GFactory {
    private Map<Class<?>, Function<Object, G>> mappings;

    public <T> void register(
             Class<T> klass,
             Function<T, Header> headerExtractor,
             Function<T, Data> dataExtractor) {
        Function<Object, T> cast = klass::cast;
        mappings.put(klass, cast.andThen(t ->
             new G(klass, headerExtractor.apply(t), dataExtractor.apply(t)))); 
    }

    public G createG(Object message) {
        return mappings.get(message.getClass()).apply(message);
    }

    private GFactory() {
        register(A.class, A::getHeader, A::getData);
    }
}

I also would say to not initialize the GFactory from within itself, but do that externally.

Not really.

The only thing you can do: decouple the two responsibilities here. Meaning:

Instead of having one class that is responsible for retrieving attributes and doing this "instanceof" switching ... you could push that into two different classes.

Meaning: first you define some "data transfer class" that gives G what G needs, like

class Foo {
  Foo(String header, ...) { ...
  String getHeader() { ...
  ...

Then you move that "instanceof" switching into some sort of factory that has a method like:

class FooFactory {
  Foo makeFooFrom(Object message) { ...

And then G uses that FooFactory to acquire a Foo object for any incoming message.

But the thing is: without using reflection, the "implicit" complexity of having different fields in independent classes that happen to have the same meaning to you can't resolved.

The only "advantage" that my solution has: G doesn't need to know anything about A, B, C ... it just knows about the Foo class.

But of course, then FooFactory needs to know all these things. Most often, complexity is like water - you can make it go a different way, but you can't compress it or make it "disappear".

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