Start learning free

LESSON 564 OF 574 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Shipping Options in Swift: Overriding 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. So each one overrides the parent's methods with its own versions.

Implement the class Shipping, the standard option:

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

and two classes that inherit from Shipping and override both methods:

  • Express: name() returns "Express", and cost(weight) returns 10 + 2 * weight.
  • Economy: name() returns "Economy", and cost(weight) returns 3 plus half the weight, rounded down to a whole number: half of 7 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 classes creates that option and prints <name>: <cost> from its name() and cost(weight). The two calls are the same for every option (where variables have types, through a variable of the parent type Shipping); because the methods are overridden, each option answers with its own version. The starter code spells these names the way your language does.

 

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 + 2 = 5 with Economy (half of 4 is 2).

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: half of 7 is 3, so 3 + 3 = 6, and half of 1 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 6
    Ctrl + Enter to check

    SWIFT · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Shipping Options in Swift, explained

    Problem overview

    A shop has three ways to ship a parcel, and each answers the same two questions: what is the option called, and what does it cost for a given weight? Shipping is the standard option. Express and Economy inherit from it and give their own answers to both questions.

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

    For a parcel of 4 kilograms that is 9, 18 and 5.

    Approach

    The parent class holds the standard rules. A subclass is written with : Shipping after its name, and each method it wants to replace is written again with the same header and the keyword override. The code that uses the options can then hold any of them in a variable of type Shipping and call name() and cost(_:). Swift runs the version that belongs to the object's real class, not to the variable's type.

    Step by step

    1. In Shipping, name() returns "Standard".
    2. In Shipping, cost(_ weight: Int) returns 5 + weight.
    3. In Express, name() has the parent's header plus override, and returns "Express".
    4. In Express, cost returns 10 + 2 * weight.
    5. In Economy, name() returns "Economy".
    6. In Economy, cost returns 3 + weight / 2.

    The solution in Swift

    class Shipping {
        func name() -> String {
            return "Standard"
        }
    
        func cost(_ weight: Int) -> Int {
            return 5 + weight
        }
    }
    
    class Express: Shipping {
        override func name() -> String {
            return "Express"
        }
    
        override func cost(_ weight: Int) -> Int {
            return 10 + 2 * weight
        }
    }
    
    class Economy: Shipping {
        override func name() -> String {
            return "Economy"
        }
    
        override func cost(_ weight: Int) -> Int {
            return 3 + weight / 2
        }
    }
    

    Here the three options sit in one array of type [Shipping], and the same two calls run on each:

    let options: [Shipping] = [Shipping(), Express(), Economy()]
    for o in options { print("\(o.name()): \(o.cost(4))") }
    print("--")
    for o in options { print("\(o.name()): \(o.cost(0))") }
    
    Standard: 9
    Express: 18
    Economy: 5
    --
    Standard: 5
    Express: 10
    Economy: 3
    

    With a weight of 0 only the base prices are left: 5, 10 and 3.

    Swift details worth knowing

    • override is required. A method that replaces the parent's must say so. Leaving it out is a compile error: overriding declaration requires an 'override' keyword.
    • Each class overrides only for itself. Changing Express does nothing to Economy or to Shipping.
    • Integer division rounds down. Both operands of weight / 2 are Int, so the remainder is dropped.

    Edge cases and common mistakes

    • Odd weights in Economy. Dividing 7 by 2 gives 3, and 1 by 2 gives 0, so the costs are 6 and 3.
    • Forgetting one override. If Express had no cost, an express parcel would get the parent's 5 + weight: 12 for 7 kilograms instead of 24.
    • Weight 0. It is allowed, and the price is just the base.
    • Different headers. An override must have the same name, parameter label and types as the parent's method. Here that is cost(_ weight: Int) -> Int in all three classes; a different header would be a new, unrelated method.

    Testing

    Try these in a file of your own. On the solution above, Economy().cost(7) is 6, Economy().cost(1) is 3, Shipping().cost(7) is 12 and Express().cost(7) is 24, and a variable declared as let e: Shipping = Economy() still answers "Economy" to e.name().

    Testing

    Try these in a file of your own. On the solution above, a parcel of 4 kilograms prints Standard: 9, Express: 18 and Economy: 5, and a parcel of 0 kilograms prints Standard: 5, Express: 10 and Economy: 3. Beyond the numbers, check two things about the classes themselves:

    • A Shipping object gives 12 for cost(7), while an Express object gives 24: if both give 12, the override is missing.
    • An array typed [Shipping] holding the three options must still print three different names. If all three print Standard, the subclasses' methods are not overriding the parent's.

    Tracing the third example

    The parcels are economy 7, economy 1, standard 7 and express 7. Each one builds the matching class and calls the same two methods:

    • Economy with 7: the base is 3 and 7 / 2 is 3, so the cost is 6.
    • Economy with 1: 1 / 2 is 0, so the cost is just the base, 3.
    • Standard with 7: 5 + 7 is 12.
    • Express with 7: 10 + 2 * 7 is 24.

    The printed lines are Economy: 6, Economy: 3, Standard: 12 and Express: 24, as the parcels went in. Only the class decides the number, so a line for any weight from 0 to 1000 follows the same pattern.

    Why use inheritance here

    The alternative is one class with a text field for the kind and an if chain inside cost. Every new option then means editing that chain. With subclasses, a new option is a new class, and the code that calls name() and cost(_:) doesn't change. For example, an overnight option can reuse the standard price through super:

    class Overnight: Shipping {{
        override func name() -> String {{
            return "Overnight"
        }}
    
        override func cost(_ weight: Int) -> Int {{
            return super.cost(weight) + 20
        }}
    }}
    let all: [Shipping] = [Shipping(), Express(), Economy(), Overnight()]
    for o in all {{ print(o.name(), o.cost(7)) }}
    
    Standard 12
    Express 24
    Economy 6
    Overnight 32
    

    super.cost(weight) runs the parent's version, 5 + 7 = 12, and the subclass adds 20 to it. The lesson's Express and Economy don't call super: each of them replaces the parent's formula completely, so they don't need to.

    Reading the same parcel across the options

    For a parcel of 7 kilograms, the four calls above show how one weight gives four prices. Standard is the base plus one per kilogram, Express charges double per kilogram, and Economy charges only for half the weight. Because Economy rounds down, 6 and 7 kilograms cost the same there, while the other options go up with every kilogram.

    Key takeaways

    • A subclass replaces a parent's method by repeating its header with override.
    • Calling a method through the parent's type runs the object's own version.
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free