ShippingCostCalculator.java

package com.university.bookstore.visitor;

import com.university.bookstore.model.AudioBook;
import com.university.bookstore.model.EBook;
import com.university.bookstore.model.Magazine;
import com.university.bookstore.model.Material;
import com.university.bookstore.model.Media;
import com.university.bookstore.model.PrintedBook;
import com.university.bookstore.model.VideoMaterial;

/**
 * Concrete visitor implementation for calculating shipping costs.
 * Demonstrates the Visitor pattern by providing different shipping
 * cost calculations based on material type.
 * 
 * <p>Shipping costs vary by material type:
 * - Physical items: $0.50 per 100g
 * - Digital items: $0 (instant download)
 * - Magazines: $2 flat rate</p>
 * 
 * @author Navid Mohaghegh
 * @version 2.0
 * @since 2024-09-15
 */
public class ShippingCostCalculator implements MaterialVisitor {
    
    private static final double PHYSICAL_ITEM_RATE = 0.50; // per 100g
    private static final double MAGAZINE_FLAT_RATE = 2.00;
    private static final double DIGITAL_ITEM_RATE = 0.00;
    
    private double totalShippingCost = 0.0;
    
    /**
     * Calculates shipping cost for a printed book based on weight.
     * Assumes average book weight of 500g.
     * 
     * @param book the printed book
     */
    @Override
    public void visit(PrintedBook book) {
        // Assume average book weight of 500g
        double weightInHundredGrams = 5.0;
        double cost = weightInHundredGrams * PHYSICAL_ITEM_RATE;
        totalShippingCost += cost;
    }
    
    /**
     * Calculates flat rate shipping cost for magazines.
     * 
     * @param magazine the magazine
     */
    @Override
    public void visit(Magazine magazine) {
        totalShippingCost += MAGAZINE_FLAT_RATE;
    }
    
    /**
     * Calculates shipping cost for audio books.
     * Physical CDs: weight-based, Digital: free
     * 
     * @param audioBook the audio book
     */
    @Override
    public void visit(AudioBook audioBook) {
        if (audioBook.getQuality() == Media.MediaQuality.PHYSICAL) {
            // Assume CD weight of 100g
            double weightInHundredGrams = 1.0;
            double cost = weightInHundredGrams * PHYSICAL_ITEM_RATE;
            totalShippingCost += cost;
        } else {
            // Digital download - no shipping cost
            totalShippingCost += DIGITAL_ITEM_RATE;
        }
    }
    
    /**
     * Calculates shipping cost for video materials.
     * Physical DVDs: weight-based, Digital: free
     * 
     * @param video the video material
     */
    @Override
    public void visit(VideoMaterial video) {
        if (video.getQuality() == Media.MediaQuality.PHYSICAL) {
            // Assume DVD weight of 150g
            double weightInHundredGrams = 1.5;
            double cost = weightInHundredGrams * PHYSICAL_ITEM_RATE;
            totalShippingCost += cost;
        } else {
            // Digital download - no shipping cost
            totalShippingCost += DIGITAL_ITEM_RATE;
        }
    }
    
    /**
     * Calculates shipping cost for e-books.
     * E-books are always digital downloads with no shipping cost.
     * 
     * @param ebook the e-book
     */
    @Override
    public void visit(EBook ebook) {
        // E-books are always digital - no shipping cost
        totalShippingCost += DIGITAL_ITEM_RATE;
    }
    
    /**
     * Gets the total shipping cost calculated so far.
     * 
     * @return the total shipping cost
     */
    public double getTotalShippingCost() {
        return totalShippingCost;
    }
    
    /**
     * Resets the shipping cost calculator for a new calculation.
     */
    public void reset() {
        totalShippingCost = 0.0;
    }
    
    /**
     * Calculates shipping cost for a single material.
     * 
     * @param material the material to calculate shipping for
     * @return the shipping cost for this material
     */
    public double calculateShippingCost(Material material) {
        reset();
        
        // Use pattern matching or instanceof to determine the correct visit method
        if (material instanceof PrintedBook) {
            visit((PrintedBook) material);
        } else if (material instanceof Magazine) {
            visit((Magazine) material);
        } else if (material instanceof AudioBook) {
            visit((AudioBook) material);
        } else if (material instanceof VideoMaterial) {
            visit((VideoMaterial) material);
        } else if (material instanceof EBook) {
            visit((EBook) material);
        } else {
            throw new IllegalArgumentException("Unknown material type: " + material.getClass().getSimpleName());
        }
        
        return totalShippingCost;
    }
}