99 Bottles of Beer/Java/Object Oriented

From Rosetta Code

Another solution for 99 Bottles of Beer, which in addition correctly handles the grammar. This solution is object-oriented. It is completely overkill for this problem.

/*************************
 * Interface for things. *
 *************************/
interface Thing {
    String singular();
    String plural();
}

/***************
 * Containers. *
 ***************

 Containers are things which can contain other things. The
 following class makes any thing into a container. The container
 class is actually a decorator which makes any thing into a
 container. Note that the contained thing is actually mutable,
 even if the container is not. Note that the container can only
 contain a single thing; if it shall contain several things, make
 it contain a collection instead. */
class Container implements Thing {
    /** The format gives the name. %self% is replaced by the containing
     *  object's name (in proper pluralization), %contained% is
     *  replaced by the contained object's name. */
    private final String format;
    private final Thing self;
    private final Thing containedThing;

    public Container(String fmt, Thing what, Thing contained) {
        format = fmt;
        self = what;
        containedThing = contained;
    }

    public String singular() {
        return format.replace("%self%", self.singular())
            .replace("%contained%", containedThing.singular());
    }

    public String plural() {
        return format.replace("%self%", self.plural())
            .replace("%contained%", containedThing.singular());
    }
}

/*********************************
 * A collection of equal things. *
 *********************************

 In the context of this program, a collection of things is again
 considered a single thing.
 This is a concrete class. */
class EqualCollection implements Thing {
    private int countOfThings;
    private final Thing typeOfThing;

    public EqualCollection(int count, Thing what) {
        countOfThings = count;
        typeOfThing = what;
    }

    /** get singular. The singular of the collection is just the number
     *  followed by the thing, proper pluralized. The fact that it's
     *  grammatically still a plural form doesn't matter for the problem
     *  at hand. */
    public String singular() {
        StringBuilder sb = new StringBuilder();
        sb.append(countOfThings + " ");
        if (countOfThings == 1)
            sb.append(typeOfThing.singular());
        else
            sb.append(typeOfThing.plural());
        return sb.toString();
    }

    /** get plural. For collections, the plural is just "times " followed
     *  by the singular. That is 3 collections of 4 bottles each give 3
     *  times 4 bottles.
     *  This has to be implemented, even if it isn't used,
     *  because the it is specified by the interface, and this
     *  class is not abstract. */
    public String plural() {
        return "times " + singular();
    }

    /** tell if there are still things to take away. There are things to
     *  take away if there are more than 0 things. */
    public boolean thereIsSomeLeft() {
        return countOfThings > 0;
    }

    /** this takes one thing away from the collection. Taking a thing
     *  away from an empty collection is undefined behaviour (i.e. not
     *  explicitly checked). */
    public void takeOneAway() {
        --countOfThings;
    }
}

/************
 * The beer *
 ************/
class Beer implements Thing {
    public String singular() { return "beer"; }
    public String plural() { return "beers"; }
}

/**************
 * The bottle *
 **************/
class Bottle implements Thing {
    public String singular() { return "bottle"; }
    public String plural() { return "bottles"; }
}

/************
 * The wall *
 ************/
class Wall implements Thing {
    public String singular() { return "wall"; }
    public String plural() { return "walls"; }
}

/** this is the class for the song. */
public class Song {
    private final Thing beverage = new Beer();
    private final Thing drinkSource = new Bottle();
    private final Thing bottleOfBeer =
        new Container("%self% of %contained%", drinkSource, beverage);
    private final EqualCollection collectionOfBottles;
    private final Thing bottleStorage = new Wall();
    private final Thing wallOfBottles;

    public Song(int bottleCount) {
        collectionOfBottles =
            new EqualCollection(bottleCount, bottleOfBeer);
        wallOfBottles =
            new Container("%contained% on the %self%", bottleStorage, collectionOfBottles);
    }

    public void sing(java.io.PrintStream where) {
        while (collectionOfBottles.thereIsSomeLeft()) {
            where.println(wallOfBottles.singular() + ".");
            where.println(collectionOfBottles.singular() + ".");
            where.println("Take one down, pass it around.");
            collectionOfBottles.takeOneAway();
            where.println(wallOfBottles.singular() + ".");
            where.println();
        }
    }

    public static void main(String[] args) {
        Song song = new Song(100);
        song.sing(System.out);
    }
}