Search code examples
phpoopfactory-patternsingle-responsibility-principle

How to create an object using Factory method, instead of supplying alternative object constructor


I am having some trouble applying Factory Pattern.

I have a class that I usually call as Product($modelNumber, $wheelCount). But in a part of legacy code that I am refactoring, I do not have $modelNumber, and only have $productID, where the link between {$modelNumber, $productID} is in the database (or in my case I can hardcode it, as I only have a select few products at the moment).

I need to be able to create my class using $productId, but how?

Using Procedural ways I would have a function that does the lookup, and I would put that function in a file, and include that file anywhere where I need to do the lookup. Thus do this:

$modelNumber = modelLookup($productId)
Product($modelNumber, $wheelCount);

But how do I do it using Object Oriented way?

Note: I have posted a more detailed situation here: https://softwareengineering.stackexchange.com/q/233518/119333 and this is where Factory pattern (and other patterns, like interfaces and function pointer passing) were suggested conceptually, but I hit a wall when trying to implement them in PHP. It kind of seems like a simple question, but I think there are several ways to do it and I am a bit lost as to how. And so I need some help.


Solution

  • I provided a conceptual answer to your SRP problem on Programmers Exchange but I think I can demonstrate it here.

    What you basically want is some other object that will do the work to get you the model number of given product ID.

    class ProductModelNumberProvider {
        public function findByProductId($productId) {
            // The lookup logic...
        }
    }
    

    Your factory should provide a setter constructor so it can make use of this object internally to lookup the model number if needed. So basically you will end up with a ProductFactory similar to this.

    class ProductFactory {
        private $productModelNumberProvider;
        
        public function __construct(ProductModelNumberProvider $productModelNumberProvider) {
            $this->productModelNumberProvider = $productModelNumberProvider;
        }
    
        public function getProductByIdAndWheels($productId, $wheels) {
            $modelNumber = $this->productModelNumberProvider($productId);
            return $this->getProductByModelNumberAndWheels($modelNumber, $wheels);
        }
    
        public function getProductByModelNumberAndWheels($modelNumber, $wheels) {
            // Do your magic here...
            return $product;
        }
    }
    

    EDIT

    On second thought the setter is not the best approach since having a ProductModelNumberProvider instance is mandatory. That is why I moved it to have it injected through the constructor instead.