Last active
April 2, 2022 23:59
-
-
Save CharlieDigital/1a60bf5e8410f6cac37eef6488b8d245 to your computer and use it in GitHub Desktop.
Code listing showing object-oriented techniques for using structural logic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Product { | |
| constructor( | |
| public readonly name: string, | |
| public readonly weight: number, | |
| public readonly isLiquid: boolean = false | |
| ) { } | |
| } | |
| type ShippingMethod = 'USPS' | 'UPS' | 'FedEx' | 'DHL' | |
| abstract class ShippingStrategy { | |
| constructor( | |
| public readonly weight: number, | |
| public readonly hasLiquid: boolean | |
| ) { } | |
| // The default is that there is no surcharge | |
| get surcharge(): number { | |
| return 1; | |
| } | |
| // An abstract method has no body; it defines a contract | |
| // that an inheriting class has to fulfill. | |
| protected abstract calculate(): number; | |
| getShippingPrice() { | |
| const price = this.calculate() * this.surcharge; | |
| console.log(`Calculated shipping cost: ${price}`); // We can add centralized logic | |
| return price | |
| } | |
| } | |
| class UspsShippingStrategy extends ShippingStrategy { | |
| calculate(): number { | |
| let price; | |
| if (this.weight < 2) { | |
| price = 1.50; | |
| } | |
| else if (this.weight < 10) { | |
| price = 4.00; | |
| } | |
| else { | |
| price = 10.00; | |
| } | |
| return price; | |
| } | |
| override get surcharge(): number { | |
| return this.hasLiquid ? 1.25 : 1; | |
| } | |
| } | |
| class UpsShippingStrategy extends ShippingStrategy { | |
| calculate(): number { | |
| let price | |
| if (this.weight < 4) { | |
| price = 2.00; | |
| } | |
| else { | |
| price = 12.00; | |
| } | |
| return price; | |
| } | |
| } | |
| class FedExShippingStrategy extends ShippingStrategy { | |
| calculate(): number { | |
| return 0; // Exercise for the reader | |
| } | |
| } | |
| class DhlShippingStrategy extends ShippingStrategy { | |
| calculate(): number { | |
| return 0; // Exercise for the reader | |
| } | |
| } | |
| const shippingStrategies: | |
| Record<ShippingMethod, { | |
| new(weight: number, hasLiquid: boolean): ShippingStrategy | |
| }> = { | |
| USPS: UspsShippingStrategy, | |
| UPS: UpsShippingStrategy, | |
| FedEx: FedExShippingStrategy, | |
| DHL: DhlShippingStrategy | |
| } | |
| class Order { | |
| constructor( | |
| public readonly products: Product[], | |
| public readonly shippingMethod: ShippingMethod | |
| ) { } | |
| calculateShippingCost(): number { | |
| // Calculate the total weight. | |
| const weight = this.products.reduce( | |
| (previous, current) => previous + current.weight, 0 | |
| ); | |
| // Determine if we have a liquid | |
| const hasLiquid = this.products.some(p => p.isLiquid); | |
| const StrategyConstructor = shippingStrategies[this.shippingMethod]; | |
| const strategy = new StrategyConstructor(weight, hasLiquid); | |
| return strategy.getShippingPrice(); | |
| } | |
| } | |
| const cart = [ | |
| new Product('Microwave Popcorn', 1), | |
| new Product('Cooking Oil', 4, true), | |
| new Product('Chocolate Bar', 2) | |
| ] | |
| const order = new Order(cart, 'USPS'); | |
| console.log(order.calculateShippingCost()); | |
| // Run with: | |
| // tsc -t es5 delivery.ts | |
| // node delivery.js |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment