Write a method called licencePlate that takes an array of objectionable words and returns a random licence plate that does not have any of those words in it. Your plate should consist of four letters, a space, then three numbers.

4 Answers
4

Don't be afraid of whitespace -- your code is a little hard to read due to the lack of new lines. Also, it's fairly standard to indent method names inside of classes.

Always use descriptive names: String[] a tells me nothing about what that parameter is.

When x is a bool, x == true is equivalent to x.

On an extremely picky note, contains seems like a more natural variable name for contain (grammatically anyway)

It doesn't really matter when there's only 4 characters (so 4 * A.length runs), but when doing a linear comparison, you should typically bail out of it with break or continue

Also, you might want to look into indexOf, contains and equalsIgnoreCase

Obviously this is just a little homework assignment, so it would be a bit overkill, but I would put the license plate generation in its own class in a real application.

I would also pass the banned words as a parameter to the constructor instead of to a method -- that allows your instance to carry around the words without any farther down consumers having to know what the words are

Imagine that you make a machine that's used in the DMV. Imagine that this machine has a "generate license plate" button. When pressed, a screen simply shows the plate.

Passing the words to the method is like having to key in the banned words every time the button is pressed -- the DMV employee shouldn't be responsible for knowing/controlling those words

Storing the words inside of an instance (whether set with a setBannedWords method or passed to the constructor) is like configuring the machine at creation. The DMV employee no longer has to know what the words are, or be responsible for entering them. Years could go by before an employee realized "Oh my, I just noticed that the machine has never output a cuss word!"

For completeness: A third option would be to not filter the words -- it would then be up to the employee to go "Hrmmm, 'FART 743' is probably a bad license plate."

Ok, so does this work? Probably. It looks correct to my rusty-with-Java-eye anyway.

The easiest way to test it would probably be to whittle down the set of possible characters from A-Z to A-D and then make ABC a bad word. After a lot of test runs, you could be fairly certain that it was throwing out any ABCX or XABC.

This just made me realize: did your professor specify that the bad words would always be four characters? If not, your code is wrong.

So how could this be tested more properly-ish? Unfortunately it requires that the code become a lot longer. Your professor would probably think you got a bit carried away if you truly made this testable. Despite a fairly simple requirement, to test this with granularity would require breaking it down into quite a few pieces.

I can code this up if you want, but for the sake of brevity and time, I'll just describe it here.

The main problem with testing this is that all of the concerns are mixed together. You have a few concerns:

Generating a license plate

Generating the text part

Generating the number part

Filtering a license plate

Note that separating this concerns allows you to test them independently.

The second option is easier to test because it allows you to pass in a license plate generator that is rigged to generate bad words.

The text and number generation should arguably be separated. That would allow for easily reusing generated numbers when a bad word is in the word part, but other than that, the added complexity would probably not be worth it.

Note how this is transparent to any consuming class. A consuming class doesn't need to know whether it has a LicensePlateGeneratorRandom or LicensePlateGeneratorFiltered; it just needs to know it has a LicensePlateGenerator.

Note that this allows you to test very easily. Testing your random generator means just checking a few outputs. Checking your filter could be done by rigging a generator:

(I would probably define this as an anonymous class in actual testing code, but my Java is too rusty to remember the syntax for that :p)

public class LicensePlateGeneratorFake implements LicensePlateGenerator {
private final List<String> words;
private Iterator<String> currentPos;
public LicensePlateGeneratorFake(List<String> words) {
this.words = words;
currentPos = this.words.iterator();
}
public function generateLicensePlate() {
if (!currentPos.hasNext()) {
//Something should happen here...
//could always just loop back around, or it
//might also be worth considering having the interface
//declare a certain exception as being possible in this method.
//(That would also allow a way out of the infinite loop in the
//(filter generator)
//throw new LicensePlateGeneratorException("...");
//(that could be the base class, and then a LicensePlateGeneratorInfiniteLoopException
//could be thrown in the filter class)
}
//Obviously the end of the iterator should be checked, but I'm lazy
return currentPos.next() + " 123";
}
}

You then configure this fake generator to generate a certain stream of words ("ABCD", "DCBA", etc). Then, you pass this fake generator to the filter, and give the filter a list of words you know that the fake generator will generate.

If you tell the fake generator to generate "ABCD" and you tell the filter to reject "ABCD", you can then test success by whether or not the filter generator returns "ABCD" in any of the generations.

An alternative approach would be to have the filter not be a generator. Instead, your consuming code would be responsible for configuring its own filters and then generating plates until a suitable one is found.

Both designs have fairly strong pros and cons.

(Note: I completely bastardized the class names in my examples. In a real application, namespaces should be used. The code formatting on here makes namespaces a bit clumsy though, so I didn't use them.)

Holy, it took me so long to read all of your writings... I thank you very much for your efforts. By the way, for my question bad words would always be four characters. This is grade 12 computer science, that's why rules are simple :)
–
cook cookOct 19 '12 at 1:46

Some small notes (actually the last few are more or less small comments to Corbin's great answer):

The licensePlate method should validate its input parameters. Does it make sense to call it with null? If not, check it and throw a NullPointerException. (Effective Java, Second Edition, Item 38: Check parameters for validity)

The code contains lots of magic numbers. Using named constants instead of numbers would make the code more readable and less fragile. If you have to modify their value (for example, you need three-letter, three-digit plates instead of four-letter, three-digit plates) it's easy to forget to do it everywhere.

An endless while loop with a return seems more natural and easier to read to me than the do-while loop: