车辆路线分析类设计咨询:月度燃油成本计算方案优化建议
Hey Paul, let's break down your route cost calculation design and refine it to better fit your monthly unified fuel rate requirement, while keeping things clean and maintainable.
First, Let's Spot the Pain Points in Your Current Design
The core issue here is that your Route class is handling fuel cost logic, which doesn't align with your "monthly unified rate" rule. If every route needs to use the same rate, forcing each Route to store its own fuel cost creates redundancy and risk—you might accidentally set different rates for different routes, breaking your core requirement. Plus, it mixes data storage (route distance) with business logic (cost calculation) in the Route class, violating the single responsibility principle.
Optimized Class Structure
Here's a revised approach that keeps each class focused on its job, while fully supporting your monthly rate needs:
class FullRoute { private float $monthlyFuelRate; private array $routes = []; // Initialize with the monthly rate upfront to ensure it's set before adding routes public function __construct(float $monthlyFuelRate) { $this->monthlyFuelRate = $monthlyFuelRate; } public function addRoute(Route $route): void { $this->routes[] = $route; } public function getTotalDistance(): float { return array_reduce($this->routes, fn($sum, $route) => $sum + $route->getDistance(), 0.0); } public function getTotalMonthlyFuelCost(): float { // Total cost = total distance * unified monthly rate return $this->getTotalDistance() * $this->monthlyFuelRate; } // Optional: Get cost for a single route using the monthly rate public function getSingleRouteCost(Route $route): float { return $route->getDistance() * $this->monthlyFuelRate; } } class Route { private float $distance; // Initialize distance via constructor to avoid accidental post-creation changes public function __construct(float $distance) { $this->distance = $distance; } public function getDistance(): float { return $this->distance; } // Optional: If you ever need to calculate a route's cost with a one-off rate public function calculateCostWithRate(float $fuelRate): float { return $this->distance * $fuelRate; } }
Why This Works Better
- Single Responsibility:
Routeonly cares about storing its own distance data—no cost logic here.FullRoutehandles the unified rate management and all aggregation (total distance, total cost). - Eliminates Redundancy: No need to set the same fuel rate on every individual route; it's managed once at the monthly level.
- Data Consistency: Since the rate is set once in
FullRoute, you can't have inconsistent rates across routes by mistake. - Flexibility: If you ever need to adjust cost logic (e.g., add toll fees later), you can modify
FullRouteor extend it without touching theRouteclass.
Bonus: Scalable Extension with Strategy Pattern
If you think you might need to support different cost-calculation rules down the line (e.g., mixing fuel costs with tolls, or seasonal rates), you can decouple the cost logic even further with a strategy pattern:
// Define a contract for cost calculators interface RouteCostCalculator { public function calculateTotalCost(array $routes): float; } // Fuel-only calculator for your current need class FuelCostCalculator implements RouteCostCalculator { private float $fuelRate; public function __construct(float $fuelRate) { $this->fuelRate = $fuelRate; } public function calculateTotalCost(array $routes): float { $totalDistance = array_reduce($routes, fn($sum, $route) => $sum + $route->getDistance(), 0.0); return $totalDistance * $this->fuelRate; } } // Updated FullRoute that depends on the calculator interface class FullRoute { private array $routes = []; private RouteCostCalculator $costCalculator; public function __construct(RouteCostCalculator $costCalculator) { $this->costCalculator = $costCalculator; } public function addRoute(Route $route): void { $this->routes[] = $route; } public function getTotalDistance(): float { return array_reduce($this->routes, fn($sum, $route) => $sum + $route->getDistance(), 0.0); } public function getTotalCost(): float { return $this->costCalculator->calculateTotalCost($this->routes); } }
Now, if you need to add tolls, you just create a FuelAndTollCostCalculator that implements RouteCostCalculator—no changes to FullRoute or Route required. This follows the open/closed principle (open for extension, closed for modification).
内容的提问来源于stack exchange,提问作者Paul

