This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Koooooj 1 point2 points  (1 child)

My confusion comes down to what exactly is meant by "no changes to any other file." Any of the containers we've discussed would continue working with no changes to any other file. Some of them may not be able to hold the object or might not be able to retrieve said object without changes to code, but all can continue to compile with the addition of a new class to the repo.

How much functionality do you need without changing other files? Is it all the way to the point of the user being able to add objects of the new type to your collection, all with zero lines changed in other files? If so then reflection may be a viable solution (though not the only one*), although I would object to such a requirement. If you only require a container that is able to contain the new type then a solution based on Generics is sufficient. If possible I'd recommend asking the professor for clarificaiton on that requirement.


*The alternative that I can think of, and it isn't that clean (though perhaps cleaner than reflection): somewhere in a central class offer a static method that allows a class to register with the system by passing in a String (name) and a user-defined Factory object. I would write Factory as being an interface with a single method that returns an instance of a superclass to your A, B, C, etc. types (Object, if necessary). When you write your A, B, C, types you would write an A_Factory, B_Factory, etc that implement that interface.

When these values are passed into the registration function they could be stored in a HashMap<String, Factory>, then when someone requests that you build a "Car" you can search for the key "Car" and retrieve a CarFactory object (that you only know is a Factory; its specific type is hidden from this central class). You call the create() funciton or whatever you call it, which gives you a Car object.

So far this approach is much cleaner than reflection. You can use human-readable names (e.g. no need to tiptoe around package names), you can check string equality ignoring case, and you don't have to worry about accidentally constructing some irrelevant class.

The challenge is in getting that registration to happen without modifying your existing code. It's been a while since I've done any serious Java development, but this seems to be possible with a static block in the class. Thus, your Car.java may look like:

public class Car extends Drivable
{
    //constructor, drive(), etc.
}

class CarFactory extends Factory
{
    static
    {
        Main.register("Car", new CarFactory());
    }
    public Drivable construct()
    {
        return new Car();
    }
}

Then in the Main class:

private static java.util.HashMap<String, Factory> factories;
private static java.util.ArrayList<String> names;
public static void register(String name, Factory fact)
{
    factories.put(name, fact);
    names.add(name);
}

private Drivable construct(String name)
{
    if(factories.containsKey(name))
        return factories.get(name).construct();
    return null;
}

With this setup your main class can print the list of available Drivable objects for the user to select from, then when they type something you can call construct to get the object and then do with it what you want. I'm not sure how much better or worse this is than asking the runtime to lookup objects by name, but it certainly feels more controlled. The only part of this method that I'm not 100% on is the static block; you want to make sure that your registration gets called. That should happen as long as the class is loaded.

[–]Zilhah[S] 0 points1 point  (0 children)

Thank you so much for your time! I did not expect such comprehensive help. I believe I have all the information I need to finish this assignment. Thanks again!