[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/HUcMlwx_) :sectnums: :nofooter: :toc: left :icons: font :data-uri: :source-highlighter: highlightjs :stem: = Int.04 -- Shipping Costs You are tasked with implementing a shipping cost calculation component of a webshop. The store is based in Austria and ships to several other countries. But the owner does not like their neighbors, so they decided to not ship to Austria 🙃 Three shipping options are available to the customers: . Self-Pickup: come by the store and pick up your order ** Free of charge! . Leonding Parcel Service (LPS) ** Fast, affordable, operated by always reliable students of the local HTL . LuxuryShipping ** Boxes are only handled with single-use silk gloves by employees who attended a butler school in Switzerland The handling fee of the shop itself, together with the actual billing of the product(s) is handled by a different component of the webshop, so you don't have to concern yourself with these matters. You have worked out the following data model: [plantuml] ---- @startuml hide empty fields hide empty methods interface ICountryDistanceProvider { +CountryDistanceInformation? GetDistanceTo(string countryName) +bool IsPossibleCountry(string countryName) } struct CountryDistanceInformation { +int ApproxDistance [readonly] +bool IsDirectNeighbor [readonly] +bool IsEuMember [readonly] } interface IMeasuredBox { +int Width [readonly] +int Height [readonly] +int Depth [readonly] +double Weight [readonly] } interface IShippingCostCalculator { +string CarrierName [readonly] +decimal? CalculateShippingCosts(string targetCountry, IMeasuredBox box) } class CountryDistanceProvider <> { {static} -Dictionary distanceDatabase [readonly] } class Package <> { -int _width [readonly] -int _height [readonly] -int _depth [readonly] -double _weight [readonly] {static} -T SanitizeValue(T value) [T : INumber] } class SelfPickup <> {} abstract class ShippingCostCalculatorBase { #ICountryDistanceProvider CountryDistanceProvider [readonly] #ShippingCostCalculatorBase(ICountryDistanceProvider countryDistanceProvider) {static} #int SumOfLongestAndShortestSides(IMeasuredBox box) } class LPSShippingCostCalculator <> { +LPSShippingCostCalculator(ICountryDistanceProvider countryDistanceProvider) -decimal CalcDistanceCost(string targetCountry) } class LuxuryShippingCostCalculator <> { +LuxuryShippingCostCalculator(ICountryDistanceProvider countryDistanceProvider) } ICountryDistanceProvider <|.. CountryDistanceProvider IMeasuredBox <|.. Package IShippingCostCalculator <|.. SelfPickup IShippingCostCalculator <|.. ShippingCostCalculatorBase ShippingCostCalculatorBase <|-- LPSShippingCostCalculator ShippingCostCalculatorBase <|-- LuxuryShippingCostCalculator ShippingCostCalculatorBase *-- ICountryDistanceProvider @enduml ---- == Implementation You have been provided with a skeleton code, unit tests and XMLDoc comments. In addition, there is an almost complete `Program` which provides a simple UI. Complete the application! === `CountryDistanceProvider` * A _default_ country distance provider * Contains information about all countries the store ships to ** Approximate distance (calculated as great-circle distance between the capital cities) ** If the country shares a border with Austria ** If the country is a member of the EU * Do you feel comfortable with the dictionary initializer? === `Package` * Simple implementation of `IMeasuredBox` * Hint: the interface demands `get` properties, that does _not_ stop you from adding `init` setters * The `SanitizeValue` method allows to ensure that numeric values are not stem:[<0] ** It can be used for both `int` & `double` ** To allow for this flexibility it is a _generic math_ method, with a type parameter constraint of `T : INumber` => this ensures, that `T.Zero` exists for every `T` === `SelfPickup` * A very simple shipping cost 'calculator' => self pickup is always free of charge * The 'carrier name' is `Self Pickup` === `ShippingCostCalculatorBase` * Base class for shipping providers which need to calculate shipping cost based on distance and package measurements * `SumOfLongestAndShortestSides` determines the sum of the longest and shortest side ** Example: `120x190x340` => `460` === `LPSShippingCostCalculator` * Can only ship to a country known to the distance provider * Total shipping cost is calculated based on three parts: .. Weight *** Max. 20€ *** stem:[< 1.5"kg"]: 0€ *** stem:[< 2"kg"]: 0.5€ *** stem:[< 3"kg"]: 1.5€ *** stem:[>= 3"kg"]: stem:[x * 0.55]€ .. Size *** stem:[< 250"mm"]: 0€ *** stem:[< 400"mm"]: 0.75€ *** stem:[< 650"mm"]: 1.25€ *** stem:[< 900"mm"]: 2.6€ *** stem:[>= 900"mm"]: 3.5€ .. Distance *** Base price 2.99€ *** If _not_ an EU member +2.49€ *** Per 500km +1.1€ (added at least once) **** If direct neighbor: 25% reduction of price per 500km * The 'carrier name' is `Leonding Parcel Service` === `LuxuryShippingCostCalculator` * Can only ship to a country known to the distance provider * Total shipping cost is calculated as follows: ** Max. 4444€ ** Box size cost (dimensions in centimeter): stem:[("height"*"width"*"depth")^2] *** 'Länge mal Breite mal Höhe zum Quadrat' ** Total cost: stem:["km" * "boxSizeCost"] * The 'carrier name' is `LuxuryShipping` == Sample Run Here a recording of a sample run of the program, including invalid input retries: video::pics/sample_run.mp4[Sample Run, width=800px]