|
1 | | -# 🚀 Requirements |
| 1 | +# Warehouse Kata – Instructions |
2 | 2 |
|
3 | | -Add requirements here |
| 3 | +Welcome! In this exercise you will implement a small domain around a Warehouse with products. Your goal is to: |
| 4 | +- Create and complete several classes/interfaces under src/main/java that are currently missing or partially implemented. |
| 5 | +- Pick correct data types for parameters and return values so the project compiles and tests can run. |
| 6 | +- Make all tests in BasicTest green first. |
| 7 | +- Commit after major steps. When all BasicTest tests are green, push your commits. |
| 8 | +- Extra credit: implement the advanced features so that EdgeCaseTest is also green. |
| 9 | + |
| 10 | +## 1) Getting started |
| 11 | +1. Open this project in your IDE (IntelliJ IDEA recommended). |
| 12 | +2. Ensure you have JDK 25. |
| 13 | +3. Build once to see current state: |
| 14 | + - ./mvnw compile |
| 15 | + |
| 16 | +## 2) What you will need to create/finish |
| 17 | +You will work primarily in src/main/java/com/example. Some files already exist but contain TODOs or empty methods. Implement the following: |
| 18 | + |
| 19 | +- Category (value object) |
| 20 | + - Use a private constructor and a public static factory Category.of(String name). |
| 21 | + - Validate input: null => "Category name can't be null"; empty/blank => "Category name can't be blank". |
| 22 | + - Normalize name with initial capital letter (e.g., "fruit" -> "Fruit"). |
| 23 | + - Cache/flyweight: return the same instance for the same normalized name. |
| 24 | + |
| 25 | +- Product (abstract base class) |
| 26 | + - Keep UUID id, String name, Category category, BigDecimal price. |
| 27 | + - Provide getters named uuid(), name(), category(), price() and a setter price(BigDecimal). |
| 28 | + - Provide an abstract String productDetails() for polymorphism. |
| 29 | + |
| 30 | +- FoodProduct (extends Product) |
| 31 | + - Implements Perishable and Shippable. |
| 32 | + - Fields: LocalDate expirationDate, BigDecimal weight (kg). |
| 33 | + - Validations: negative price -> IllegalArgumentException("Price cannot be negative."); negative weight -> IllegalArgumentException("Weight cannot be negative."). |
| 34 | + - productDetails() should look like: "Food: Milk, Expires: 2025-12-24". |
| 35 | + - Shipping rule: cost = weight * 50. |
| 36 | + |
| 37 | +- ElectronicsProduct (extends Product) |
| 38 | + - Implements Shippable. |
| 39 | + - Fields: int warrantyMonths, BigDecimal weight (kg). |
| 40 | + - Validation: negative warranty -> IllegalArgumentException("Warranty months cannot be negative."). |
| 41 | + - productDetails() should look like: "Electronics: Laptop, Warranty: 24 months". |
| 42 | + - Shipping rule: base 79, add 49 if weight > 5.0 kg. |
| 43 | + |
| 44 | +- Interfaces |
| 45 | + - Perishable: expose expirationDate() and a default isExpired() based on LocalDate.now(). |
| 46 | + - Shippable: expose calculateShippingCost() and weight() (used by shipping optimizer in extra tests). |
| 47 | + |
| 48 | +- Warehouse (singleton per name) |
| 49 | + - getInstance(String name) returns the same instance per unique name. |
| 50 | + - addProduct(Product): throw IllegalArgumentException("Product cannot be null.") if null. |
| 51 | + - getProducts(): return an unmodifiable copy. |
| 52 | + - getProductById(UUID): return Optional. |
| 53 | + - updateProductPrice(UUID, BigDecimal): when not found, throw NoSuchElementException("Product not found with id: <uuid>"). Also track changed products in getChangedProducts(). |
| 54 | + - expiredProducts(): return List<Perishable> that are expired. |
| 55 | + - shippableProducts(): return List<Shippable> from stored products. |
| 56 | + - remove(UUID): remove the matching product if present. |
| 57 | + |
| 58 | +- WarehouseAnalyzer (extra credit) |
| 59 | + - Implement the advanced methods used by EdgeCaseTest: price-range search (inclusive), expiring-within-days, case-insensitive name search, above-price search, weighted average per category (round to 2 decimals), price outliers (population stddev), shipping group optimization (first‑fit decreasing by weight), expiration-based discounts, inventory validation summary, and inventory statistics. |
| 60 | + |
| 61 | +## 3) Workflow to follow |
| 62 | +1. Implement the missing classes/interfaces and methods so the project compiles. |
| 63 | +2. Run tests: |
| 64 | + - Basic first: ./mvnw -Dtest=BasicTest test |
| 65 | + - When green, commit with a clear message. |
| 66 | +3. Extra credit: make EdgeCaseTest green: |
| 67 | + - ./mvnw -Dtest=EdgeCaseTest test |
| 68 | +4. Commit after each major milestone (e.g., "Implement Product & FoodProduct", "Warehouse behaviors", "Analyzer advanced features"). |
| 69 | +5. Push when BasicTest is fully green (and EdgeCaseTest too if you do the extra credit). |
| 70 | + |
| 71 | +## 4) Tips |
| 72 | +- Prefer BigDecimal for prices and weights (exact values in tests). Where an interface requires Double (e.g., weight()), convert BigDecimal to double on return. |
| 73 | +- Always round monetary results to 2 decimals using HALF_UP when tests assert exact values. |
| 74 | +- Keep public APIs exactly as tests expect (method names, exception messages). |
| 75 | +- Ensure Warehouse.clearProducts() is called in tests; do not share state between tests. |
| 76 | + |
| 77 | +Good luck and have fun! |
0 commit comments