Start learning free

LESSON 562 OF 573 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Employees and Managers in Go: Embedding and Interfaces

Practice O(1) Read the full explanation

A company pays every employee a salary. A manager is an employee too, but earns a bonus on top of the salary. Go has no classes and no extends: as in the lesson Inheritance, Manager embeds Employee, so it gets the employee's fields and methods and changes only how its pay is worked out.

Write the struct Employee:

  • its fields name and salary, and the constructor NewEmployee(name, salary);
  • getName() returns the name;
  • pay() returns the salary.

the struct Manager, which embeds Employee:

  • the embedded Employee and a field bonus, and the constructor NewManager(name, salary, bonus), which builds the embedded Employee with NewEmployee;
  • its own pay(), which returns the embedded employee's pay plus the bonus. Call the parent's version as in the lesson Calling the Parent Method, this.Employee.pay(): it is what knows how an employee is paid;
  • getName() is not written again: it is promoted from the embedded Employee, unchanged.

and the function describe(w Worker), which returns the text <name> earns <pay> for any Worker, where the amount is what w.pay() returns, for example Ana earns 3000. Worker is an interface with the methods getName() and pay(); both structs satisfy it.

Why describe is a function in Go. In Java or Python, describe would be a method of Employee that a manager inherits, and its call to pay() would run the manager's pay. Go's embedding works differently: inside a method of Employee, this is the embedded Employee, so this.pay() is always Employee's pay, even when that employee sits inside a manager (the lesson Method Overriding: the shadowing goes by the static type). A function that takes the interface calls the pay of the value it is given: Manager's own pay for a manager.

The input is a list of people, one per line: employee name salary or manager name salary bonus. For each line, the code that tests your structs creates that value with NewEmployee or NewManager and prints what describe returns for it, on a line of its own.

 

Example 1:

Input:
    employee Ana 3000
    manager Bob 4000 1000
Output:
    Ana earns 3000
    Bob earns 5000
Explanation:
Ana's pay is her salary. Bob's pay is the embedded employee's pay, his salary 4000, plus his bonus 1000.
describe gets a Manager, so w.pay() runs Manager's pay.

Example 2:

Input:
    manager Cleo 5000 0
    employee Dan 5000
Output:
    Cleo earns 5000
    Dan earns 5000
Explanation: A manager whose bonus is 0 earns exactly the salary, like an employee.

Example 3:

Input:
    manager Eve 7000 2500
    employee Finn 2800
    employee Gus 3100
    manager Hana 6000 500
Output:
    Eve earns 9500
    Finn earns 2800
    Gus earns 3100
    Hana earns 6500
Explanation: Employees and managers come in any order, and each value keeps its own name and numbers.

 

Constraints:

  • 0 <= salary <= 106
  • 0 <= bonus <= 106
  • A name is one word of 1 to 20 English letters. Two people may have the same name.
  • At most 100 people per test case.
STEP 1 OF 10
    Ctrl + Enter to check

    GO · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Employees And Managers in Go, explained

    Problem overview

    A company pays every employee a salary. A manager is an employee too, but earns a bonus on top. The lesson asks for the code that models this, and a function describe that turns any worker into text such as Ana earns 3000.

    Go has no classes and no extends. Two other features do the job:

    • Embedding. A struct can list another type, with no field name, among its fields. It then gets that type's fields and methods as if they were its own.
    • Interfaces. An interface lists methods. Any type that has those methods satisfies the interface automatically, with no implements declaration.

    The pieces are the Employee struct with NewEmployee, getName() and pay(); the Manager struct, which embeds Employee, adds a bonus and has its own pay(); the interface Worker; and describe(w Worker).

    For an employee Ana with salary 3000 the description is Ana earns 3000. For a manager Bob with salary 4000 and bonus 1000 it is Bob earns 5000.

    Approach

    Employee is simple: it stores a name and a salary, and its pay is the salary.

    Manager embeds Employee, so it already has a name, a salary and getName() without writing any of them again. It stores only what an employee doesn't know: the bonus. Its pay() has the same name as the employee's, which shadows the embedded one: calling pay() on a manager runs the manager's version. The manager's version calls the employee's through the embedded field, this.Employee.pay(), and adds the bonus. That way the code that knows how an employee is paid stays in one place.

    Writing this.pay() inside Manager.pay would be a mistake: it calls the same method again, which calls itself again, until the program runs out of stack.

    Finally, describe must work for both types. It takes a Worker, an interface with getName() and pay(). Both structs have those methods, so both fit. Calling w.pay() through the interface runs the method of the value inside: the manager's for a manager, the employee's for an employee.

    Why describe is a function, not a method

    In some languages, describe would be a method of the parent class that managers inherit, and its call to pay() would pick the manager's version. Go's embedding doesn't work that way. Inside a method of Employee, this is the embedded Employee, which knows nothing about the manager around it. If the method were written as func (this Employee) describe() string and called on Manager{Employee{"Bob", 4000}, 1000}, it would report Bob earns 4000, while the manager's pay() is 5000. A function that takes the interface avoids the problem, because the interface value remembers which type it holds.

    Step by step

    1. Employee fields. Declare name as a string and salary as an int.
    2. Employee constructor. NewEmployee returns Employee{name: name, salary: salary}.
    3. getName. Return this.name. The interface lists methods, not fields, so describe needs this method to reach the name.
    4. pay. Return this.salary.
    5. Embed in Manager. Write Employee alone on a line inside the struct.
    6. Add the bonus. Declare bonus as an int.
    7. Manager constructor. Return a Manager whose embedded part is built by NewEmployee(name, salary). An embedded field is named after its type, so it is set with Employee: ....
    8. Manager's pay. Return this.Employee.pay() + this.bonus.
    9. Describe. Return the text built by fmt.Sprintf with the format "%s earns %d", w.getName() and w.pay().

    The solution in Go

    type Employee struct {
        name string
        salary int
    }
    func NewEmployee(name string, salary int) Employee {
        return Employee{name: name, salary: salary}
    }
    func (this Employee) getName() string {
        return this.name
    }
    func (this Employee) pay() int {
        return this.salary
    }
    type Manager struct {
        Employee
        bonus int
    }
    func NewManager(name string, salary int, bonus int) Manager {
        return Manager{Employee: NewEmployee(name, salary), bonus: bonus}
    }
    func (this Manager) pay() int {
        return this.Employee.pay() + this.bonus
    }
    type Worker interface {
        getName() string
        pay() int
    }
    func describe(w Worker) string {
        return fmt.Sprintf("%s earns %d", w.getName(), w.pay())
    }
    

    Go details:

    • Promotion. getName() is written only for Employee, yet Manager has it too, because methods of an embedded type are promoted. Fields are promoted as well: for a manager m, m.salary is the embedded employee's salary.
    • Shadowing is not overriding. The manager's pay hides the employee's for calls made on a Manager (or through an interface that holds one). The employee's method is still there and reachable as m.Employee.pay().
    • Implicit interfaces. Neither struct mentions Worker. Each simply has both methods with the right signatures. If a struct lacked one, passing it to describe would be a compile error.
    • fmt.Sprintf. It builds text like fmt.Printf prints it, but returns the string. %s takes a string and %d an integer, in order.

    Using it

    fmt.Println(describe(NewEmployee("Ana", 3000)))
    fmt.Println(describe(NewManager("Bob", 4000, 1000)))
    fmt.Println(describe(NewManager("Cleo", 5000, 0)))
    m := NewManager("Eve", 7000, 2500)
    fmt.Println(m.getName(), m.pay(), m.Employee.pay(), m.salary)
    var w Worker = m
    fmt.Println(w.pay())
    

    This prints:

    Ana earns 3000
    Bob earns 5000
    Cleo earns 5000
    Eve 9500 7000 7000
    9500
    

    A manager with a bonus of 0 earns exactly the salary, like an employee. For Eve, m.pay() is 9500, the salary plus the bonus, while m.Employee.pay() is just the salary 7000. Storing the manager in a Worker variable keeps the manager's behaviour: w.pay() is still 9500.

    Edge cases and common mistakes

    • Calling this.pay() in the manager's pay. It calls itself and never stops.
    • Repeating the salary logic. Returning this.salary + this.bonus works today but copies the idea of how an employee is paid. Calling the employee's pay keeps it in one place.
    • Making describe a method of Employee. It would always use the employee's pay, even for a manager.
    • Forgetting to build the embedded part. A Manager built without setting Employee has an empty name and a salary of 0.
    • Zero bonus and equal names. A bonus of 0 is valid, and two people may share a name, since each value keeps its own data.
    Keep going with Go500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free