Java: common interfaces and factory

I have a base class Plantfrom which there are two subtype paths. Fruit extends Plantand Vegetable extends Plant. Fruitand Vegetableare also abstract classes, and then they have other classes, such as Banana extends Fruitand Spinach extends Vegetable.

Now, say, for example, the operation must be performed through these classes and there is an interface for the operation. The current project is as follows:

I have an interface:

abstract class Plant {}
abstract class Fruit extends Plant {}
class Banana extends Fruit {}

public interface PlantWeigher<T extends Plant> {    
    public int weight(T fruit);    
}

Then I:

public abstract class FruitWeigher<Y extends Fruit> implements PlantWeigher<Y> { }

Next I have a specific class:

public class BananaWeigher extends FruitWeigher<Banana> {
  public int weight(Banana fruit);    
}

Now, as a user who simply understands Plant, I would like to get the correct instance of Weigher, and then perform the operation.

So Factory looks like this:

public class WeigherFactory {    
   public static PlantWeigher<? extends Plant> getInstance(Plant plant){
     // based on some logic on Plant argument, I return, lets say:
      return new BananaWeigher();    
   }
}

So, in the user class, I call the following function:

PlantWeigher<? extends Plant> instance = WeigherFactory.getInstance(userPlant);
instance.weight(plant);  // THIS LINE GIVES AN ERROR

-, , , , - .

.

+4
3

:

 PlantWeigher<? extends Plant> instance = WeigherFactory.getInstance(userPlant);

: " , Plant, ". , factory a BananaWeigher, a Tomato . , factory :

public class WeigherFactory{

   public static <P extends Plant> PlantWeigher<P> getInstance(P plant){
    // should return a correct Weigher for the subttype of Plant P
            return ...
  }
}

:

 PlantWeigher<Banana> instance =WeigherFactory.getInstance(banana);
 instance.weigh(banana)

PlantWeigher,

, Plant PlantWeigher, :

 public interface PlantWeigher {
    public int weight(Plant p);
}

, a PlantWeigher , .

+2

, , - The method weight(capture#2-of ? extends Plant) in the type PlantWeigher<capture#2-of ? extends Plant> is not applicable for the arguments (Banana).

Banana PlantWeigher<? extends Plant>, int weight(T fruit). PlantWeigher<? extends Plant> PlantWeigher<? extends Orange>, , -, Banana s.

- factory :

public static <T extends Plant> PlantWeigher<T> getInstance(T plant) {

:

PlantWeigher<Plant> instance = WeigherFactory.getInstance(myBanana);
instance.weight(myBanana); // No more error

PECS. Java > Java Generics: PECS.

:

  • ? extends X
  • ( ) ? super X

(extends) . , instance.weight , null.

instance.weight(null);  // No more error

, () , "out". , , - . Plant. Banana T getTestPortion() PlantWeigher<? extends Plant>, PlantWeigher<Banana>.

, , - Plant, Banana, Orange.

, PlantWeigher<Banana> PlantWeigher<Orange> , , .

+2

, , - . factory, . , , . .

Plant banana = ...;
Plant spinach = ...;
Weigher<Plant> bananaWeigher = WeigherFactory.getInstance(banana);
bananaWeigher.weigh(spinach); // Compiles, but will produce a runtime error!

- WeigherFactory Plant , WeigherFactory. ,

public class WeigherFactory {
   public static BananaWeigher getInstance(Banana banana) {
      return new BananaWeigher();
   }
   public static SpinachWeigher getInstance(Spinach spinach) {
      return new SpinachWeigher();
   }
}

public abstract class FactoryUser<P extends Plant> {
    /**
     * Method that knows how to call the appropriate WeigherFactory.getInstance method
     * Defined by concrete implementations that know the full plant type.
     */
    public abstract Weigher<P> getWeigher(P plant);

    // common code for all FactoryUsers
    public void run(P plant) {
        Weigher<P> weigher = getWeigher(plant);
        int weight = weigher.weigh(plant);
        System.out.println("the plant weighs: " + weight);
    }
}

public class Main {
    public static void main(String[] args) {
        Banana banana = new Banana();
        FactoryUser<Banana> factoryUser = new FactoryUser<Banana>() {
            // We know the generic type to be Banana now, so we can call the 
            // appropriate WeigherFactory.getInstance method.
            // This technique is known as double dispatch
            @Override
            public BananaWeigher getWeigher(Banana banana) {
                return WeigherFactory.getInstance(banana);
            }
        };
        factoryUser.run(banana);    
    }
}

( )

PlantWeigher, , Plant. . , . , .

Visitor , , . .

public interface Visitor<T> {
    public T visit(Banana banana);
    public T visit(Spinach spinach);
}

public class WeighingVisitor implements Visitor<Integer> {
    @Override
    public Integer visit(Banana banana) {
        return banana.getBananaWeight();
    }
    @Override
    public Integer visit(Spinach spinach) {
        return spinach.getSpinachWeight();
    }   
}

public class PlantWeigher {    
    public int weigh(Plant plant) {
        return plant.accept(new WeighingVisitor());
    }
}

-, Plant, , Visitor. Plant .

public interface Plant {
    public <T> T accept(Visitor<T> visitor);
}

public class Banana implements Fruit {
    private int weight;
    public Banana(int weight) {
        this.weight = weight;
    }
    public int getBananaWeight() {
        return weight;
    }
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this); // the implementation is always exactly this
    }
}

, :

List<Plant> plants = asList(new Banana(10), new Spinach(20));
PlantWeigher weigher = new PlantWeigher();

int weight = 0;
for (Plant p : plants) {
    weight += weigher.weigh(p);
}

System.out.println("total weight: " + weight); // expect 30
+1
source

All Articles