AP Computer Science A: Class Design FRQs
AI-Generated Content
AP Computer Science A: Class Design FRQs
Mastering the class design question is non-negotiable for success on the AP Computer Science A exam. Historically appearing as Free Response Question 2 (FRQ 2), this problem tests your fundamental ability to model real-world scenarios using object-oriented programming. It’s where you prove you can translate a written specification into a robust, well-encapsulated Java class—a skill that accounts for a significant portion of your FRQ score.
Deciphering the Prompt and Identifying Core Data
Every class design FRQ begins with a paragraph describing a scenario, such as a Hotel with rooms, a Club with members, or a Sentence with words. Your first task is to identify the essential state—the data the object must remember. This directly translates to your private instance variables.
Read the prompt for nouns and their descriptors. If a problem states, "A Book has a title and number of pages," you’ve found two instance variables: a String title and an int numPages. Crucially, you must determine the data type. A list of items implies an ArrayList. A yes/no condition suggests a boolean. A value that can be fractional points to double. For example, a Student with multiple test scores would need an ArrayList<Double> scores. Your choice here sets the foundation for every method you will write.
Constructing the Class Skeleton and Initializing State
Once the instance variables are declared as private, you must provide a way to create an object. This is the constructor. Its purpose is to initialize all instance variables to a valid starting state. The prompt will specify the parameters. Your job is to assign those parameter values to the corresponding instance variables.
A common twist is the need to initialize a collection. If a Playlist starts empty, your constructor would initialize an ArrayList<Song> instance variable to a new, empty ArrayList<>(), even though no Song objects are passed in as parameters. Remember, constructors do not have a return type, not even void. A correctly implemented constructor ensures that no object is ever in an undefined or inconsistent state the moment it is born.
Implementing Accessor, Mutator, and Core Behavior Methods
With the object's state established, you now implement its behaviors, described in the prompt as specific methods. These fall into three broad categories, often mixed within one problem:
- Accessor Methods (Getters): These methods return information about the object's state without modifying it. They typically start with "get" or "is" (for
boolean). For example,public String getTitle()simply returnstitle. Their implementation is usually one line:return variableName;.
- Mutator Methods (Setters): These methods modify the object's state. They often have a
voidreturn type and change one or more instance variables based on parameters. A method likepublic void setPrice(double newPrice)would assignnewPriceto the instance variableprice. Some mutators implement more complex logic, like adding an item to a list or toggling abooleanstate.
- Core Algorithmic Methods: This is where the bulk of the points lie. These methods perform calculations or operations using the object's state. You might need to calculate a total, search a list, or determine if a condition is met. For instance, a
public double getAverageScore()method for aStudentwould loop through theArrayList<Double>scores, sum them, and return the sum divided by the size. Precision matters: usedoublearithmetic when needed, handle edge cases (like empty lists), and always return the specified type.
Adhering to Object-Oriented Design and Encapsulation
Throughout your implementation, you are being evaluated on your adherence to object-oriented design principles, primarily encapsulation. Encapsulation is the bundling of data (instance variables) and the methods that operate on that data within a single unit (a class), while restricting direct access to some of the object's components.
You enforce encapsulation by:
- Declaring all instance variables as
private. - Using method parameters and local variables appropriately. Do not create unnecessary instance variables. If a value is only needed inside a single method for calculation (like a loop counter or a temporary sum), it must be a local variable.
- Interacting with the object's data only through its public methods. Even within the class, you refer to your own instance variables directly (e.g.,
scores.add(x)), but from outside, access would be throughgetScores(). - Ensuring methods have the exact signature (name, parameter types, and return type) specified in the problem. Changing a
public boolean isValid()topublic void checkValid()will cost you points, even if the logic is correct.
Common Pitfalls
Pitfall 1: Creating Superfluous Instance Variables
- Mistake: Declaring an instance variable for something that should be a local variable inside a method, such as a loop index or a temporary sum used in
getAverage. - Correction: Ask yourself: "Does this value define the persistent state of the object, or is it only needed temporarily to perform a single calculation?" If it's the latter, declare it as a local variable within the method.
Pitfall 2: Forgetting to Initialize or Update State
- Mistake 1: Writing a constructor that receives parameters but fails to assign them to the instance variables (e.g., writing
String title = bookTitle;instead oftitle = bookTitle;). - Mistake 2: In a mutator method that adds to a list, calling
addon the parameter instead of the instance variable (e.g.,nameList = otherList; otherList.add(name);). - Correction: In constructors, carefully assign parameter values to the instance variables. In methods, always ensure you are modifying
thisobject's data (this.scores.add(x)or simplyscores.add(x)).
Pitfall 3: Misunderstanding Method Responsibilities
- Mistake: An accessor method like
getListreturns a direct reference to the private instance variable (e.g.,return myList;), which breaks encapsulation because the caller can then modify the internal list. - Correction: Unless the problem specifically requires it, a getter for a mutable object (like an
ArrayList) should return a defensive copy (e.g.,return new ArrayList<>(myList);) or an immutable view. On the AP exam, simply returning the reference is usually accepted but understanding the principle is key.
Pitfall 4: Ignoring Edge Cases in Algorithmic Methods
- Mistake: Writing a
getAveragemethod that divides bylist.size()without checking if the list is empty, which would cause aArithmeticException(division by zero). - Correction: Read the prompt carefully. It often specifies what to return in such cases (e.g., "if no scores have been recorded, return 0.0"). If not specified, a reasonable default (like 0.0) is acceptable. Always consider: "What if the list is null or empty? What if a parameter is invalid?"
Summary
- FRQ 2 focuses on designing a single class from scratch. Success hinges on accurately translating a written description into
privateinstance variables, a constructor, and public methods. - Encapsulation is the governing principle. Instance variables must be
private, and the class's public interface (its methods) should be the only way to interact with an object's data. - Method implementation follows a clear pattern: Accessors return values, mutators change state, and algorithmic methods perform calculations using loops and conditionals as needed. Always match the specified method signature exactly.
- Avoid common mistakes by ensuring all instance variables are initialized in the constructor, not creating unnecessary instance variables, and handling edge cases like empty lists in your methods.
- Practice is essential. Work through past FRQ 2 problems, time yourself, and compare your solutions to the scoring guidelines. This will solidify the pattern and prepare you for the nuances of exam day.