(ns v2 (:require [clojure.spec.alpha :as s] [clojure.string :as str])) (s/def ::distance (s/and number? pos?)) (s/def ::package string?) (s/def ::destination string?) (s/def ::delivery-type #{"air" "land" "sea"}) (defprotocol DeliveryService (calculate-cost [this distance] "Calculates the delivery cost based on the distance.") (process-delivery [this package destination] "Executes the delivery of the package to the destination.") (estimated-time [this distance] "Estimates delivery time in hours.")) (defrecord AirDelivery [] DeliveryService (calculate-cost [_ distance] (* 5 distance)) (process-delivery [_ package destination] (str "Package '" package "' will be delivered via Air to " destination)) (estimated-time [_ distance] (Math/ceil (/ distance 800)))) (defrecord LandDelivery [] DeliveryService (calculate-cost [_ distance] (* 2 distance)) (process-delivery [_ package destination] (str "Package '" package "' will be delivered via Land to " destination)) (estimated-time [_ distance] (Math/ceil (/ distance 60)))) (defrecord SeaDelivery [] DeliveryService (calculate-cost [_ distance] (* 1 distance)) (process-delivery [_ package destination] (str "Package '" package "' will be delivered via Sea to " destination)) (estimated-time [_ distance] (Math/ceil (/ distance 30)))) (defmulti create-delivery-service "Factory multi-method for creating delivery services" (fn [type & _] (str/lower-case type))) (defmethod create-delivery-service "air" [_] (->AirDelivery)) (defmethod create-delivery-service "land" [_] (->LandDelivery)) (defmethod create-delivery-service "sea" [_] (->SeaDelivery)) (defmethod create-delivery-service :default [type] (throw (ex-info "Invalid delivery type" {:type type :available-types #{"air" "land" "sea"}}))) (defn calculate-and-deliver "Service that uses the factory to calculate the cost and deliver a package. Returns a map with :cost, :delivery, and :estimated-time keys." [type package destination distance] {:pre [(s/valid? ::delivery-type type) (s/valid? ::package package) (s/valid? ::destination destination) (s/valid? ::distance distance)]} (try (let [service (create-delivery-service type)] {:cost (calculate-cost service distance) :delivery (process-delivery service package destination) :estimated-time (estimated-time service distance)}) (catch Exception e (throw (ex-info "Delivery calculation failed" {:cause (.getMessage e) :type type :package package :destination destination :distance distance}))))) (defn find-cheapest-delivery "Finds the cheapest delivery service for given parameters" [package destination distance] (->> ["air" "land" "sea"] (map #(-> [(calculate-and-deliver % package destination distance) %])) (sort-by (comp :cost first)) first)) (comment (calculate-and-deliver "air" "Electronics" "São Paulo" 1000) ;; => {:cost 5000 ;; :delivery "Package 'Electronics' will be delivered via Air to São Paulo" ;; :estimated-time 2} (find-cheapest-delivery "Heavy Machinery" "Porto Alegre" 800) ;; => [{:cost 800 ;; :delivery "Package 'Heavy Machinery' will be delivered via Sea to Porto Alegre" ;; :estimated-time 27} ;; "sea"] ;; Validation error example (calculate-and-deliver "air" "Books" "Curitiba" -100) ;; => Assertion Error: Invalid input )
![Cover image for Clojure Is Awesome!!! [PART 5]](https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8zob7rc418omdle4e2yo.jpg)
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)