One possible solution to make the code more readable and maintainable is to use inheritance and polymorphism. Instead of having separate classes for each option bill, create a base class called OptionBill and have each option bill class extend from it. Then, move the common code from the calculate() method to the base class and make it abstract. This way, each option bill class only needs to implement the specific rules for that option.
Here's an example implementation:
abstract class OptionBill
{
protected $id;
protected $consumption;
protected $amount;
protected $option;
protected $service;
public function __construct($id, $option, $service)
{
$this->id = $id;
$this->option = $option;
$this->service = $service;
}
public function calculate($save)
{
// common calculation code
$this->consumption = $this->calculateConsumption();
$this->amount = $this->calculateAmount();
if ($save) {
$this->save();
}
return $this->amount;
}
protected abstract function calculateConsumption();
protected abstract function calculateAmount();
protected function save()
{
// save to database
}
}
class Option1Bill extends OptionBill
{
protected function calculateConsumption()
{
// specific calculation for option 1
}
protected function calculateAmount()
{
// specific calculation for option 1
}
}
class Option2Bill extends OptionBill
{
protected function calculateConsumption()
{
// specific calculation for option 2
}
protected function calculateAmount()
{
// specific calculation for option 2
}
}
// and so on for other option bills
Then, in the Service class, you can create an array of option bill objects and loop through them to calculate and sum the amounts:
class Service
{
protected $id;
protected $options = [];
public function __construct($id)
{
$this->id = $id;
$this->options[] = new Option1Bill(1, $this->getOption(1), $this);
$this->options[] = new Option2Bill(2, $this->getOption(2), $this);
// add other option bills here
}
public function calculate($save)
{
$totalAmount = 0;
foreach ($this->options as $option) {
$totalAmount += $option->calculate($save);
}
return $totalAmount;
}
protected function getOption($id)
{
// get option from database
}
}
This way, you have a more modular and extensible code that is easier to read and maintain.