Note
This question was born in request to post more details from this question on Programmers. Since details may uncover new/different complexity, I'm asking another question here. I also will use this question for reference purposes as it more fully defines my application.
Currently I am seeking to improve
- storing/retrieving pricing information for a specific product
- computing pricing information for a specific product
Right now the above information is retrieved and computed via if/then/else statement blocks.
CODE
class Price
{
public $product;
public $base;
public $assembly;
public $discount;
public $total;
public $motorPart1;
public $motorOption1;
public $pumpPart2;
public $pumpPart3;
public $pumpOption2;
public $pumpOption3;
function load()
{
if ($this->product == "MOTOR")
{
$this->base = 200;
$this->motorPart1 = 90;
$this->motorOption1 = 21;
}
elseif ($this->product == "PUMP")
{
$this->base = 500;
$this->pumpPart2 = 70;
$this->pumpPart3 = 110;
$this->pumpOption2 = 13;
$this->pumpOption3 = 19;
}
}
}
class Calc
{
public $price;
public $spec;
public $option;
function __construct()
{
$this->spec = new Spec();
$this->price = new Price();
$this->option = new Option();
}
function calcPrice()
{
$this->spec->load();
if ($this->spec->product == "MOTOR")
{
$this->price->assembly = $this->price->base
+ $this->price->motorPart1
* $this->spec->motorPart1Count;
$this->price->total = $this->price->assembly * 1.1;
if ($this->option->motorOption1)
$this->price->total += $this->price->motorOption1;
$this->price->discount = $this->price->total * 0.05;
$this->price->total -= $this->price->discount;
}
elseif ($this->spec->product == "PUMP")
{
$this->price->assembly = $this->price->base
+ $this->price->pumpPart2
* $this->spec->pumpPart2Count
+ $this->price->pumpPart3
* $this->spec->pumpPart3Count;
$this->price->total = $this->price->assembly * 1.1;
if ($this->option->pumpOption2)
$this->price->total += $this->price->pumpOption2;
if ($this->option->pumpOption3)
$this->price->total += $this->price->pumpOption3;
$this->price->discount = $this->price->total * 0.05;
$this->price->total -= $this->price->discount;
}
}
}
class Option
{
public $motorOption1;
public $pumpOption2;
public $pumpOption3;
}
class Spec
{
public $product;
public $motorPart1Count;
public $pumpPart2Count;
public $pumpPart3Count;
function load()
{
if ($this->product == "MOTOR")
{
$this->motorPart1Count = 13;
}
elseif ($this->product == "PUMP")
{
$this->pumpPart2Count = 19;
$this->pumpPart3Count = 21;
}
}
}
class MotorController
{
private $calc;
function __construct()
{
$this->calc = new Calc();
}
function controllerAction()
{
$this->calc->price->product = "MOTOR";
$this->calc->spec->product = "MOTOR";
// option data is specified through HTTP POST (hardcoded here)
$this->calc->option->motorOption1 = true;
$this->calc->price->load();
$this->calc->calcPrice();
print "MOTOR:" . PHP_EOL;
print "Motor Total:\t" . $this->calc->price->total . PHP_EOL;
print "Discount:\t" . $this->calc->price->discount . PHP_EOL;
if ($this->calc->option->motorOption1)
print "Option 1 cost:\t" . $this->calc->price->motorOption1 . PHP_EOL;
print "-----------------------" . PHP_EOL;
}
}
class PumpController
{
private $calc;
function __construct()
{
$this->calc = new Calc();
}
function controllerAction()
{
$this->calc->price->product = "PUMP";
$this->calc->spec->product = "PUMP";
// option data is specified through HTTP POST (hardcoded here)
$this->calc->option->pumpOption2 = true;
$this->calc->option->pumpOption3 = false;
$this->calc->price->load();
$this->calc->calcPrice();
print "PUMP:" . PHP_EOL;
print "Pump Total:\t" . $this->calc->price->total . PHP_EOL;
print "Discount:\t" . $this->calc->price->discount . PHP_EOL;
if ($this->calc->option->pumpOption2)
print "Option 2 cost:\t" . $this->calc->price->pumpOption2 . PHP_EOL;
if ($this->calc->option->pumpOption3)
print "Option 3 cost:\t" . $this->calc->price->pumpOption3 . PHP_EOL;
print "-----------------------" . PHP_EOL;
}
}
// Example of Motor Controller use:
$motorController = new MotorController();
$motorController->controllerAction();
// Example of Pump Controller use
$pumpController = new PumpController();
$pumpController->controllerAction();
Above code compiles and runs. It fairly accurately represents the real-world application scenario that I am working on.
Questions
Application/Example Differences
User uses HTML form, chooses product, chooses options, then submits. Appropriate Product Controller picks up from there, does some processing, then sends results to the view. In my example above I chose to hardcode HTML form selections which are normally made by the user. I've also chosen to hardcode corresponding View output. The hardcoded code is in the appropriate Controllers.
Simplifications:
Example code contains two types -- "Pump" and "Motor". Actual application contains about 6 different types of Pumps with similar and differing characteristics, and a couple of business-specific non-pump object, which I chose to be represented as a Motor. More Pump and non-pump types to be expected in the real app, as application develops.
Use Case (using Pump)
User picks Pump Product type (appropriate Controller), selects available Pump option #3 out of the options available, submits. User gets resulting Pricing for the chosen option.
What am I looking for, aka "My Question"
Ideally I am seeking for ways to do step-wise refactoring. A single-step total rewire of the application is prohibitive even if possible, simply due to the complexity/verbosity of the app and the amounts of tests to fix while rewriting. Nevertheless I am seeking a final destination:
- a set of Design Patterns that will make the if/then/else mess go away
- make the code more maintainable.
- reduce complexity
getPrice
function will keep if/then/else blocks for each product type, correct? It will also need to store every property related to each product type and corresponding algorithm to compute price for each product type. That can be a bit messy and long. \$\endgroup\$ – Dennis Nov 18 '15 at 23:05getPrice
function. Because of polymorphism, if they share the same interface (for the shared functiongetPrice
), the product type will be looked up at run time in order to call the appropriate function. I'm not familiar enough with PHP to code a solution for you, but can demonstrate this in Java if you like. \$\endgroup\$ – Kyle Falconer Nov 19 '15 at 0:06getPrice
\$\endgroup\$ – Kyle Falconer Nov 19 '15 at 0:09