Example Programs
Here are some example BBj programs. Each example is designed to demonstrate some aspect of using Custom Object Classes in BBj.
The complete source code for each program is included in an appropriately named sub-folder in the zip file you can download here.
Program #5: Class Inheritance
Let’s take a look at a BBj program that demonstrates how to use class inheritance to pay employees when there are multiple types of employees. We will create a simple Employee class to hold the attributes and operations that are common to all types of employees. Then we will extend the Employee class to create two subclasses, SalariedEmployee and CommissionedEmployee. It should be intuitive that the logic to pay employees of the different types will have to be different for each type. In this example, we create a PayMaster class to hold the logic for paying all of our employees. PayMaster in turn relies on information from the various Employee classes, together with its own “pay by writing a check” logic. For simplicity, we will use the same Check and PayrollCheck classes from Program 3 to help make the payments. Again, we show the code for each new source file using a different border color for readability:
REM /** ==============================================
REM * EmployeeIF.bbj
REM * This file defines the EmployeeIF Interface
REM ====================================================
USE ::Check.src::Check
INTERFACE public EmployeeIF
METHOD public BBjString getName()
METHOD public BBjString getID()
REM Pay Methods
METHOD public VOID markAsPaid()
REM Salary information
METHOD public BBjNumber isSalaried()
METHOD public BBjNumber getSalaryToPay()
REM Commission information
METHOD public BBjNumber isCommissioned()
METHOD public BBjNumber getCommissionToPay()
INTERFACEEND
|
REM /** ============================================================
REM * Employee.bbj
REM * This file defines the Employee class, which implements the
REM * EmployeeIF Interface
REM =============================================================== */
USE ::EmployeeIF.bbj::EmployeeIF
CLASS public Employee implements EmployeeIF
REM These attributes should only be settable in a constructor
FIELD protected BBjString Name$ = ""
FIELD protected BBjString EmployeeID$ = ""
REM ===============================================================
REM Constructors
REM This protected constructor can be called from subclasses
METHOD protected Employee(BBjString newName$,
: BBjString newEmployeeID$)
#Name$ = newName$
#EmployeeID$ = newEmployeeID$
METHODEND
REM This default private constructor can never be called
METHOD private Employee()
METHODEND
REM ===============================================================
REM Employee individual attributes such as Name...
REM Anyone should be able to ask any Employee for his/her name
METHOD public BBjString getName()
METHODRET #Name$
METHODEND
REM Anyone should be able to ask any Employee for his/her ID
METHOD public BBjString getID()
METHODRET #EmployeeID$
METHODEND
REM ===============================================================
REM Salary Methods
REM Is this a salaried employee?
REM For generic Employees, the answer is "no"
METHOD public BBjNumber isSalaried()
METHODRET 0
METHODEND
REM What monthly salary should this employee be paid?
REM For generic Employees, the answer is "0"
METHOD public BBjNumber getSalaryToPay()
METHODRET 0
METHODEND
REM ===============================================================
REM Commission Methods
REM Is this a commissioned employee?
REM For generic Employees, the answer is "no"
METHOD public BBjNumber isCommissioned()
METHODRET 0
METHODEND
REM What commission is due to be paid to this employee?
REM For generic Employees, the answer is "0"
METHOD public BBjNumber getCommissionToPay()
METHODRET 0
METHODEND
CLASSEND
|
REM /**
REM * SalariedEmployee.bbj
REM * This file defines the SalariedEmployee class, which
REM * extends the Employee class
REM */
USE ::Employee.bbj::Employee
CLASS public SalariedEmployee extends Employee
FIELD public BBjNumber MonthlySalary = 0
REM ===============================================================
REM Constructors
REM Use this constructor - we always have to have a name and ID
METHOD public SalariedEmployee(BBjString newName$, BBjString newID$,
: BBjNumber newMonthlySalary)
REM Pass the name and ID to the parent Employee class
#super!(newName$, newID$)
#MonthlySalary = newMonthlySalary
METHODEND
REM The default constructor is private so that it can never be called
METHOD private SalariedEmployee()
METHODEND
REM ===============================================================
REM SalariedEmployee Fields
REM BBj provides implicit get/setMonthlySalary() methods
REM ===============================================================
REM Since this the salaried employees class, we need to fill in the
REM methods the EmployeeIF says a salaried employee should have
REM Is this a salaried employee? Yes (1).
METHOD public BBjNumber isSalaried()
METHODRET 1
METHODEND
REM What should this employee be paid for this month?
REM For salaried Employees, the answer is "one month's salary"
METHOD public BBjNumber getSalaryToPay()
METHODRET #MonthlySalary
METHODEND
REM Once a salaried employee is paid, call this method to update any
REM internal record keeping values. In this case, we have none,
REM BUT we define it to fully implement the EmployeeIF Interface
METHOD public VOID markAsPaid()
METHODEND
CLASSEND
|
REM /**
REM * CommissionedEmployee.bbj
REM * This file defines the CommissionedEmployee class, which
REM * extends the Employee class
REM */
USE ::Employee.bbj::Employee
CLASS public CommissionedEmployee extends Employee
FIELD public BBjNumber CommissionPercentage = 0
FIELD public BBjNumber SalesDueForCommission = 0
REM ===============================================================
REM Constructors
REM Use this constructor - we always have to have a name and ID
METHOD public CommissionedEmployee(BBjString newName$,
: BBjString newID$,
: BBjNumber newCommissionPercentage)
REM Pass the name and ID to the parent Employee class
#super!(newName$, newID$)
#CommissionPercentage = newCommissionPercentage
REM New employees always start with "0" sales so far...
METHODEND
REM The default constructor is private so that it can never be called
METHOD private CommissionedEmployee()
METHODEND
REM ===============================================================
REM CommissionedEmployee Fields
REM BBj provides implicit get/setX() methods for the public fields
REM When the employee makes a sale, add the value to the running
REM total so commission will be paid on it at the next pay day
METHOD public void addSale(BBjNumber newSaleAmount)
#SalesDueForCommission = #SalesDueForCommission + newSaleAmount
METHODEND
REM Once the employee is paid, call this method to reset the
REM "unpaid sales" back to zero. Even if we did not have any
REM actions to take, we would still have to define markAsPaid()
REM to fully implement the EmployeeIF Interface
METHOD public VOID markAsPaid()
#SalesDueForCommission = 0
METHODEND
REM ===============================================================
REM Since this the commissioned employees class, fill in the methods
REM the EmployeeIF says a commissioned employee must have
REM Is this a commissioned employee? Yes (1).
METHOD public BBjNumber isCommissioned()
METHODRET 1
METHODEND
REM What should this employee be paid for commissions?
REM For commissioned employees: "commission on sales"
REM NOTE: Be sure to call markAllSalesPaid() once the employee is
REM actually paid, to avoid paying multiple commissions!
METHOD public BBjNumber getCommissionToPay()
METHODRET #CommissionPercentage * #SalesDueForCommission
METHODEND
CLASSEND
|
REM /**
REM * PayMaster.bbj
REM * This file defines the PayMaster class, which pays employees
REM */
USE ::Employee.bbj::Employee
USE ::PayrollCheck.src::PayrollCheck
CLASS public PayMaster
FIELD private static BBjNumber TaxRate = 0.30
METHOD public PayrollCheck pay(Employee employeeToPay!)
DECLARE BBjNumber netPay
netPay = 0
IF (employeeToPay!.isSalaried()) THEN
netPay = employeeToPay!.getSalaryToPay()
ELSE
IF (employeeToPay!.isCommissioned())
netPay = employeeToPay!.getCommissionToPay()
ENDIF
ENDIF
employeeToPay!.markAsPaid()
REM Now create a check based on the net pay
METHODRET new PayrollCheck(employeeToPay!.getName(),
: netPay, #TaxRate * netPay)
METHODEND
CLASSEND
|
REM /** ============================================================
REM * This file uses a number of classes to pay Employees
REM * PayEmployees.bbj
REM * ============================================================= */
USE ::Employee.bbj::Employee
USE ::SalariedEmployee.bbj::SalariedEmployee
USE ::CommissionedEmployee.bbj::CommissionedEmployee
USE ::PayMaster.bbj::PayMaster
USE ::PayrollCheck.src::PayrollCheck
DECLARE PayMaster PayMaster!
PayMaster! = new PayMaster()
REM Create a commissioned employee
DECLARE CommissionedEmployee James!
James! = new CommissionedEmployee("James Dean", "101C", 0.16)
REM Give James some sales for us to pay him commission on
James!.addSale(5219)
James!.addSale(811)
REM Create a salaried employee #201
DECLARE SalariedEmployee Bob!
Bob!= new SalariedEmployee("Bob Wills", "201S", 5000)
DECLARE Employee Employee!
REM Now pay James
Employee! = James!
DECLARE PayrollCheck checkForJames!
checkForJames! = PayMaster!.pay(Employee!)
REM Now pay Bob
Employee! = Bob!
DECLARE PayrollCheck checkForBob!
checkForBob! = PayMaster!.pay(Employee!)
REM Print out the check information
PRINT "James received check #", checkForJames!.getCheckNumber(),
PRINT " for the amount of ", checkForJames!.getAmount()
PRINT "Bob received check #", checkForBob!.getCheckNumber(),
PRINT " for the amount of ", checkForBob!.getAmount()
Employee! = James!
PRINT "> Does James work on commission? ",
: IFF(Employee!.isCommissioned(),"YES","NO")
Employee! = Bob!
PRINT "> Does Bob work on commission? ",
: IFF(Employee!.isCommissioned(),"YES","NO")
|
Program 5. The Classes and Other Code to Pay Employees
Execution
Creating the BBj source files and then running Program 5 (the code in PayEmployees.bbj) resulted in the following SysConsole output:
Figure 5. The Output from Program 5
Observations
Here are some details that this code demonstrates:
-
Only the code in PayEmployees.bbj explicitly uses the SalariedEmployee and CommissionedEmployee subclasses. All other access uses a reference to the Employee superclass. This means if, in the future, you add more subclasses than just those two, the various classes will continue to work. The exception is the code in PayEmployees.bbj, of course. That code is specifically interested in working with salaried vs. commissioned employees, and presumably is where any new subclasses would be needed.
-
The PayMaster.pay(Employee employeeToPay!) method is an example of object-oriented programming that relies upon classes and inheritance to work. It takes as an argument a reference to a superclass, when in reality the code will always pass an instance of one of its subclasses. As an alternative, you could have created two PayMaster.pay() methods, one for each of the subclasses we created. This, however, would mean that every time you add a new subclass, you would need to add another method to the PayMaster class.
-
You could easily enhance the PayMaster class by giving its constructor a parameter to take in the tax rate (instead of having the TaxRate field fixed at 0.30). Since the PayMaster class consistently uses the TaxRate field instead of the literal “0.30” value, this would be a simple improvement, and the class would be more generally useful.
-
The EmployeeIF Interface is not absolutely necessary in this simple case, but it demonstrates how you can share an interface definition without sharing any implementation details. This sharing is useful in object-oriented programming to help avoid coupling between class implementations.
BBj Custom Objects Tutorial Contents
BBj Custom Objects Tutorial: Introduction
BBj Custom Objects Tutorial: Interfaces
BBj Custom Objects Tutorial: Classes
BBj Custom Objects Tutorial: Fields
BBj Custom Objects Tutorial: Methods
BBj Custom Objects Tutorial: Using Custom Objects
BBj Custom Objects Tutorial: Program #1 - Writing a Check
BBj Custom Objects Tutorial: Program #2 - Protected and Private Fields
BBj Custom Objects Tutorial: Program #3: The Static Keyword
BBj Custom Objects Tutorial: Program #4 - Error Handling
BBj Custom Objects Tutorial: Program #5 - Class Inheritance
BBj Custom Objects Tutorial: Program #6 - Callback Choices
BBj Custom Objects Tutorial: Program #7 - Constructors and Field Initialization