Add validation and business logic to domain models
Enhance Tool, BinItem, and MultiBin classes with: - Input validation in constructors and property setters - Guard clauses for invalid states (negative values, empty names) - Business logic methods (CanFitItem, CalculateWaste, etc.) - Proper encapsulation with backing fields - Comprehensive XML documentation - Override Equals/GetHashCode for value semantics - Better ToString() implementations These changes enforce business rules at the domain level and prevent invalid states from being created. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,109 @@
|
|||||||
namespace CutList
|
using System;
|
||||||
|
|
||||||
|
namespace CutList
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a cutting tool with its kerf (blade width).
|
||||||
|
/// Enforces business rules for valid tools.
|
||||||
|
/// </summary>
|
||||||
public class Tool
|
public class Tool
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
private string _name;
|
||||||
|
private double _kerf;
|
||||||
|
|
||||||
public double Kerf { get; set; }
|
public Tool(string name, double kerf, bool allowUserToChange = false)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
throw new ArgumentException("Tool name cannot be empty", nameof(name));
|
||||||
|
|
||||||
|
if (kerf < 0)
|
||||||
|
throw new ArgumentException("Kerf cannot be negative", nameof(kerf));
|
||||||
|
|
||||||
|
_name = name;
|
||||||
|
_kerf = kerf;
|
||||||
|
AllowUserToChange = allowUserToChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameterless constructor for serialization only.
|
||||||
|
/// Use the parameterized constructor for creating valid instances.
|
||||||
|
/// </summary>
|
||||||
|
public Tool()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
throw new ArgumentException("Tool name cannot be empty", nameof(value));
|
||||||
|
_name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double Kerf
|
||||||
|
{
|
||||||
|
get => _kerf;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < 0)
|
||||||
|
throw new ArgumentException("Kerf cannot be negative", nameof(value));
|
||||||
|
_kerf = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool AllowUserToChange { get; set; }
|
public bool AllowUserToChange { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the total material wasted for a given number of cuts.
|
||||||
|
/// </summary>
|
||||||
|
public double CalculateTotalWaste(int numberOfCuts)
|
||||||
|
{
|
||||||
|
if (numberOfCuts < 0)
|
||||||
|
throw new ArgumentException("Number of cuts cannot be negative", nameof(numberOfCuts));
|
||||||
|
|
||||||
|
return Kerf * numberOfCuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this tool is compatible with the given material thickness.
|
||||||
|
/// Some tools may have minimum/maximum thickness requirements.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompatibleWithThickness(double thickness)
|
||||||
|
{
|
||||||
|
// For now, all tools are compatible with any thickness
|
||||||
|
// This can be extended based on specific tool requirements
|
||||||
|
return thickness > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is Tool other)
|
||||||
|
{
|
||||||
|
return Name == other.Name &&
|
||||||
|
Math.Abs(Kerf - other.Kerf) < 0.0001 &&
|
||||||
|
AllowUserToChange == other.AllowUserToChange;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = 17;
|
||||||
|
hash = hash * 23 + (Name?.GetHashCode() ?? 0);
|
||||||
|
hash = hash * 23 + Kerf.GetHashCode();
|
||||||
|
hash = hash * 23 + AllowUserToChange.GetHashCode();
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,97 @@
|
|||||||
namespace SawCut
|
using System;
|
||||||
|
|
||||||
|
namespace SawCut
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an item to be placed in a bin.
|
||||||
|
/// Enforces business rules for valid items.
|
||||||
|
/// </summary>
|
||||||
public class BinItem
|
public class BinItem
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
private string _name;
|
||||||
|
private double _length;
|
||||||
|
|
||||||
public double Length { get; set; }
|
public BinItem(string name, double length)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
throw new ArgumentException("Item name cannot be empty", nameof(name));
|
||||||
|
|
||||||
|
if (length <= 0)
|
||||||
|
throw new ArgumentException("Item length must be greater than zero", nameof(length));
|
||||||
|
|
||||||
|
_name = name;
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameterless constructor for serialization only.
|
||||||
|
/// Use the parameterized constructor for creating valid instances.
|
||||||
|
/// </summary>
|
||||||
|
public BinItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
throw new ArgumentException("Item name cannot be empty", nameof(value));
|
||||||
|
_name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double Length
|
||||||
|
{
|
||||||
|
get => _length;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value <= 0)
|
||||||
|
throw new ArgumentException("Item length must be greater than zero", nameof(value));
|
||||||
|
_length = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this item can fit in the given available length.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanFitIn(double availableLength)
|
||||||
|
{
|
||||||
|
return Length <= availableLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this item can fit in the given available length with spacing.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanFitInWithSpacing(double availableLength, double spacing)
|
||||||
|
{
|
||||||
|
return Length + spacing <= availableLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Name} ({Length}\")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is BinItem other)
|
||||||
|
{
|
||||||
|
return Name == other.Name && Length.IsEqualTo(other.Length);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = 17;
|
||||||
|
hash = hash * 23 + (Name?.GetHashCode() ?? 0);
|
||||||
|
hash = hash * 23 + Length.GetHashCode();
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,125 @@
|
|||||||
namespace SawCut
|
using System;
|
||||||
|
|
||||||
|
namespace SawCut
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a type of bin with quantity and priority.
|
||||||
|
/// Enforces business rules for valid bin configurations.
|
||||||
|
/// </summary>
|
||||||
public class MultiBin
|
public class MultiBin
|
||||||
{
|
{
|
||||||
public int Quantity { get; set; } = 1;
|
private int _quantity;
|
||||||
|
private double _length;
|
||||||
|
private int _priority;
|
||||||
|
|
||||||
public double Length { get; set; }
|
public MultiBin(double length, int quantity = 1, int priority = 25)
|
||||||
|
{
|
||||||
|
if (length <= 0)
|
||||||
|
throw new ArgumentException("Bin length must be greater than zero", nameof(length));
|
||||||
|
|
||||||
|
if (quantity < -1 || quantity == 0)
|
||||||
|
throw new ArgumentException("Quantity must be positive or -1 for unlimited", nameof(quantity));
|
||||||
|
|
||||||
|
_length = length;
|
||||||
|
_quantity = quantity;
|
||||||
|
_priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lower value priority with be used first. Default to 25
|
/// Parameterless constructor for serialization only.
|
||||||
|
/// Use the parameterized constructor for creating valid instances.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Priority { get; set; } = 25;
|
public MultiBin()
|
||||||
|
{
|
||||||
|
_quantity = 1;
|
||||||
|
_priority = 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quantity of bins available. Use -1 for unlimited bins.
|
||||||
|
/// </summary>
|
||||||
|
public int Quantity
|
||||||
|
{
|
||||||
|
get => _quantity;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < -1 || value == 0)
|
||||||
|
throw new ArgumentException("Quantity must be positive or -1 for unlimited", nameof(value));
|
||||||
|
_quantity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double Length
|
||||||
|
{
|
||||||
|
get => _length;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value <= 0)
|
||||||
|
throw new ArgumentException("Bin length must be greater than zero", nameof(value));
|
||||||
|
_length = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lower value priority will be used first. Default is 25.
|
||||||
|
/// </summary>
|
||||||
|
public int Priority
|
||||||
|
{
|
||||||
|
get => _priority;
|
||||||
|
set => _priority = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this bin type has unlimited quantity.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsUnlimited => Quantity == -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if an item of the given length can fit in this bin.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanFitItem(double itemLength)
|
||||||
|
{
|
||||||
|
return itemLength <= Length && itemLength > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the waste for a single bin if the given length is used.
|
||||||
|
/// </summary>
|
||||||
|
public double CalculateWaste(double usedLength)
|
||||||
|
{
|
||||||
|
if (usedLength < 0 || usedLength > Length)
|
||||||
|
throw new ArgumentException("Used length must be between 0 and bin length", nameof(usedLength));
|
||||||
|
|
||||||
|
return Length - usedLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var quantityStr = IsUnlimited ? "Unlimited" : Quantity.ToString();
|
||||||
|
return $"Bin {Length}\" (Qty: {quantityStr}, Priority: {Priority})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is MultiBin other)
|
||||||
|
{
|
||||||
|
return Quantity == other.Quantity &&
|
||||||
|
Length.IsEqualTo(other.Length) &&
|
||||||
|
Priority == other.Priority;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = 17;
|
||||||
|
hash = hash * 23 + Quantity.GetHashCode();
|
||||||
|
hash = hash * 23 + Length.GetHashCode();
|
||||||
|
hash = hash * 23 + Priority.GetHashCode();
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user