1
\$\begingroup\$

I'm working on figuring out the best way to design this so that it's well organized and it seems like the factory design pattern makes sense.

Updated: What I'm building: - Pricing calculator - There are 4 products each having their own specific pricing rules that vary from user input - The person can choose between 1 product or all products

Should I have a Calculator class to determine product pricing or leave the calculations in each product type?

/*
 * Use Factory Pattern to build objects
 */

class ProductFactory {
    public static function createProduct($type) {
        switch ($type) {
            case 'solution1':
                return new Solution1Product();
                break;

            case 'solution2':
                return new Solution2Product();
                break;

            case 'solution3':
                return new Solution3Product();
                break;

            case 'solution4':
                return new Solution4Product();
                break;
        }

    }
}


/*
 * Abstract Product
 */

abstract class AbstractProduct {
    protected $price;
    protected $max_limit;
    protected $max_amount;

    abstract public function calculatePrice($dob, $gender, $amount);
}


/*
 * Solution 1 Product
 */
class Solution1Product extends AbstractProduct {
    protected $max_limit = 50000;

    function __contruct() {
        print "Test";
        return "Solution1";
    }

    function calculatePrice($dob, $gender, $amount) {
        $rate = 1.03;
        $price = 55;

        if ($amount >= $this->max_limit) {
            return "Max Limit Reached";
        }
        //@TODO go lookup price from tables


        $this->price = $rate * $price;
        return $this->price;
    }
}
$solution1 = ProductFactory::createProduct('solution1');
print $solution1->calculatePrice("02/25/1982", "male", 100000);
\$\endgroup\$
7
  • 1
    \$\begingroup\$ I love the return; break; combos. I always do that too. I type case ...: and break; and then anything in between. But I always have a default: break; too :) \$\endgroup\$
    – CodeAngry
    Jul 9, 2013 at 20:16
  • \$\begingroup\$ default, yes will be adding that one :) In terms of the ProductFactory - I think I've changed my thinking on this. Where Solution1Product would be the actual product - to be more of a type of product. Eg: BookProduct - book would have price, isbn, author, etc, etc. \$\endgroup\$
    – Steve
    Jul 10, 2013 at 13:36
  • \$\begingroup\$ Isn't it nice :) $classname = ucfirst( strtolower( $type ) )."Product"; if ( class_exists( $classname ) ) { return new $classname; } \$\endgroup\$
    – bystwn22
    Jul 10, 2013 at 21:10
  • \$\begingroup\$ you have a Procutfactory that creates Solutions? hmm... Personally I think you are looking at the problem the wrong way. I'll try and get an answer by this evening \$\endgroup\$
    – Pinoniq
    Jul 12, 2013 at 10:12
  • 1
    \$\begingroup\$ "return ...; break;" simply doesn't make any sense. Adding a "default:" on the other hand would make sense. \$\endgroup\$
    – e-sushi
    Jul 12, 2013 at 18:26

1 Answer 1

2
\$\begingroup\$

Flexible approach

  1. First defining an ultimate abstraction of the factory
  2. Implement it with product type - class mapping
  3. Creating an entry point to register and use the factory

    interface IProductFactory {
    
        function createProduct($type);
    }
    
    class DefaultProductFactory implements IProductFactory {
    
        private $_mappings;
    
        public function __construct(array $mappings = array()) {
            $this->_mappings = $mappings;
        }
    
        public function addMappings(array $mappings) {
            $this->_mappings = array_merge($this->_mappings, $mappings);
        }
    
        public function createProduct($type) {
            if (!isset($this->_mappings[$type])) {
                throw new \Exception("Cannot create product of type " . $type);
            }
    
            $class = $this->_mappings[$type];
    
            //can be passed to a DI container to instantiate if needed
    
            return new $class();
        }
    }
    
    class ProductFactory {
    
        private static $_factory;
    
        public static function setCurrent(IProductFactory $factory) {
            $this->_factory = $factory;
        }
    
        public static function getCurrent() {
            return $this->_factory;
        }
    }
    
    ProductFactory::setCurrent(new DefaultProductFactory(array(
        "solution1" => "Solution1Product",
        "solution2" => "Solution2Product",
        "solution3" => "Solution3Product",
        "solution4" => "Solution4Product",
        "solution5" => "\\\\In\\\Some\\\\Other\\\\Namespace\\\\Solution5Product",
        )));
    
    $solution1 = ProductFactory::getCurrent()->createProduct('solution1');
    
    print $solution1->calculatePrice("02/25/1982", "male", 100000);
    
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.