Exam Practice

MCQs, flashcards, UML scenario diagrams, and refactoring examples covering the four pillars, SOLID, GRASP, DRY/YAGNI/KISS, and code refactoring.

Reference text: Object First with Java, Barnes and Kölling, 6th Ed.

QUICK NAVIGATION(18 of 18)
01

Extract Method

Problem: A method does too many things at once, mixing several concerns into one long block of code.

Solution: Move a cohesive chunk of the method into its own well-named method, then call that method from the original spot.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Book {
    void printDetails() {
        System.out.println(title);
        System.out.println(author);
        System.out.println(isbn);
        System.out.println(price);
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
class Book {
    void printDetails() {
        printIdentity();
        printPrice();
    }

    void printIdentity() {
        System.out.println(title);
        System.out.println(author);
        System.out.println(isbn);
    }

    void printPrice() {
        System.out.println(price);
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:long method becomes a smaller method.
02

Inline Method

Problem: A method's body is exactly as clear as its name, so calling it just adds an extra hop for no real benefit.

Solution: Remove the method and place its one line of logic directly where it was called.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
boolean moreThanFive() {
    return score > 5;
}

int getRating() {
    return moreThanFive() ? 2 : 1;
}
AFTER REFACTORING
RefactoredStructure.javajava
int getRating() {
    return score > 5 ? 2 : 1;
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a small unnecessary method becomes code placed directly.
03

Extract Class

Problem: A single class has grown to cover more than one responsibility, which usually points at a Single Responsibility Principle violation.

Solution: Move the related fields and methods for one responsibility into a new class of their own.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Customer {
    String name;
    String phoneNumber;
    String areaCode;
}
AFTER REFACTORING
RefactoredStructure.javajava
class Customer {
    String name;
    PhoneNumber phone;
}

class PhoneNumber {
    String areaCode;
    String phoneNumber;
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:one class doing too much splits into two.
04

Inline Class

Problem: A class barely does anything on its own anymore and just adds an extra layer between the caller and the real work.

Solution: Move its remaining behavior into the class that uses it, then delete the now unnecessary class.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class ConsoleWriter {
    void writeLine(String text) {
        System.out.println(text);
    }
}

class Report {
    ConsoleWriter writer = new ConsoleWriter();

    void print(String content) {
        writer.writeLine(content);
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
class Report {
    void print(String content) {
        System.out.println(content);
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a useless small class merges into its caller.
05

Move Method

Problem: A method in one class relies mostly on the data of a different class, calling that class's fields over and over.

Solution: Move the method to the class whose data it actually uses.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Order {
    Cart cart;

    double calculateTotal() {
        double total = 0;
        for (Item item : cart.items) {
            total += item.price;
        }
        return total;
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
class Cart {
    List<Item> items;

    double calculateTotal() {
        double total = 0;
        for (Item item : items) {
            total += item.price;
        }
        return total;
    }
}

class Order {
    Cart cart;

    double getTotal() {
        return cart.calculateTotal();
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:behavior belongs with the data it uses most.
06

Move Field

Problem: A field is read and updated by another class more than by the class that declares it.

Solution: Relocate the field to the class that actually owns that responsibility.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Order {
    double discountRate;
}

class PricingEngine {
    double apply(Order order, double amount) {
        return amount - (amount * order.discountRate);
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
class PricingEngine {
    double discountRate;

    double apply(double amount) {
        return amount - (amount * discountRate);
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:data belongs with the class that uses it most.
07

Replace Temp with Query

Problem: A temporary variable simply stores the result of an expression, adding a local variable for something that could just be computed on demand.

Solution: Replace the temporary variable with a method that calculates the value.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
double basePrice = quantity * itemPrice;

if (basePrice > 1000) {
    applyDiscount();
}
AFTER REFACTORING
RefactoredStructure.javajava
if (basePrice() > 1000) {
    applyDiscount();
}

double basePrice() {
    return quantity * itemPrice;
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a temp variable becomes a method (a query).
08

Rename Variable / Method

Problem: A name does not clearly explain its purpose, forcing readers to guess or dig through the code to understand it.

Solution: Give it a meaningful name that describes what it actually holds or does.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
int d;
AFTER REFACTORING
RefactoredStructure.javajava
int daysElapsed;
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a good name means the code explains itself.
09

Pull Up Field

Problem: Multiple subclasses independently declare the exact same field.

Solution: Move the shared field up into the common superclass, so it is declared once.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Dog extends Animal {
    int age;
}

class Cat extends Animal {
    int age;
}
AFTER REFACTORING
RefactoredStructure.javajava
class Animal {
    int age;
}

class Dog extends Animal {
}

class Cat extends Animal {
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a common field moves up to the parent.
10

Push Down Field

Problem: A field lives in the superclass, but only some subclasses actually need it.

Solution: Move the field down into just the subclasses that need it.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Animal {
    int wings;
}

class Bird extends Animal {
}

class Dog extends Animal {
    // does not need wings
}
AFTER REFACTORING
RefactoredStructure.javajava
class Animal {
}

class Bird extends Animal {
    int wings;
}

class Dog extends Animal {
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a specific field moves down to the child.
11

Pull Up Method

Problem: Subclasses each implement an identical method, duplicating the same logic more than once.

Solution: Move the common method up into the superclass so it exists in one place.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Dog extends Animal {
    void eat() {
        System.out.println("Eating");
    }
}

class Cat extends Animal {
    void eat() {
        System.out.println("Eating");
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
class Animal {
    void eat() {
        System.out.println("Eating");
    }
}

class Dog extends Animal {
}

class Cat extends Animal {
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a duplicate method moves up to the parent.
12

Push Down Method

Problem: A method sits in the superclass, but only some subclasses can meaningfully perform it.

Solution: Move the method down into just the subclasses that need it.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Animal {
    void fly() {
        System.out.println("Flying");
    }
}

class Bird extends Animal {
}

class Dog extends Animal {
    // cannot fly
}
AFTER REFACTORING
RefactoredStructure.javajava
class Animal {
}

class Bird extends Animal {
    void fly() {
        System.out.println("Flying");
    }
}

class Dog extends Animal {
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a specific method moves down to the child.
13

Extract Interface

Problem: Several client classes only need a small, common subset of a class's methods, creating an unnecessarily tight dependency on the whole class.

Solution: Create an interface containing just those required methods, and have the class implement it.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Customer {
    double calculateBill() {
        return 100;
    }

    void updateAddress(String address) {
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
interface Billable {
    double calculateBill();
}

class Customer implements Billable {
    public double calculateBill() {
        return 100;
    }

    void updateAddress(String address) {
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:common behavior becomes an interface.
14

Replace Type Code with Subclasses

Problem: A type code or switch statement decides how an object should behave, forcing every new case to edit the same method.

Solution: Create a subclass for each variant and let each one handle its own behavior.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Employee {
    String type;

    double bonus() {
        if (type.equals("ENGINEER")) {
            return 500;
        } else if (type.equals("MANAGER")) {
            return 1000;
        }
        return 0;
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
abstract class Employee {
    abstract double bonus();
}

class Engineer extends Employee {
    double bonus() {
        return 500;
    }
}

class Manager extends Employee {
    double bonus() {
        return 1000;
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:type code becomes a subclass hierarchy.
15

Decompose Conditional

Problem: A conditional statement mixes the condition and its logic together in one hard-to-read line.

Solution: Extract the condition and each branch's logic into clearly named methods.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
if (date.after(SUMMER) && date.before(WINTER)) {
    charges = quantity * summerRate;
} else {
    charges = quantity * winterRate;
}
AFTER REFACTORING
RefactoredStructure.javajava
if (isSummer(date)) {
    charges = summerCharges();
} else {
    charges = winterCharges();
}

boolean isSummer(Date date) {
    return date.after(SUMMER) && date.before(WINTER);
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a complex if becomes small, named methods.
16

Introduce Parameter Object

Problem: A method takes too many related parameters, making calls hard to read and easy to get wrong.

Solution: Group the related parameters into a single object and pass that instead.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
void bookRoom(Date start, Date end,
              int guests, boolean vip) {
    // booking logic
}
AFTER REFACTORING
RefactoredStructure.javajava
void bookRoom(BookingRequest request) {
    // booking logic
}

class BookingRequest {
    Date start;
    Date end;
    int guests;
    boolean vip;
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:many related parameters become one parameter object.
17

Replace Conditional with Polymorphism

Problem: A switch or if statement checks an object's type to decide which behavior to run.

Solution: Give each type its own subclass with an overridden method, so the correct behavior runs through dynamic method dispatch instead of a type check.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
class Duck {
    String type;

    String quack() {
        if (type.equals("MALLARD")) {
            return "Quack!";
        } else if (type.equals("RUBBER")) {
            return "Squeak!";
        }
        return "...";
    }
}
AFTER REFACTORING
RefactoredStructure.javajava
abstract class Duck {
    abstract String quack();
}

class MallardDuck extends Duck {
    String quack() {
        return "Quack!";
    }
}

class RubberDuck extends Duck {
    String quack() {
        return "Squeak!";
    }
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:a switch based on type becomes polymorphism. This connects directly to dynamic method dispatch.
18

Substitute Algorithm

Problem: An existing algorithm works correctly, but a simpler or clearer one could produce the exact same result.

Solution: Replace the old algorithm with the cleaner one while keeping the same behavior.

Code Transformation
BEFORE REFACTORING
LegacyStructure.javajava
boolean containsItem(List<String> list, String target) {
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).equals(target)) {
            return true;
        }
    }
    return false;
}
AFTER REFACTORING
RefactoredStructure.javajava
boolean containsItem(List<String> list, String target) {
    return list.contains(target);
}
UML Class Transformation
BEFORE ARCHITECTURE
Rendering UML diagram...
AFTER ARCHITECTURE
Rendering UML diagram...
MEMORY CUE:same result, better algorithm.