Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: everything in a class is static - is this reasonable?

Tags:

java

I 'm just wondering if what I'm doing is somehow poor design.

I have a ArrayList of things. I need this list to always exist. I only need to have one of these lists. I also have some methods to interact with this list. Thusly, I made everything static.

The thing is that since all of these things are tucked away into a single class, literally everything in that class is declared as static. Which seems a bit odd, because it's like I want to have the entire class be static.

The facts that Java doesn't allow me to make an entire class static and that I was taught to minimize static methods in my code are setting off a few alarm bells in my head, but I honestly can't see any rational reason why what I'm doing won't work well.

EDIT: A bit more about the program and why I decided to do what I did, because I guess that would help (and it was asked, of course).

The center of the program are two databases, one for items and another for characters. Characters need to have temporary posession of items, but all items must be able to be listed at all times.

I decided I would have an arraylist of items, each item having a boolean marking it available or not available (making it easy to display both all items and available items). Each character would have their own, smaller arraylist of items, to which I would add duplicates of the item from the database.

To be able to access the database from other classes (this is where I started with the idea), I considered my easiest option to simply make the large arraylist static because there is no situation where I need it to not exist and there is no situation where I need more than one. Of course, as I made the list static, I needed to make all of the basic methods of interacting with it static as well.

I'm pretty sure there are better ways of doing what I'm trying to do, but I'm just a beginner practising.

EDIT2: Oh, and the list of items will be added to, removed from, and it's items modified while the program runs. Another effect of the characters receiving copies of items is that their own items will remain the same as long as they have them.

like image 697
Klaabu Avatar asked May 20 '13 02:05

Klaabu


1 Answers

If you need exactly one list of Things in your system, you need to ask yourself what these things are. Are they items that can be configured before each run? Are they things that will change as the user executes the program? Perhaps these things should be stored as records in a database, even a lightweight in-memory one like Apache Derby or a NoSQL database.

Even if you genuinely have a fixed set of items, you should consider using a dependency injection system and vending the list in singleton scope instead of using a hardwired singleton. This way you can replace the list in your test classes by changing the configuration.

If you find that last confusing, consider this instead. Suppose you had a class that handles Things, say by keeping a static list of them inside it. Something like:

public class ThingCatalog {
    private static final List<Thing> things = new ArrayList<>();
    public ThingCatalog() {
        // initialize list of things.
    }
    public List<Thing> getThings() {
        return things;
    }
    public Thing getThingWithId(int id) {
        // ...
    }
}

Now, you could make ThingCatalog a singleton class; you've seen how to do it. Make the constructor private, and create a static getInstance method. You'd be tempted to write

public class TreasureGenerator {
    private ThingCatalog things = ThingCatalog.getInstance();
    // ...
}

What happens if you want write unit test for method in this class that don't use things? You don't need the things, so you don't really need ThingCatalog at all. Unforunately, you're stuck with it.

You can start to fix this by giving TreasureGenerator a setThingCatalog method:

public void setThingCatalog(ThingCatalog things) {
   this.things = things;
}

Of course, you only have one ThingCatalog, so that doesn't help much. But if you had an interface that ThingCatalog could implement:

public interface ThingVendor {
    List<Thing> getThings();
    Thing getThingById(int id);
}

and all your classes used ThingVendor instead of ThingCatalog, you could replace it in your tests.

Here's a more business-like example. You are writing a financial program, and you need have it print today's date on checks. Typical is to write code like:

String recipient = ...;
String accountOwner = ...;
BigDecimal amount = ...;
String accountNumber = ...;
Date today = new Date();
printCheck(...);

Now, somebody asks you "Can your program handle leap days correctly?" Fourteen years ago, the question might have been about Y2K. How would you test this? You're stuck with today in this method.

Instead, you write an interface called DateGenerator:

public interface DateGenerator {
    Date today();
}

public class TodayGenerator implements DateGenerator {
    public Date today() { return new Date(); }
}

public class LeapDayGenerator implements DateGenerator {
    public Date today() {
        Calendar cal = Calendar.getInstance();
        cal.set(2016, FEBRUARY, 29); // assume static imports;
        return cal.getTime();
    }
}

Your class would have a setDateGenerator method, and you'd use TodayGenerator normally. In your leap day test, you'd use the LeapDayGenerator.

A dependency injection system automates these processes. What you will learn with experience if you stay with computing is that objects should not know how to configure themselves. Other parts of the project should glue objects together.

like image 131
Eric Jablow Avatar answered Sep 27 '22 21:09

Eric Jablow