Start learning free

LESSON 563 OF 573 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Shipping Options in Go: Embedding and Shadowed Methods

Practice O(1) Read the full explanation

An online shop offers three ways to ship a parcel. Standard shipping is the basic one. Express and Economy are kinds of shipping too: they answer the same two questions, what the option is called and what it costs, but each in its own way. As in the lesson Method Overriding, Express and Economy embed Shipping and give themselves methods with the same names, which shadow the embedded ones.

Write the struct Shipping, the standard option (it has no fields):

  • name() returns "Standard".
  • cost(weight) returns 5 + weight: a base price of 5, plus 1 for each kilogram.

and two structs that embed Shipping and have their own name and cost:

  • Express: name() returns "Express", and cost(weight) returns 10 + 2*weight.
  • Economy: name() returns "Economy", and cost(weight) returns 3 + weight/2, where the division rounds down: 7/2 is 3.

The input lists parcels, one per line: the option (standard, express or economy) and the weight. For each line, the code that tests your structs creates a value of that type (Shipping{}, Express{} or Economy{}) and prints <name>: <cost> from its name() and cost(weight). On an Express value, name() runs Express's own method, not the embedded Shipping's.

 

Example 1:

Input:
    standard 4
    express 4
    economy 4
Output:
    Standard: 9
    Express: 18
    Economy: 5
Explanation: The same parcel of 4 kilograms costs 5 + 4 = 9 with Standard, 10 + 2*4 = 18 with Express
and 3 + 4/2 = 5 with Economy.

Example 2:

Input:
    standard 0
    express 0
    economy 0
Output:
    Standard: 5
    Express: 10
    Economy: 3
Explanation: With a weight of 0, only the base prices are left.

Example 3:

Input:
    economy 7
    economy 1
    standard 7
    express 7
Output:
    Economy: 6
    Economy: 3
    Standard: 12
    Express: 24
Explanation: Economy halves the weight and rounds down: 7/2 is 3, so 3 + 3 = 6, and 1/2 is 0, so 3 + 0 = 3.

 

Constraints:

  • 0 <= weight <= 1000, a whole number of kilograms.
  • At most 100 parcels per test case.
STEP 1 OF 8
    Ctrl + Enter to check

    GO · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Shipping Options in Go, explained

    Problem overview

    A shop ships parcels in three ways. Shipping is the standard option, and Express and Economy are two more. All three answer the same two questions: name() says what the option is called, and cost(weight) says what a parcel of that many kilograms costs. Only the answers differ.

    • Shipping: name "Standard", cost 5 + weight.
    • Express: name "Express", cost 10 + 2*weight.
    • Economy: name "Economy", cost 3 + weight/2, with the division rounding down.

    For a parcel of 4 kilograms, the three costs are 9, 18 and 5. Express and Economy must embed Shipping and then give themselves methods with the same names.

    Approach

    Go has no classes and no inheritance. What it has is embedding: when a struct lists another type on a line of its own, without a field name, it gets that type's fields and methods as if they were its own. That is why Express and Economy start with just Shipping inside the braces.

    When the outer struct defines a method with the same name as an embedded one, the outer method wins. It shadows the embedded one: on an Express value, name() finds Express's own method first. The embedded method is not gone, and you can still reach it by naming the embedded field.

    Because every shipping type has the same two methods, the code that prints a parcel's line calls the same name() and cost(weight) whichever of the three types it created, and never cares how the answer is made. That is the point of giving the three types matching method names and signatures.

    Step by step

    1. Write Shipping, an empty struct. It needs no fields, because its answers never change. Its name returns "Standard".
    2. Its cost returns 5 + weight: a base price plus one per kilogram.
    3. In Express, write Shipping alone on a line. That is the embedding.
    4. Give Express its own name and cost. Each one shadows the embedded method of the same name. If you left out cost, an express parcel would quietly get the standard price.
    5. Do the same for Economy: embed Shipping, then write name and a cost that returns 3 + weight/2.

    The solution in Go

    type Shipping struct{}
    
    func (this Shipping) name() string {
        return "Standard"
    }
    
    func (this Shipping) cost(weight int) int {
        return 5 + weight
    }
    
    type Express struct {
        Shipping
    }
    
    func (this Express) name() string {
        return "Express"
    }
    
    func (this Express) cost(weight int) int {
        return 10 + 2*weight
    }
    
    type Economy struct {
        Shipping
    }
    
    func (this Economy) name() string {
        return "Economy"
    }
    
    func (this Economy) cost(weight int) int {
        return 3 + weight/2
    }

    The receiver is named this here. Go lets you pick any receiver name; the lessons use this, and the code never reads it, since none of these types has fields.

    Using the structs

    Create a value of each type and call the methods:

    e := Express{}
    c := Economy{}
    fmt.Println(Shipping{}.name(), Shipping{}.cost(4))
    fmt.Println(e.name(), e.cost(4))
    fmt.Println(c.name(), c.cost(4))
    fmt.Println(c.cost(7), c.cost(1))

    This prints:

    Standard 9
    Express 18
    Economy 5
    6 3
    

    The last line shows the rounding: 7/2 is 3, so the cost is 6, and 1/2 is 0, so the cost is 3. Both operands of weight/2 are int, so Go drops the remainder.

    The embedded method is still there

    Shadowing hides the embedded method; it doesn't delete it. Name the embedded field, which has the same name as its type, and you get the original:

    e := Express{}
    fmt.Println(e.name(), e.cost(4))
    fmt.Println(e.Shipping.name(), e.Shipping.cost(4))
    Express 18
    Standard 9
    

    Edge cases and common mistakes

    • Forgetting one override. An Express with its own name but no cost compiles and runs, and prints Express with the standard price. Write both methods in both types.
    • Weight 0. Only the base prices are left: 5, 10 and 3.
    • Odd weights in Economy. The halving rounds down, so weights 6 and 7 both cost 6.
    • Expecting inheritance. Embedding copies no behaviour into Express; it forwards calls. A method of Shipping that calls this.name() always calls Shipping's own name, even when the value came from an Express. With a helper label() on Shipping that returns "Shipping by " + this.name(), Express{}.label() gives Shipping by Standard. Go has no virtual dispatch through embedding, so write the methods you need on each type.

    Testing

    Try one parcel through all three types, then the extremes. A weight of 4 gives 9, 18 and 5. A weight of 0 gives 5, 10 and 3. A weight of 1000 gives 1005, 2010 and 503. Checking the odd weights matters most for Economy: if 7 kilograms does not cost 6, the division is the problem. If Express prints a standard price, one of its methods is missing and the embedded one is answering instead.

    Why this shape

    The three types share one idea, shipping a parcel, and each answers it in its own way. Embedding Shipping says that Express and Economy start from the standard option. Anything they don't redefine, they get for free. In this lesson they redefine everything, so the embedding shows the mechanism more than it saves code. It pays off when a type changes only one of several methods.

    Key takeaways

    • Embedding a type means writing its name alone in the struct.
    • An outer method with the same name shadows the embedded one, and value.Shipping.name() still reaches the original.
    • Embedded methods run with the embedded value as the receiver, so they never see the outer type's overrides.
    Keep going with Go500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free