Pergunta

This might be a very basic question with a very obvious answer but i am having hard time figuring this out.

How to know the return type of a method of a class involve in java factory patterns. for example looking at the code below... what would be the return type of the method invocation and how to cast it properly... and also how to write the javadoc also for the classes.

i am trying to write a library which user then can plug in to their project...

I have an interface

public interface myInterface
{
     public Object doA();
     public Object doB();
}

and concrete Classes as follow

public class concerete1 implements myInterface
{
public concerete1() {
}

@override
public Object doA()
{ return new String("Hello"); }

@override
public Object doB()
     { return "hello".getBytes(); }

}

and

public class concerete1 implements myInterface
{
public concerete2() {
}

@override
public Object doA()
{ return "hello".getBytes(); }

@override
public Object doB()
{ return new String("Hello"); }

}

and my factory class is as follow

public class factory
{
     private myInterface mi;

     public myInterface actionProducer(String choice)
     {
           switch(choice)
           {
           case "a":
                 mi = new concerete1();
                 break;
           case "b":
                 mi = new concerete2();
                 break;
           }
           return mi;
     }
}

and my test runner class is as follow

String result = factory.actionProducer("a").doA();
Foi útil?

Solução

You should not have to explicitly test the dynamic type of a factory method's return value. The static type should tell you all you need to know. That means the return type should be as specific as it needs to be to tell you what you can do with the object. For example, a factory method that makes maps of varying implementation should return Map:

public interface MapFactory {
    public Map makeMap();
    ...
}

Whether the Map is a HashMap or TreeMap or ConcurrentSkipListMap, you can use the Map methods to interact with it. If it turns out you need to call ceilingKey, which isn't a Map method, you have a design problem.

In your case, your factory methods make no sense, since there is no more specific type to return than Object, and nothing you can do with the return values beyond the API of Object. A more reasonable factory would return objects that can be interacted with the same way, regardless of how the objects are implemented.

Outras dicas

You shouldn't be interested in real factory product type. Abstract factory are constructed for factoring objects with common interface / abstract superclass. So, if you will have

public abstract class Animal {
     public void eat();
     public String foo();
}

and implementor classes:

public class Dog extends Animal {
    public void eat() {
     //stuff here
    }
    public String foo() {
        return "howgh";
    }
}

public class Cat extends Animal {
    public void eat() {
     //stuff here
    }
    public String foo() {
        return "meow";
    }
}

Your factory interface will look something like this:

public interface AnimalFactory {
     public Animal createAnimal();
}

And the concrete factory will be

public class CatFactory implements AnimalFactory {
     public Cat createAnimal(){
         return new Cat();
     }
}


public class DogFactory implements AnimalFactory {
     public Dog createAnimal(){
         return new Dog();
     }
}

So you can create common interface products, without knowing their real class:

Animal awesomeAnimal = catFactory.createAnimal();
System.out.println(awesomeAnimal.foo());

the output will be "meow". It is possible, because all products are subclasses of common interface (Animal here).

The return type will be an Object in all cases. I don't know what you're trying to cast to, but if it's a String, casting into a String from a byte array is not possible. You would have to use one of the String class' constructors instead ("String result = new String(factory.actionProducer("a").doA());"). If you were calling "doB()" (and thus getting a String as an Object) you could convert it to a string either by casting it ("String result = (String)factory.actionProducer("a").doA();") or by calling the return Object's toString() method ("String result = factory.actionProducer("a").doB().toString();").

Javadoc for these methods would describe each of them as returning an Object, because that's what they do.

In short, I would change the return types of "doA()" and "doB()" to what they are actually returning (String or byte array), but maybe this is not an option for what you are trying to do. Some additional context might help in coming up with a more detailed answer.

EDIT: Sorry, I didn't realize that the String and byte array return types were reversed over the two implementations. In the answer I described "doA()" as returning a byte array and "doB()" as returning a String, and I believe those are flipped around in the implementation you're using in your example (Concrete1). However, everything else pretty much stands.

The simplest answer would be to declare 'result' as an Object and then force a cast and just hope you got it right: if you don't, you'll get an error at runtime:

 Object obj = factory.actionProducer("a").doA();
 String str = (String) obj;

However, in your example, doA() returns either String or byte[], depending on the choice of implementation, so this will fail for "b" and succeed for "a". Maybe it was a typo in the question though?

With a slightly clearer definition of your objectives, using generic types might help, but its hard to say without more information.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top