Skip to content
Ameen Abozaid
PublishedBEG-019-L05

Demo: rewriting a procedural bank as objects

  • Lesson 5 of 5
  • 12 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

This is the module's demo. You will take the parallel-array bank from lesson 1 and rewrite it, step by step, as an Account class with objects. Along the way you will use the noun/verb hunt from lesson 4 to decide what the class contains, and you will make the planned mistake of leaving deposit outside the class, so you can see exactly why behavior should live with its data.

Follow along in your own IDE or editor if you can: type each version, compile, and compare your output with the output shown here. Menu names and keyboard shortcuts differ between IDEs and operating systems, so use the run option of your own tool. The Java syntax for classes is a preview: the next module teaches it properly, so do not worry if some lines feel new.

Why this matters: this rewrite is the pattern of almost every backend refactoring, and the practice run for the module's lab, "Noun and verb hunt".

This lesson builds on all four previous lessons of the module.

Learning Objectives

By the end of this lesson you will be able to:

  • Start from a procedural program and list the nouns and verbs of its description.
  • Create an Account class with fields for owner, number, and balance.
  • Move deposit, withdraw, and show into the class so they work on the object's own balance.
  • Recognize and correct the mistake of keeping behavior outside the class.
  • Compare the procedural and object-oriented versions and say what improved.
  • Show how one new property touches fewer places in the object-oriented version.

Written Explanation

Step 1: the procedural version

The description: "The bank has accounts. Each account has an owner, a number, and a balance. Customers deposit and withdraw money, and an account can be shown."

Here is the procedural program, with the money in cents:

java
public class BankProc {
    static void deposit(int[] balances, int i, int amount) {
        balances[i] += amount;
    }
    static boolean withdraw(int[] balances, int i, int amount) {
        if (amount > balances[i]) {
            return false;
        }
        balances[i] -= amount;
        return true;
    }
    static void show(String[] owners, int[] numbers, int[] balances, int i) {
        System.out.println(numbers[i] + " " + owners[i] + ": " + balances[i]);
    }
    public static void main(String[] args) {
        String[] owners = {"Ana", "Omar"};
        int[] numbers = {1001, 1002};
        int[] balances = {10000, 5000};
        deposit(balances, 0, 2500);
        boolean ok = withdraw(balances, 1, 9000);
        System.out.println("Withdrawal accepted: " + ok);
        show(owners, numbers, balances, 0);
        show(owners, numbers, balances, 1);
    }
}

Output:

text
Withdrawal accepted: false
1001 Ana: 12500
1002 Omar: 5000

Ana's deposit worked. Omar's withdrawal of 90.00 was refused because he only has 50.00. Notice how every call passes arrays and an index, and show needs three arrays.

Step 2: nouns and verbs

From the description: nouns are bank, accounts, owner, number, balance, customers, money. Verbs are deposit, withdraw, show.

NounDecisionReason
accountclassHas state (owner, number, balance) and behavior
owner, number, balancefields of AccountSimple values describing an account
moneynot a classJust an amount in cents (an int)
customersnot a class yetHere a customer is only the account's owner name; a Customer class would come if the text gave customers their own data and actions
banknot a class yetThe program has no data or behavior for "the bank" itself in this text; it is just the whole program

Verbs deposit, withdraw, and show all change or read an account's own balance, so they belong to Account. One class results, and that is a fine outcome. Rejecting Bank and Customer here is a decision for this text only.

Step 3: create the Account class

Three arrays become three fields in one class:

java
class Account {
    String owner;
    int number;
    int balance;
}

Step 4: move the behavior in

Each loose method becomes a method of Account. The array and the index vanish: the method now works on this object's balance.

java
class Account {
    String owner;
    int number;
    int balance;
    void deposit(int amount) {
        balance += amount;
    }
    boolean withdraw(int amount) {
        if (amount > balance) {
            return false;
        }
        balance -= amount;
        return true;
    }
    void show() {
        System.out.println(number + " " + owner + ": " + balance);
    }
}

The rule "no withdrawal above the balance" is now inside withdraw, next to the balance it protects.

Step 5: the objects in main

java
public class BankObj {
    public static void main(String[] args) {
        Account ana = new Account();
        ana.owner = "Ana";
        ana.number = 1001;
        ana.balance = 10000;
        Account omar = new Account();
        omar.owner = "Omar";
        omar.number = 1002;
        omar.balance = 5000;
        ana.deposit(2500);
        boolean ok = omar.withdraw(9000);
        System.out.println("Withdrawal accepted: " + ok);
        ana.show();
        omar.show();
    }
}

Output (compiled together with the Account class):

text
Withdrawal accepted: false
1001 Ana: 12500
1002 Omar: 5000

Identical output, different structure. The calls ana.deposit(2500) and ana.show() say what they mean: no arrays, no index.

One honest observation: this main is not shorter than before, because we still fill in each field by hand (about 25 lines for the procedural file against about 35 for the whole object file). The setup lines will shrink in the next module, where constructors let you create an account and set its values in a single line. The calls that use the accounts are already shorter and clearer.

Step 6: the planned mistake

Here is a tempting half-measure. You create the Account class but leave deposit outside, as a static method that takes the account as a parameter:

java
public class BankMistake {
    static void deposit(Account a, int amount) {
        a.balance += amount;
    }
    public static void main(String[] args) {
        Account ana = new Account();
        ana.balance = 10000;
        deposit(ana, 2500);
        ana.balance = -1;              // still nothing guards the data
        System.out.println(ana.balance);
    }
}
class Account {
    String owner;
    int number;
    int balance;
}

It compiles and runs, and prints -1. The data is now grouped in one object, which is an improvement, but the behavior is separated from the data it acts on. deposit is a loose method any file can have a copy of; the account does not know how to deposit; and because rules live elsewhere, a different part of the program can write -1 freely. This is the module's common mistake: a class that is only a bag of fields, with all logic in outside functions, gives you little more than the parallel arrays did.

The fix is Step 4: move deposit inside the class so the object manages its own balance. (Making fields impossible to write from outside is a topic for a later module; at this point moving behavior in is the first step.)

Step 7: adding currency

Now the change that hurt in lesson 1. Add one field to the class and print it in show:

java
    String currency = "SAR";
    // in show():
    System.out.println(number + " " + owner + ": " + balance + " " + currency);

Compiling and running the object version with exactly these two edits gives:

text
Withdrawal accepted: false
1001 Ana: 12500 SAR
1002 Omar: 5000 SAR

Two lines changed. In the procedural version, a new array, new parameters on show, and updated calls were required. The currency = "SAR" assignment on the field is a default value that every new account starts with; the next module explains it properly. (SAR is used here only as an example currency code.)

Examples

Example 1: side-by-side comparison (simple)

QuestionProcedural (BankProc)Object-oriented (BankObj)
Where is one account's data?Spread over three arraysIn one Account object
How do you deposit into Ana's account?deposit(balances, 0, 2500)ana.deposit(2500)
Where does "no withdrawal above balance" live?In a loose method that must be given the right array and indexInside Account.withdraw, next to the balance
What does a caller need to know?Which arrays and which indexWhich object

Example 2: spot the mistake (practical)

Look at this snippet and decide whether it follows the module's advice:

java
static void withdraw(Account a, int amount) {
    if (amount <= a.balance) {
        a.balance -= amount;
    }
}

It does not. The method is correct in itself, but it sits outside Account and takes the account as a parameter. Move it inside the class, remove the Account a parameter, and use balance directly. Calling code changes from withdraw(ana, 500) to ana.withdraw(500).

Example 3: a third account for free (realistic)

In the procedural version, adding a third customer means adding an element to three arrays, and one forgotten array silently breaks things. In the object version it is one object:

java
Account sara = new Account();
sara.owner = "Sara";
sara.number = 1003;
sara.balance = 20000;
sara.deposit(1000);
sara.show();

Expected output of sara.show(): 1003 Sara: 21000. (This snippet follows directly from the compiled version, with the same Account class.) Each object brings all of its data along, so there is nothing else to keep in step.

Backend Insight

This rewrite is what refactoring toward objects looks like in real code bases: a function with a long list of related parameters is a hint that those parameters want to be one object, and the function wants to become its method. When you read code that passes the same four or five values everywhere, ask whether they are really the state of a thing you have not named yet.

Notice also what the demo did not do. It did not add classes for Bank or Customer, because the description gave them no data or behavior. Backend code bases suffer more from too many hollow classes than too few, so the filtering from lesson 4 is as practical as the syntax.

Finally, this version is only the first step. In the next module you will add constructors and make creation safe, and later modules will show how to stop outside code from writing -1. A design improves in rounds, not all at once.

Common Mistakes

Mistake 1: Keeping behavior outside the class

  • Incorrect: static void deposit(Account a, int amount) in a separate file.
  • Correct: void deposit(int amount) inside Account, working on its own balance.

Mistake 2: Making a class for every noun in the description

The bank text had the nouns bank and customer, but neither earned a class. A class that only wraps a name or has no behavior is clutter.

Mistake 3: Copying references and expecting copies

Account b = ana; makes b refer to Ana's object; changing b.balance changes Ana's balance. Use new for a new account.

Mistake 4: Forgetting new

Declaring Account ana; and then writing ana.balance = 100; fails, because no object exists. (The compiler or the run reports an error; the debugging module covers reading it.)

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. In the Step 1 program, which values describe one account, and how many arrays hold them?

Show solution A1

A1. The owner, the number, and the balance: three arrays (owners, numbers, balances).

A2. Which methods of the procedural bank move into Account, and what disappears from their parameter lists?

Show solution A2

A2. deposit, withdraw, and show. The arrays and the index parameter disappear, because the method works on the object's own fields.

A3. In one sentence, why is static void deposit(Account a, int amount) outside the class the planned mistake?

Show solution A3

A3. The behavior is separated from the data it acts on, so rules cannot be kept next to the data and any code can bypass them.

Level B: Application

B1. Rewrite this procedural snippet as a class Book with a method:

java
String[] titles = {"Dune", "Emma"};
boolean[] borrowed = {false, true};
static void borrow(boolean[] borrowed, int i) { borrowed[i] = true; }
Show solution B1

B1.

java
class Book {
    String title;
    boolean borrowed;
    void borrow() { borrowed = true; }
}

B2. Write the call that borrows "Dune" in the object version, and compare it with the procedural call.

Show solution B2

B2. Object-oriented: Book dune = new Book(); dune.title = "Dune"; dune.borrow();. The procedural call is borrow(borrowed, 0); and needs the array and the index.

B3. Add a method isAvailable() to your Book that returns true when the book is not borrowed. Say whether it is state or behavior.

Show solution B3

B3. Example: boolean isAvailable() { return !borrowed; }. It is behavior (a method) that reports state.

Level C: Challenge

C1. Compile and run an Account program of your own with two objects. Make withdraw refuse amounts of zero or less, and show a deposit, an accepted withdrawal, and a refused one. Paste your real output.

Show solution C1

C1. Example: the withdraw method becomes

java
boolean withdraw(int amount) {
    if (amount <= 0 || amount > balance) {
        return false;
    }
    balance -= amount;
    return true;
}

Compile it with your main, and check that the output matches your expectations (for example, a withdrawal of 0 prints false). Your numbers will differ from the lesson's.

C2. Run a noun/verb hunt on: "The school has students and teachers. A student enrolls in a course. The teacher grades the exam." Which classes would you keep? Which nouns would you reject or turn into fields, and why?

Show solution C2

C2. Keep Student, Teacher, Course, and perhaps Exam (it has a grade to remember). Behaviors: enroll (attached to the student or the course), grade (attached to the teacher, or the exam, since the grade is stored with it). Reject school unless the text gives it data. Fields of Exam might include the grade; the answers can vary, and the justification matters most.

Mini Challenge

This is the practice run for the lab. Write a one-page description of a small bank (or use the instructor's) and perform a noun/verb hunt on it.

  1. List every noun and verb.
  2. Table of nouns: keep as class, field, merge, or reject, with one-sentence reasons. Reject at least one noun.
  3. For each class, list state and behaviors, attaching each verb to the class that owns the data it changes.
  4. Write the class skeleton for the most important class, with fields and method signatures only, and say which values are different in two example objects.

Check your work: every decision has a justification, no behavior is listed on a class that does not hold its data, and you can state the difference between the class and your example objects.

Summary & Next Lesson

Key takeaways

  • Rewriting procedural code as objects replaces parallel arrays with one class whose objects hold their own state.
  • The noun/verb hunt decides what to include; most nouns become fields or are rejected.
  • Move the methods that change a balance into the class that owns the balance.
  • Keeping behavior outside the class, passing the object in, loses much of the benefit.
  • A new property such as currency is added in one place.
  • Constructors, next module, will shorten the setup code.

Next lesson

This lesson ends the module. Your next task is the module's lab, "Noun and verb hunt": practice the technique on a written description of a bank. Then the course continues with the next module, whose first lesson is "Writing your first class", where you learn properly the syntax you previewed here.

Self-check

  1. What replaces the three arrays in the object version?
  2. Where should deposit live, and why?
  3. Why did this demo not create a Bank class?
  4. Did main get shorter in this version? Why or why not?
  5. How many places did adding currency touch in the object version?
Show answers
  1. One Account class whose objects each hold an owner, a number, and a balance.
  2. Inside Account, because it works on the account's own balance and the rules should sit next to the data.
  3. The description gave the bank no data or behavior of its own, so a class would have been empty.
  4. Not yet: the setup lines are still written by hand. Constructors in the next module will shorten them, and the calls that use the accounts are already shorter and clearer.
  5. Two lines: one field and the show output.

References

  • Oracle, The Java Tutorials: What Is a Class?. Supports the idea that a class is the blueprint from which objects (instances) are created, each keeping its own state.
  • Oracle, The Java Tutorials: What Is an Object?. Supports bundling state and behavior in objects and the benefit of modularity.
  • All programs, outputs, and the currency change were compiled and run with JDK 21 (javac 21.0.10). The sara snippet and the exercise solutions B1, B3, C1 were not separately compiled.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Type to search levels, phases, modules and lessons.