Like many aspects of our lives that we view transparently, when an election comes and we decide to vote, we have the luxury of going to our local polling place — or even just submitting an absentee ballot — casting our vote, and not thinking too much about the work that goes into managing the election. But elections pose a significant logistical challenge. Ballots must be designed and printed. Voter registration lists must be maintained. Only registered voters can be allowed to vote, and each must be allowed to vote only once. It must not be possible to figure out which voters placed which votes. Ultimately, votes must be counted and results disseminated. As with many logistical challenges, the burden of handling elections is increasingly being placed on computers; these kinds of logistics are among the things that computers handle best, though automated solutions present their own problems when not designed and built carefully.
In recent years, various on-paper voting mechanisms have been replaced with electronic voting machines. This project will let you explore the design and implementation of software for a highly simplified voting machine that allows users to cast votes for one race (say, Mayor of Simpleton) and counts the number of votes for each candidate. I should be careful to point out that the design of a viable real-world voting machine requires a fair amount of care; it is important to note that our voting machine lacks a number of important features — not the least of which is some form of security mechanism — that are essential in a realistic one.
Our program is the software for a simplified voting machine with a graphical user interface (GUI). When started up, it reads information about a single race from an input file, then displays a ballot. The ballot can be used to cast votes indiscriminately — there's nothing in our program that forces users to "log in" or otherwise identify themselves before casting a vote — with the votes being counted underneath the covers, but not displayed to the user while the voting is in progress. After closing the ballot window, the program saves the results of the election into an output file, formatted in a particular way.
To give you a more realistic context for your project, I've implemented the complete GUI; I've given you the Java code for the classes that comprise the GUI, but you will not need to read, understand, or modify that code in any way. (Still, you can check it out if you're interested. Even a cursory glance will demonstrate that coding up even simple user interfaces like this one can be a complicated process!)
I've also provided a skeleton implementation of the "model," the classes that implement the underlying engine for the program, which handles tasks like reading the input file, writing the output file, and counting the votes. You are not permitted to modify the signatures of the methods that I've provided in the engine classes. This restriction is motivated by one practical need: my GUI code expects these methods to have the same names, take the same kinds of parameters, and behave (outwardly) exactly as specified. If you alter the signatures of these methods, the GUI code will no longer compile or work. (Note that you can feel free to add new methods to the classes I provided; you just can't change the signatures of the methods I provided.)
The starting point is available in a Zip archive.
You'll be writing code in two classes:
Each of the methods in these two classes are commented but not implemented; your job will be to fill in the implementation of all of these methods. You do not need to read, understand, or modify any of the other code. (You're welcome to read through it if you'd like, but you are not permitted to modify it.)
As you work on this project, you and I have made an agreement, a contract, of sorts. I've provided the GUI in its entirety; in return, you're required to write your code according to the provided specification. If your code deviates from the specification — say, a method returns null when it's not supposed to — it's entirely possible that my GUI will behave unpredictably or even crash.
(This illustrates an important point about building large-scale software: When many people work together on a large project, it's important that they agree on how their parts will communicate with one another. In this case, you and your partner are collaborating with me — my GUI depends on your engine — and our agreement is that your engine conforms to the specification provided. Naturally, the larger the software and the more people and pieces involved, the more important these kinds of agreements become.)
If you're not sure whether your methods follow the specifications, one way to solve that problem is to test them. We'll discuss testing a bit later in the quarter, and you'll gradually be required to demonstrate more testing on each project. For now, you can write code that creates objects, calls methods on them, and prints their output to the console, so that you can check whether the output is correct. It would be wise to write your tests in a separate class with a main method, so you can execute the program itself normally, or execute the tests by running your test class instead.
The program reads ballot information from an input file, an example of which follows:
Mayor of Simpleton
3
Joe Incumbent;Powerful Party
Mark Challenger;Less Powerful Party
Gene Unpopular;Nobody Party
The first line of the input file specifies the name of the office that is being voted upon. The next line consists of a number that specifies how many candidates are running for the office. If that number is n, the next n lines each specify a candidate, with the candidate's name appearing on the line first, followed by a semicolon, and followed by the candidate's party affiliation.
You may assume that the input file will always be properly formatted according to this specification. If it's not, it's fine for your program to misbehave or even crash; we will only test your program with valid input files.
In this program, you won't just be printing unformatted text to the console using System.out.println; you'll instead be writing a nicely formatted output file that indicates the results of the election. (Unrealistic as it may be in any but the most local of elections, we'll assume that there is only one voting machine being used by all voters.) An example of the output format follows: See image.
The details of the output format are:
When you want to write formatted output, with left- and/or right-justification within certain numbers of characters, the String.format() method helps; it knows how to take data of various types, format it according to your specifications, and return it to you as a String. The first parameter you pass to it is called a format string, which is used to tell it how you'd like the data to be formatted, with placeholders for the data. Subsequent parameters specify the data that will replace the placeholders. Here's an example from a hypothetical Person class: See image.
This code might generate the following output for a hypothetical person and print it to the console: See image.
In the format string, each occurrence of the % character indicates a placeholder for a piece of data. It is followed by a letter such as s (for a String) or d (for an integer), with an optional number in front of it that indicates justification (a positive number means to right-justify, while a negative number means to left-justify). In the example code above, you can see the placeholder %-10s, which means to print a String left-justified within 10 characters (i.e., with extra space added after it so that it fills up at least 10 characters). The placeholder %4d means to print an integer right-justified within 4 characters (i.e., with extra space added before it, if needed). The placeholder %s simply means to print a String, with no extra spaces added before or after it.
If the format string has two placeholders in it, as in the example code above, you'll need to supply it with two parameters that supply the data. For example, this line:
s += String.format("%-10s: %4d\n", "Height", height);
...tells the method to left-justify the text "Height" within 10 characters, follow that by a colon and a space, and follow that with the integer height right-justified within 4 characters. The \n you see at the end of the format string says to insert a newline character, so that subsequent characters will appear on the next line when the String is printed.
This is a great tool that you can use to format your output to make it look nice; I expect you to use it to format your output as specified.
You will find some or all of the following classes and methods in the Java library useful. Sometimes, you'll find that you need to know details about how they work that we haven't yet talked about in class. When you need more information about them, see the Java API documentation, which describes all of the classes and methods in the Java library. (There is obviously much more documentation that you'll ever have the time to read; the trick with documentation, when there's as much of it as there is in the Java API, is to know what you're looking for and focus on that, rather than trying to read everything.)
Note that you may well be able to finish this project without using everything on the list above. Java has a large, industrial-strength library, which means that there are often many ways to accomplish the same goal. The ones I've listed above are the ones that I think present the simplest path to a solution, but your prior experience may have turned you on to different choices; for the most part, that's fine.
Perhaps the simplest way to test a program is to execute it and observe its output for various inputs. For a program such as this one, whose input comes from a user interface, in addition to an input file, this means running the program, trying various combinations of inputs by hand, and observing the program's behavior. The primary advantage of such a testing strategy is that it doesn't require you to write additional code. On the other hand, the need for you to actively run the test cases — by typing and/or clicking your mouse one step at a time — and assess the output by reading it and comparing it to the expected output is a major disadvantage, particular with regard to the repeatability of the tests. In other words, if you want to run the tests several times as you debug your program, you'll have to manually execute your tests each time, manually comparing the output to the expected output. Still, for our relatively simple program, this strategy will suffice. As the quarter progresses, we'll discuss and use better testing strategies.
To satisfy the Testing portion of this project, you'll write a test plan. A test plan is a series of test cases, each of which is specifically chosen to demonstrate that some particular portion of the program works correctly for a particular class of input. For example, here is a test case which addresses the problem of attempting to deposit a negative amount of money into a savings account using an ATM: See image.
You should format your test cases similarly to the style above, including Test Case, Purpose, Steps, and Expected Output sections. If the contents of the input file are relevant to a particular test case — they often will be — include a section Input File that shows the input file's contents. If you do not have any additional comments to make for a particular test case, you may leave the Additional Commentssection out.
You may use any software you'd like to write your test plan, but you may not handwrite it. Submit it in either Microsoft Word format (.doc), Rich Text Format (.rtf), PDF format, or as a text file. Unfortunately, if we can't open your document, we won't be able to grade it, so please be sure to use one of these four formats.
Note, also, that you are required work on your test plan using the same "pair programming" technique that you use for writing your code. As with programming, two heads are better than one when it comes to testing.
Take a few minutes when writing your test plan to make sure that it's written clearly; we're certainly not looking for fine literature here, but a few minutes spent cleaning up the grammar and spelling will be much appreciated, and will be reflected in the score you receive.
There are three kinds of test cases that I'd like you to focus your attention on: