I've been trying to grasp OOP concepts and while I do get the general ideas behind most of them, I often find myself in need of some advice regarding their practical implementation. One of such cases is the factory method.
I'm writing a PHP app that's going to handle requests incoming from both web and command-line interface, so I came up with the following simple class inheritance structure to cover both types of requests:
abstract class Request {
}
class HttpRequest extends Request {
}
class CliRequest extends Request {
}
Now I need a factory method that would return a concrete Request instance, depending on the value returned by php_sapi_name()
:
public function create() {
if(php_sapi_name() === 'cli')
return new CliRequest();
else
return new HttpRequest();
}
My question is: where do I put it? I can think of at least three possibilities:
1) Static method in a separate class:
class RequestFactory {
public static function create() {
// ...
}
}
2) Regular method in a separate class (would require instantiating the class first):
class RequestFactory {
public function create() {
// ...
}
}
3) Static method in the abstract parent class:
abstract class Request {
public static function create() {
// ...
}
}
What are the pros and cons of each solution and which would be considered "proper" and why?
The Factory Method pattern is generally used in the following situations: A class cannot anticipate the type of objects it needs to create beforehand. A class requires its subclasses to specify the objects it creates. You want to localize the logic to instantiate a complex object.
The factory design pattern is used when we have a superclass with multiple sub-classes and based on input, we need to return one of the sub-class. This pattern takes out the responsibility of the instantiation of a class from the client program to the factory class.
In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.
Factory method lets a class defer instantiation to subclass. Factory needs an object (whose concrete class is not known or whose concrete class may change as per the different application type ) to perform a task.
All these possibilities will work as expected. I don't really get any "cons" as it fulfils, what is IMHO, your encapsulation objective. Now, let's look at Factory Method pattern essence:
Define an interface for creating an object, but let the classes that implement the interface decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses.
I'm not sure if what you're willing to do perfectly matches this definition.
Instead, it looks like you want to implement something called "Simple Factory" in which instantiation process is encapsulated into a class.
But having this kind of method directly into the abstract class that defines the interface of your "Request" objects doesn't look like a bad idea.
As Nicolas said, it's a rather common pattern in Java, C#, and Cocoa lands.
For these reasons, my choice would go to 3rd option
So, I have an idea, to do what I think you want, using method overloading.
class Request {
private $request;
private $valid = true;
private $type;
private $vars;
private $methods;
public function __construct() {
$this->type = php_sapi_name() === 'cli' ? 'cli' : 'http';
if($this->is_cli()) $this->request = new CliRequest();
else if($this->is_http()) $this->request = new HttpRequest();
else {
$this->valid = false;
return;
}
$this->vars = get_class_vars($this->request);
$this->methods = get_class_methods($this->request);
}
public function __get( $var ){
if(!$this->valid) return false;
if(!in_array($var, $this->vars)) return false;
return $this->request->$var;
}
public function __set( $var , $val ){
if(!$this->valid) return false;
if(!in_array($var, $this->vars)) return false;
return $this->request->$var = $val;
}
public function __call( $meth, $args ){
if(!$this->valid) return false;
if(!in_array($meth, $this->methods)) return false;
return call_user_func_array($this->request->$var, $args);
}
public function is_cli( ){
return $this->type == 'cli';
}
public function is_http( ){
return $this->type == 'http';
}
}
// Then, when calling the function...
$request = new Request;
$request->variable; // will get the variable from the Cli or Http request class
$request->method("a","b","c"); // Will run the method from the Cli or Http request class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With