Using php 5.2, I'm trying to use a factory to return a service to the controller. My request uri would be of the format www.mydomain.com/service/method/param1/param2/etc. My controller would then call a service factory using the token sent in the uri. From what I've seen, there are two main routes I could go with my factory.
Single method:
class ServiceFactory {
public static function getInstance($token) {
switch($token) {
case 'location':
return new StaticPageTemplateService('location');
break;
case 'product':
return new DynamicPageTemplateService('product');
break;
case 'user'
return new UserService();
break;
default:
return new StaticPageTemplateService($token);
}
}
}
or multiple methods:
class ServiceFactory {
public static function getLocationService() {
return new StaticPageTemplateService('location');
}
public static function getProductService() {
return new DynamicPageTemplateService('product');
}
public static function getUserService() {
return new UserService();
}
public static function getDefaultService($token) {
return new StaticPageTemplateService($token);
}
}
So, given this, I will have a handful of generic services in which I will pass that token (for example, StaticPageTemplateService and DynamicPageTemplateService) that will probably implement another factory method just like this to grab templates, domain objects, etc. And some that will be specific services (for example, UserService) which will be 1:1 to that token and not reused. So, this seems to be an ok approach (please give suggestions if it is not) for a small amount of services. But what about when, over time and my site grows, I end up with 100s of possibilities. This no longer seems like a good approach. Am I just way off to begin with or is there another design pattern that would be a better fit? Thanks.
UPDATE: @JSprang - the token is actually sent in the uri like mydomain.com/location would want a service specific to loction and mydomain.com/news would want a service specific to news. Now, for a lot of these, the service will be generic. For instance, a lot of pages will call a StaticTemplatePageService in which the token is passed in to the service. That service in turn will grab the "location" template or "links" template and just spit it back out. Some will need DynamicTemplatePageService in which the token gets passed in, like "news" and that service will grab a NewsDomainObject, determine how to present it and spit that back out. Others, like "user" will be specific to a UserService in which it will have methods like Login, Logout, etc. So basically, the token will be used to determine which service is needed AND if it is generic service, that token will be passed to that service. Maybe token isn't the correct terminology but I hope you get the purpose.
I wanted to use the factory so I can easily swap out which Service I need in case my needs change. I just worry that after the site grows larger (both pages and functionality) that the factory will become rather bloated. But I'm starting to feel like I just can't get away from storing the mappings in an array (like Stephen's solution). That just doesn't feel OOP to me and I was hoping to find something more elegant.
I think there is no way to avoid this token-service-mapping maintaining work when your site growing large. No matter how you implement this list, switch block, array, etc. this file will become huge someday. So my opinion is to avoid this list and make each token a service class, for those generic services, you can inherit them, like this
class LocationService extends StaticPageTemplateService {
public function __construct(){
parent::__construct('location');
}
}
class ServiceFactory {
public static function getInstance($token) {
$className = $token.'Service';
if(class_exist($className)) return new $className();
else return new StaticPageTemplateService($token);
}
}
in this way, you can avoid to edit the factory class file each time a token added or changed, you just need to change the specific token file.
I have a better factory pattern solution that will allow you to add new services without doing anything more than creating a new class for that specific service. Outlined below:
For the Factory:
class ServiceFactory{
private static $instance = null;
private static $services = array();
private function __construct(){
// Do setup
// Maybe you want to add your default service to the $services array here
}
public function get_instance(){
if($this->instance){
return $this->instance;
}
return $this->__construct();
}
public function register_service($serviceName, $service){
$this->services[$serviceName] = $service;
}
public function get_service($serviceName){
return $this->services[$serviceName]->get_new();
}
}
An Abstract Service:
include('ServiceFactory.php');
class AbstractService{
public function __construct($serviceName){
$factory = ServiceFactory::get_instance();
$factory->register_service($serviceName, $this);
}
public function get_new(){
return new __CLASS__;
}
}
And then a concrete service:
include('AbstractService.php');
class ConcreteService extends AbstractService{
// All your service specific code.
}
This solution makes your dependencies one way and you can add new services by simply extending the AbstractService, no need to modify any existing code. You call into the factory with the get_service('news') or whichever you want, the factory looks up the associated object in its $services array and calls the get_new() function on that particular object which gets you a new instance of the specific service to work with.
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