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 '13 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\$ Jul 10 '13 at 13:36
  • \$\begingroup\$ Isn't it nice :) $classname = ucfirst( strtolower( $type ) )."Product"; if ( class_exists( $classname ) ) { return new $classname; } \$\endgroup\$
    – bystwn22
    Jul 10 '13 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 '13 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 '13 at 18:26
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, privacy policy and cookie policy

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