简体   繁体   中英

Java Object selection during runtime

I am relatively new to Java and am trying to overcome a problem.. I have a base class, for this example lets call it a box, and I have 3 more box classes inheriting the base box class. The reason for this is that each of these 3 boxes have different characteristics that differentiate them from each other. Lets pretend that: Box01 can ONLY be made out of cardboard and be between 15 and 30cm long. Box02 can ONLY be made out of aluminium and be greater than 30cm in length. box03 can ONLY be made out of plastic and be less than 100cm in length.

In the program in question the user cannot select the type of box specifically but alternatively must supply their requirements and the program must respond saying if the product is possible. So a plastic box of 120cm is not possible, neither is a 50xm cardboard box.

My current solution is to use if statements such as:

if(boxType == 'cardboard' && length >= 15 && length <= 30){
    Box = new box01(boxType, length);
else if(boxType == 'aluminium' && length >= 30){
    Box = new box02(boxType, length);
}

I know in this simplified example it appears unnecessary to have the additional box classes but assume that in the real version that the boxes have many more unique characteristics which would reflect in the classes attributes and constructor.

Now to my question. This particular method looks ok here with only 2 tests and a few ifs but my program has considerably more attributes and many many more tests. Is there another more sophisticated way for my program to detect what kind of box my program should make besides the ifs.

Thank you for anybody who takes the time to read this, understand it and post a reply. I'm sure the solution is much simpler than I am making it out to be.

A good approach for this would be to structure your application like this:

  1. Use an interface or an abstract class with a basic implementation for de:

     public interface BoxType(){ //Or BoxFactory or smth like that public boolean isApplicable(int userInput1, String userInput2...); /// If you have more than 1-2 user input elements, create a special object (something like `UserInput`) with the user input/requirements and just pass it. public Box buildBox(int userInput1, String userInput2...); ... } 
  2. Implement each box type, defining it's applicability conditions in the isApplicable() Method and how to build it in the buildBox() method.

  3. Wherever you need to check which box type is, just cycle through the existing (registered) Box types and check which one is applicable and build it.

This is what I was talking about:

import java.util.Arrays;
import java.util.List;

enum Material {
    CARDBOARD, ALUMINIUM, PLASTIC
}

class BoxProperties {
    public Material material;
    public double length;
}

abstract interface RuleSet {
    public boolean isSatisfiedBy(BoxProperties props);
}

class MyRuleSet implements RuleSet {
    private final Material material;
    private final double[] length;

    public MyRuleSet(Material mat, double minLength, double maxLength) {
        this.material = mat;
        this.length = new double[] {minLength, maxLength};
    }

    @Override
    public boolean isSatisfiedBy(BoxProperties props) {
        if (props.material != material) return false;
        if (props.length < length[0] || props.length > length[1]) return false;
        return true;
    }

}

public class Box {
    private static final List<? extends RuleSet> rules;
    public final Material material;
    public final double length;

    private Box(BoxProperties props) {
        this.material = props.material;
        this.length = props.length;
    }

    public static Box createBox(BoxProperties props)
            throws IllegalArgumentException {
        for (RuleSet rs : rules) {
            if (rs.isSatisfiedBy(props)) {
                return new Box(props);
            }
        }

        //XXX This should probably be made a checked exception
        throw new IllegalArgumentException(
                "No supported box type can fullfil the requests");
    }

    static {
        rules = Arrays.asList(
                new MyRuleSet(Material.CARDBOARD, 15, 30),
                new MyRuleSet(Material.ALUMINIUM, 30, Double.POSITIVE_INFINITY),
                new MyRuleSet(Material.PLASTIC, 0, 100)
                );
    }
}

If you determine that you need different Box subclasses, then you might make Box abstract, and add a createBox method to the RuleSet interface, which is implemented to return a custom implementation. Also, you might need to pass some extra parameter to the Box constructor, which wasn't in the request, so it's likely that you'll want to add that.

Anyway, I repeat that I don't think you should subclass, because the client class would still see a Box . In order to determine if it can be wrapped, it should make something like

if (returnedBox instanceof MaybeWrappableBox && ((MaybeWrappableBox)returnedBox).isWrappable()) {
    ...
}

Then, you'd better add isWrappable() to all your boxes, and just return false if the box is never wrappable. That would simplify caller code, while not particularly complicating callee code.

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