Software Development  ·  Level 6
Object Oriented Programming

Chapter 3: Demonstrate object-oriented programming skills

📚 8 Topics
⚑ What you will be able to do

By the end of this chapter, you will be able to:

  • Implement objects and classes correctly by following proper work procedures.
  • Declare object methods accurately to meet the needs of your application.
  • Apply namespaces correctly to organize your code as required.
  • Use data abstraction concepts effectively to simplify complex data.
  • Implement object encapsulation properly to protect your data.
  • Create class templates that fit the specific requirements of your application.
  • Implement class inheritance accurately to reuse and extend functionality.
  • Apply polymorphism effectively to make your programs more flexible and dynamic.

Mastering these skills will help you build strong, efficient, and maintainable software—key abilities for success in the programming trade!

Compiled by: Ann Chebet

Object-oriented programming (OOP) is a fundamental paradigm that enables software developers to structure code in a way that models real-world entities through objects and classes. In Kenya’s dynamic software development industry, OOP facilitates the creation of modular, reusable, and maintainable code, which is essential for the development of scalable applications in sectors such as banking, healthcare, and e-commerce. This chapter focuses on demonstrating essential OOP skills with particular emphasis on namespaces, a vital feature for organizing code and preventing naming conflicts especially in large projects or collaborative environments. Mastery of namespaces allows Kenyan software professionals to build robust applications that are easier to maintain and extend.

3.1 Implementing Objects and Classes

Object-oriented programming builds software from objects that model the things a program deals with. Each object is created from a class, which describes what every object of that kind knows and can do. This topic follows one running situation: a program that keeps track of the stock of a hardware shop. It starts with the two core ideas, moves to the parts of a class, builds and runs a small program, and ends with a team exercise in which each member writes a different class from one shared design. The code is written in Java, the language used throughout this unit. The ideas are the same in C#, C++ and Python, and only the syntax changes.

3.1.1 Overview of objects and classes in OOP

An object is a single, identifiable item inside a running program. It has state, which is the data it holds, and behaviour, which is what it can do. A class is the description from which objects are built: it lists the fields that hold the state and the methods that carry out the behaviour. Creating an object from a class is called instantiation, and the object is called an instance of the class.

Example: in the stock program, StockItem is a class. One object is the hinge sets on the shelf, with the name "Hinge set", a quantity of 12 and a unit price of Ksh 450. Another object is the 4-litre tins of paint, with its own name, quantity and price. Both objects follow the same class, but each keeps its own values.

Class and object compared:

Aspect Class Object
What it is A blueprint written once in the source code One item built from the blueprint while the program runs
Holds The declaration of fields and methods The actual values of the fields
How many One class for each kind of thing As many objects as the program needs
Example StockItem The hinge sets, the tins of paint

The main parts of a class:

  • Fields: Variables that store the state of each object, such as name, quantity and unitPrice.
  • Constructor: A special block that runs when an object is created and sets its starting values.
  • Methods: Named blocks of code that give the object its behaviour, such as calculating the value of the stock.
  • Access modifiers: Words such as public and private that control which code may use a field or method.
  • The this reference: A name that stands for the object currently running the code, used to tell a field apart from a parameter with the same name.

Three properties make objects useful. Each object has an identity, so two objects with equal values are still two objects. The state of one object is separate from the state of another, so selling hinge sets does not change the paint. The behaviour is stored with the data, so code that needs the stock value asks the object for it instead of repeating the calculation.

3.1.2 Implementation of classes

To implement a class, the developer turns a description of a thing into code: a class declaration, its fields, a constructor and its methods. The class is then compiled and used by other code. In Java, each public class is saved in a file with the same name as the class, so the class StockItem is saved in StockItem.java.

How the parts of a class are written in Java:

Part Purpose Written in Java as
Class declaration Names the class public class StockItem { ... }
Field Holds one piece of state private int quantity;
Constructor Sets the starting state public StockItem(String name, int quantity, double unitPrice) { ... }
Method Gives behaviour public double stockValue() { ... }
this reference Refers to the current object this.quantity = quantity;

Steps to implement a class:

  1. Identify the thing the program must handle and write its name as a singular noun, such as StockItem. Class names start with a capital letter and use capitals for each new word.
  2. List the data every object must keep, for example the item name, the quantity in stock and the unit price, and choose a data type for each: String, int and double.
  3. Declare each piece of data as a field inside the class. Declare fields private so that other classes cannot change them directly. Encapsulation is taught in full in Applying Object Encapsulation.
  4. Write a constructor with the same name as the class and no return type. Give it one parameter for each value an object needs at the start, and copy each parameter into its field using this.
  5. Add methods for what the object must do. A method that reports a value, such as stockValue, returns it. A method that changes the state, such as sell, can return nothing.
  6. Compile the class with the command javac StockItem.java. Read each error message from the first one, because later errors are often caused by the first.
  7. Test the class from another class that creates objects and calls their methods, as shown in the next sub-topic.

Two rules are worth knowing early. If a class declares no constructor, Java supplies a constructor with no parameters that leaves the fields at their default values: 0 for numbers and null for text. As soon as the class declares any constructor, that supplied constructor is no longer provided. Members marked static belong to the class itself and are shared by every object, while members without static belong to each object separately.

3.1.3 Creating a simple class and instantiating objects to demonstrate understanding

A good proof that a learner understands classes is a short program that defines a class, creates objects from it and shows that each object keeps its own state. The program below uses two files. StockItem.java holds the class and ShopDemo.java holds the main method that creates the objects.

StockItem.java:

public class StockItem {
    private String name;
    private int quantity;
    private double unitPrice;

    public StockItem(String name, int quantity, double unitPrice) {
        this.name = name;
        this.quantity = quantity;
        this.unitPrice = unitPrice;
    }

    public double stockValue() {
        return quantity * unitPrice;
    }

    public void sell(int units) {
        if (units > 0 && units <= quantity) {
            quantity = quantity - units;
        } else {
            System.out.println("Cannot sell " + units + " of " + name);
        }
    }

    public String describe() {
        return String.format("%s: %d in stock at Ksh %.2f each", name, quantity, unitPrice);
    }
}

ShopDemo.java:

public class ShopDemo {
    public static void main(String[] args) {
        StockItem hinges = new StockItem("Hinge set", 12, 450.0);
        StockItem paint = new StockItem("Paint 4 litre", 5, 2100.0);

        System.out.println(hinges.describe());
        System.out.println(paint.describe());

        hinges.sell(3);
        paint.sell(8);

        System.out.println(hinges.describe());
        System.out.println(paint.describe());
        System.out.printf("Value of hinges: Ksh %.2f%n", hinges.stockValue());
    }
}

Compile both files with javac StockItem.java ShopDemo.java and run the program with java ShopDemo. The output is:

Hinge set: 12 in stock at Ksh 450.00 each
Paint 4 litre: 5 in stock at Ksh 2100.00 each
Cannot sell 8 of Paint 4 litre
Hinge set: 9 in stock at Ksh 450.00 each
Paint 4 litre: 5 in stock at Ksh 2100.00 each
Value of hinges: Ksh 4050.00

Reading the program step by step:

  1. The expression new StockItem("Hinge set", 12, 450.0) creates an object in memory and runs the constructor, which stores the three values in its fields. The reference to the object is kept in the variable hinges.
  2. The second new creates a separate object for the paint. The two objects now exist side by side with different values.
  3. The first two describe calls print the starting state of each object.
  4. hinges.sell(3) changes only the hinge object: 12 − 3 = 9. The condition units <= quantity is true, so the stock is reduced.
  5. paint.sell(8) fails the same test because 8 is more than the 5 in stock. The object prints a message and its state does not change.
  6. The last describe calls show 9 hinge sets and still 5 tins of paint, which proves that the objects keep separate state.
  7. The stock value of the hinges is 9 × 450 = 4,050.

Common mistakes when creating objects:

  • Forgetting new: Writing StockItem hinges = StockItem("Hinge set", 12, 450.0); does not compile, because the constructor must be called with new.
  • Wrong arguments: Passing the values in the wrong order, such as 450.0 before 12, causes a type error or a wrong stock value. The arguments must match the constructor's parameters in number, type and order.
  • File name mismatch: A public class named StockItem saved in a file called Item.java is refused by the compiler.
  • Calling a method on the class: Writing StockItem.describe() fails because describe belongs to an object, not to the class itself.
  • Using an object that was never created: A variable that holds null, such as StockItem shelf = null; or a field of a class that was never assigned, refers to no object, and calling a method on it stops the program with a NullPointerException. A local variable with no value at all is refused by the compiler before the program runs.

Practice task: add a method restock(int units) that adds to the quantity only when units is above zero, create a third object for a box of nails, call restock on it and print its state.

3.1.4 Participating in a group project where each member implements a different class based on a shared design

Software for real clients is written by teams. One way to share the work is to agree a design first, give each member one class to build, and then join the classes together. Each member must follow the agreed design exactly so that the classes fit when they are combined.

Understanding the shared design

A shared design states which classes the system needs, what each one stores and which public methods it offers, with the exact method names, parameters and return types. It may be drawn as a UML class diagram or written as a table. Every member reads the whole design, not only their own class, because their class will call the methods of the others.

Example: three trainees build a booking program for a car wash. The shared design is:

Class Owner Data it keeps Public methods
Customer Member A name, phone Customer(String name, String phone), getName(), getPhone()
WashBay Member B number, whether it is free WashBay(int number), getNumber(), isFree(), occupy(), release()
Booking Member C customer, bay, service, price Booking(Customer customer, WashBay bay, String service, double price), summary()

Division of responsibilities

Each member owns one class and writes, compiles and tests it alone, without editing a class owned by someone else. This allows parallel progress without two people changing the same file. A change to the design, such as a new method, is discussed with the whole team and written into the design first.

Ensuring interface compatibility

Classes work together through their public methods, so the names, parameter types and return types agreed in the design must be followed exactly. Member C writes the Booking class knowing only that WashBay has an occupy method and a getNumber method. If Member B renamed occupy to take, Booking would fail to compile when the classes are joined.

The three classes written by the three members:

public class Customer {
    private final String name;
    private final String phone;

    public Customer(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

    public String getPhone() {
        return phone;
    }
}
public class WashBay {
    private final int number;
    private boolean free = true;

    public WashBay(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public boolean isFree() {
        return free;
    }

    public void occupy() {
        free = false;
    }

    public void release() {
        free = true;
    }
}
public class Booking {
    private final Customer customer;
    private final WashBay bay;
    private final String service;
    private final double price;

    public Booking(Customer customer, WashBay bay, String service, double price) {
        this.customer = customer;
        this.bay = bay;
        this.service = service;
        this.price = price;
        bay.occupy();
    }

    public String summary() {
        return String.format("%s booked %s in bay %d for Ksh %.2f",
                customer.getName(), service, bay.getNumber(), price);
    }
}

Version control and collaboration tools

A tool such as Git keeps every version of the project in one shared repository. Each member works on a separate branch for their class and merges it into the main branch when it compiles and passes its own tests. Git records who changed what, lets the team compare versions, and helps them resolve a conflict if two people edited the same lines.

Code integration and testing

After the classes are written, the team joins them and tests them together. An integration test creates objects of every class and checks that they cooperate. The test below books a bay for a customer and checks that the bay becomes taken:

public class Main {
    public static void main(String[] args) {
        Customer customer = new Customer("Wanjiru", "0700 000 001");
        WashBay bay = new WashBay(1);

        if (bay.isFree()) {
            Booking booking = new Booking(customer, bay, "full wash", 600.0);
            System.out.println(booking.summary());
        }

        if (!bay.isFree()) {
            System.out.println("Bay " + bay.getNumber() + " is now taken");
        }
    }
}

Output:

Wanjiru booked full wash in bay 1 for Ksh 600.00
Bay 1 is now taken

The price and the customer details are values chosen for the exercise. When the integration test fails, the team reads the error to find which class and which method do not match the design, and the owner of that class repairs it. Teams that use continuous integration let a server compile and test the whole project after every merge, so an interface mismatch is found within minutes.

Communication and documentation

Short regular meetings help the team share progress and ask questions before small doubts become integration faults. Each class carries comments that say what its methods do, and the design document is updated whenever the team agrees a change. Clear documentation lets a member who joins late, or who must repair a class written by someone else, understand it without guessing.

The rest of this chapter
🔒

Create a free account to open more of this chapter.

Free: practical guides, quick cards, workplace scenarios and more.

Create a free account
🔒3.2 Declaring Object Methods

A method is a named block of code inside a class that gives its objects behaviour. Without methods an object is only a container of values, and with methods it can calculate, check, change its own state and report results. This topic uses one running example:…

🔒3.3 Applying Namespaces

Namespaces provide a method to group related classes, interfaces, functions, and constants under a unique identifier, thereby avoiding naming collisions and enhancing code organization. In a country like Kenya where software projects often involve multiple dev…

🔒3.4 Applying Data Abstraction Concepts

In software development, particularly within Kenya's evolving tech industry, data abstraction is a crucial principle of object-oriented programming that enables developers to manage complexity by hiding irrelevant details. This is especially important when bui…

🔒3.5 Applying Object Encapsulation

Encapsulation is the principle of bundling data and the methods that work on that data into one unit, a class, and restricting direct access to some of the object's parts. The inside of the object is hidden, and other code reaches it only through the methods t…

🔒3.6 Implementing Class Templates

In Kenya's software development industry, flexibility and code reusability are crucial for delivering efficient solutions across diverse sectors such as banking, healthcare, and government services. Class templates in object-oriented programming enable develop…

🔒3.7 Implementing Class Inheritance

In Kenyan software development projects, especially within financial institutions like Equity Bank or SACCOs, efficient code reuse and maintainability are critical. Class inheritance in object-oriented programming (OOP) allows developers to build upon existing…

🔒3.8 Implementing Polymorphism

Polymorphism is a foundational concept in object-oriented programming (OOP) that enables software developers in Kenya to write flexible and reusable code that can operate on objects of different classes through a common interface. It is particularly valuable i…

Chapter Summary

This chapter provided a comprehensive demonstration of object-oriented programming skills, beginning with defining class methods that meet application requirements and emphasizing best practices for method naming and functionality. It explored the development of classes with multiple methods and their practical use in small applications, followed by conducting code reviews to ensure method declarations align with the intended application needs. The role of namespaces was examined to organize code effectively, including implementation techniques and the benefits of using namespaces in collaborative environments. Data abstraction concepts were introduced, highlighting their importance and practical implementation through abstract classes and pair programming exercises. The chapter also covered object encapsulation, detailing access modifiers such as private, public, and protected, and demonstrated encapsulation advantages through class design and presentations. Further, the use of class templates was discussed, focusing on creating generic templates and applying them to various data types in collaborative projects. Finally, the chapter addressed class inheritance by explaining different types and applying them in program hierarchies, and concluded with polymorphism, detailing its compile-time and runtime forms, practical implementation, and the benefits of polymorphism in software development.

Self-Assessment

🔒 PDFDownload this self-assessment, with answers

A. Written Assessment

  1. What is the primary purpose of defining class methods in object-oriented programming? (2 marks)
  2. Identify two best practices for naming methods in a software development project. (2 marks)
🔒20 more in this section.

Chapter Examination Questions

🔒 PDFDownload these examination questions, with worked answers

SECTION A (40 Marks) - Answer ALL Questions

  1. Explain how defining class methods that fulfill application requirements improves software maintainability. Illustrate your answer with an example relevant to a banking application such as KCB. (4 marks)
  2. Identify four best practices for method naming in object-oriented programming and explain why each is important. (4 marks)
🔒18 more in this section.

Chapter Practical Activities

Practical 1: Define and Implement Class Methods for Student Management

Software Development · Level 6
Object Oriented Programming
PRACTICAL ASSESSMENT
TIME: 4 HOURS
⬇ PDFCandidate Instructions (Candidate Tool)

Type: Individual

INSTRUCTIONS TO CANDIDATE:
1.  You are required to perform the following task:
i.  Create a Java class Student with attributes name (String), age (int), grade (String) and implement methods displayDetails(), updateGrade(String newGrade), and calculateYearOfBirth(currentYear) for a student with sample values.
2.  You have been provided with the following resources for the practical task:
Tools & EquipmentMaterials
Computer installed with Windows 7 OS or higherJava source code files
Java Development Kit (JDK)Flash disk
NetBeans or Eclipse IDE
⬇ PDFResources Required (Cutting List)
S/NItemQuantity
1Computer installed with Windows 7 OS or higher1 Pc per Candidate
2Java Development Kit (JDK) Installation Media1 Pc per Candidate
3NetBeans or Eclipse IDE1 Pc per Candidate
4Flash disk1 Pc per Candidate
⬇ PDFAssessor Guide
Items to be EvaluatedMarks AvailableMarks ObtainedComments
TASK 1: Define Class and Attributes
Created a class named Student
(Award 1 mark for correct class declaration or zero)
1
Declared attributes: name (String), age (int), grade (String)
(Award 1 mark for each correctly declared attribute or zero)
2
Sub-Total3
TASK 2: Implement Class Methods
Implemented displayDetails() method to print all attribute values
(Award 2 marks for correct method implementation or zero)
2
Implemented updateGrade(String newGrade) method to update grade attribute
(Award 2 marks for correct method implementation or zero)
2
Implemented calculateYearOfBirth(int currentYear) method returning year of birth
(Award 3 marks for correct calculation and return statement or zero)
3
Sub-Total7
TASK 3: Testing and Demonstration
Instantiated a Student object with sample values
(Award 1 mark for correct object instantiation or zero)
1
Called displayDetails() method and output is correct
(Award 2 marks for correct output or zero)
2
Updated grade using updateGrade() and verified change
(Award 2 marks for correct update or zero)
2
Called calculateYearOfBirth() with current year and verified output
(Award 2 marks for correct output or zero)
2
Sub-Total7
TASK 4: Code Quality and Documentation
Used appropriate method and variable naming conventions
(Award 2 marks for consistent and meaningful names or zero)
2
Included inline comments explaining methods and logic
(Award 2 marks for relevant comments or zero)
2
Sub-Total4
PRODUCT CHECKLIST
Class Student compiles without errors and methods function as specified
(Award 5 marks for fully functional, error-free class or zero)
5
Sub-Total5
GRAND TOTAL26
ASSESSMENT OUTCOME:   ☐ Competent    ☐ Not Yet Competent (competent if at least 50%)

Practical 2: Develop and demonstrate a class with multiple methods in Java

Software Development · Level 6
Object Oriented Programming
PRACTICAL ASSESSMENT
TIME: 4 HOURS
⬇ PDFCandidate Instructions (Candidate Tool)

Type: Individual

INSTRUCTIONS TO CANDIDATE:
1.  You are required to perform the following task:
i.  Develop a Java class named 'Product' with attributes and methods, and demonstrate its usage in a console application with at least three objects.
2.  You have been provided with the following resources for the practical task:
Tools & EquipmentMaterials
Computer with Windows 7 OS or higherJava source code files
Java Development Kit (JDK)Documentation files
NetBeans or Eclipse IDE
Flash disk or CD RW
⬇ PDFResources Required (Cutting List)
S/NItemQuantity
1Computer with Windows 7 OS or higher1 Pc per Candidate
2Java Development Kit (JDK) Installation Media1 Pc per Candidate
3NetBeans or Eclipse IDE1 Pc per Candidate
4Flash disk or CD RW1 Pc per Candidate
⬇ PDFAssessor Guide
Items to be EvaluatedMarks AvailableMarks ObtainedComments
TASK 1: Create Java Class and Methods
Implemented class 'Product' with attributes: productName (String), price (double), quantity (int)
(Award 1 mark for each correctly declared attribute or zero)
3
Included methods: displayDetails(), updatePrice(double newPrice), updateQuantity(int newQuantity)
(Award 1 mark for each correctly implemented method or zero)
4
Used appropriate access modifiers and included inline comments for methods
(Award 2 marks for proper use of access modifiers and meaningful comments)
2
Sub-Total9
TASK 2: Instantiate Objects and Demonstrate Methods
Instantiated three 'Product' objects with distinct attribute values
(Award 1 mark per correctly instantiated object or zero)
3
Called displayDetails() for each object and printed output to console
(Award 2 marks for correct method calls and output display)
2
Demonstrated use of updatePrice() and updateQuantity() on at least one object and displayed updated details
(Award 4 marks for correct method usage and output)
4
Sub-Total9
TASK 3: Debugging and Testing
Detected and fixed at least one logical error in the program
(Award 2 marks for identifying and correcting a logic error)
2
Detected and fixed at least one compilation error (e.g., missing semicolon or type mismatch)
(Award 2 marks for identifying and correcting a compilation error)
2
Used debugging tools or print statements to validate program workflow
(Award 2 marks for applying debugging techniques)
2
Tested edge cases such as zero quantity or negative price and handled appropriately
(Award 2 marks for testing and handling edge cases)
2
Sub-Total8
TASK 4: Package and Deploy Application
Prepared project folders and assets including source code and documentation
(Award 1 mark for organized project folder)
1
Generated runnable Java archive (.jar) file of the application
(Award 1 mark for generating runnable .jar)
1
Successfully executed the packaged application without errors
(Award 1 mark for successful execution)
1
Sub-Total3
TASK 5: Write Deployment Guide and Use Case
Wrote a step-by-step installation and deployment guide including screenshots
(Award 2 marks for clear and complete deployment guide with screenshots)
2
Explained required dependencies such as JDK and environment variables
(Award 2 marks for explaining dependencies)
2
Proposed a real-world use case where the class could be adopted in a business
(Award 1 mark for relevant real-world use case)
1
Sub-Total5
TASK 6: Documentation Submission
Submitted complete, well-formatted source code with inline comments
(Award 3 marks for complete and well-commented source code)
3
Included UML class diagram and screenshots of application output
(Award 4 marks for including UML and screenshots)
4
Provided a technical summary explaining system workflow from user perspective
(Award 5 marks for clear technical summary)
5
Submitted a well-organized digital folder containing all files and documentation
(Award 6 marks for professional folder structure and presentation)
6
Sub-Total18
PRODUCT CHECKLIST
Class 'Product' correctly implemented with required attributes and methods
(Award 5 marks for correct class implementation matching specifications)
5
Objects instantiated with accurate attribute values and methods demonstrated correctly
(Award 5 marks for correct object creation and method demonstration)
5
Application runs without compilation or runtime errors and handles edge cases gracefully
(Award 5 marks for error-free execution and edge case handling)
5
Packaged application (.jar) runs successfully on target system
(Award 3 marks for successful packaged application execution)
3
Sub-Total18
GRAND TOTAL70
ASSESSMENT OUTCOME:   ☐ Competent    ☐ Not Yet Competent (competent if at least 50%)
🔒

Free: practical guides, quick cards, workplace scenarios and more.

Create a free account
🔒Conduct Code Review on Java Method Declarations for Application CompliancePractical 3
🔒Implement Namespaces in an Object-Oriented Programming ProjectPractical 4
🔒Develop a Java project demonstrating multiple namespaces for code organizationPractical 5
🔒Presentation on Benefits of Namespaces in Collaborative CodingPractical 6
🔒Implement Data Abstraction in a Student Management SystemPractical 7
🔒Design and Implement an Abstract Class in JavaPractical 8
🔒Pair Work on Abstraction in Existing Java CodePractical 9
🔒Implement Encapsulation with Access Modifiers in JavaPractical 10
🔒Demonstrate Encapsulation Using Private and Public Modifiers in JavaPractical 11
🔒Presentation on Advantages of Encapsulation in Object-Oriented ProgrammingPractical 12
🔒Create and Demonstrate Generic Class Templates in JavaPractical 13
🔒Collaborative Development of a Java Project Using Class TemplatesPractical 14
🔒Inheritance Coding Challenge: Vehicle Hierarchy ImplementationPractical 15
🔒Implement and demonstrate polymorphism in JavaPractical 16
🔒Discussion on Practical Applications of Polymorphism in Software DevelopmentPractical 17
Flashcards 20 cards Study deck ▾
Question
1

↻ Tap card to reveal answer
🔒

18 more in this section.

Create a free account
Test Yourself 16 questions Start quiz ▾
0%
0 / 2
🔒

14 more in this section.

Create a free account
Am I competent?

At the start of this chapter we promised you would be able to:

  • Implement objects and classes correctly by following proper work procedures.
  • Declare object methods accurately to meet the needs of your application.
  • Apply namespaces correctly to organize your code as required.
  • Use data abstraction concepts effectively to simplify complex data.
  • Implement object encapsulation properly to protect your data.
  • Create class templates that fit the specific requirements of your application.
  • Implement class inheritance accurately to reuse and extend functionality.
  • Apply polymorphism effectively to make your programs more flexible and dynamic.

Tick each one you can genuinely do.

So, are you there yet?

You're competent when you can confidently do 50% or more of what this chapter promised.

Sign in to record how you're doing.