使用php 5.2,我正在尝试使用工厂将服务返回给控制器。我的请求uri的格式为www.mydomain.com/service/method/param1/param2/etc。然后我的控制器将使用uri中发送的令牌调用服务工厂。从我所看到的情况来看,我可以在工厂找到两条主要路线。
单一方法:
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);
}
}
}
或多种方法:
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);
}
}
所以,考虑到这一点,我将有一些通用服务,我将传递该令牌(例如,StaticPageTemplateService和DynamicPageTemplateService),它可能会实现另一个工厂方法,就像抓取模板,域对象等。还有一些将是特定服务(例如,UserService),它将是该令牌的1:1而不会被重用。因此,对于少量服务而言,这似乎是一种不错的方法(如果不是,请提供建议)。但是,随着时间的推移,随着时间的推移和我的网站的增长,我最终会有100种可能性。这似乎不再是一个好方法。我只是开始使用或者是否有其他更适合的设计模式?感谢。
更新:@JSprang - 令牌实际上是在uri中发送的,例如mydomain.com/location会想要一个特定于loction的服务,而mydomain.com/news会想要一个特定于新闻的服务。现在,对于其中很多,这项服务将是通用的。例如,许多页面将调用StaticTemplatePageService,其中令牌被传递到服务。该服务反过来将获取“位置”模板或“链接”模板,然后将其吐出来。有些人需要传递令牌的DynamicTemplatePageService,例如“news”,该服务将获取一个NewsDomainObject,确定如何呈现它并将其吐出。其他人,比如“user”将特定于UserService,其中它将具有Login,Logout等方法。因此,基本上,令牌将用于确定需要哪个服务,如果它是通用服务,则该令牌将是传递给那项服务。也许令牌不是正确的术语,但我希望你能达到目的。
我想使用工厂,因此我可以轻松更换我需要的服务,以防我的需求发生变化。我只是担心,在网站变得更大(页面和功能)后,工厂将变得相当臃肿。但我开始觉得我无法摆脱将数组存储在数组中(如Stephen的解决方案)。这对我来说并没有感觉到OOP,我希望能找到更优雅的东西。
答案 0 :(得分:1)
我认为当您的网站变得庞大时,无法避免此令牌服务映射维护工作。无论你如何实现这个列表,切换块,数组等,这个文件总有一天会变得很大。所以我的意见是避免这个列表并使每个令牌成为服务类,对于那些通用服务,你可以继承它们,就像这样
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);
}
}
通过这种方式,您可以避免每次添加或更改令牌时编辑工厂类文件,只需更改特定的令牌文件即可。
答案 1 :(得分:1)
我有一个更好的工厂模式解决方案,它允许您添加新服务,而不需要为该特定服务创建新类。概述如下:
对于工厂:
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();
}
}
抽象服务:
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__;
}
}
然后是具体的服务:
include('AbstractService.php');
class ConcreteService extends AbstractService{
// All your service specific code.
}
此解决方案使您的依赖项成为一种方式,您只需扩展AbstractService即可添加新服务,无需修改任何现有代码。你使用get_service('news')或你想要的任何一个进入工厂,工厂在$ services数组中查找关联的对象,并在该特定对象上调用get_new()函数,该函数为您提供特定的新实例与之合作的服务。
答案 2 :(得分:0)
以下是我如何做一个单身工厂(为简洁起见删除了评论):
class ServiceFactory {
private static $instance;
private function __construct() {
// private constructor
}
public function __clone() {
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public static function init() {
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
public function get_service($name, $parameter) {
$name .= 'TemplateService';
return $this->make_service($name, $parameter);
}
private function make_service($name, $parameter) {
if (class_exists($name)) {
return new $name($parameter);
} else {
throw new LogicException('Could not create requested service');
return false;
}
}
}
以这种最简单的形式,只需传递服务的字符串名称:
function whatever() {
$ServiceFactory = ServiceFactory::init();
$new_service = $ServiceFactory->get_service('StaticPage', 'location');
return $new_service;
}
答案 3 :(得分:0)
我不是PHP开发人员,所以我不会尝试显示任何代码,但这就是我要做的。我将实现Strategy Pattern并创建一个IServiceProvider接口。该接口可以具有GetService()方法。然后,您将创建四个新对象:LocationService,ProductService,UserService和DefaultService,所有这些对象都将实现IServiceProvider接口。
现在,在您的工厂中,构造函数将接受IServiceProvider并具有一个公共GetService()方法。调用该方法时,它将使用注入的IServiceProvider的策略。这样可以提高可扩展性,因为每次有新服务时都不必打开Factory,只需要创建一个实现IServiceProvider的新类。
我决定快速在C#中快速模拟它,所以你会有一个例子。我知道这不是你正在使用的语言,但它可能有助于澄清我在说什么。代码如下所示。
public interface IServiceProvider
{
Service GetService();
}
public class UserServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class StaticPageTemplateServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class DynamicPageTemplateServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class DefaultServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class ServiceFactory
{
public ServiceFactory(IServiceProvider serviceProvider)
{
provider = serviceProvider;
}
private IServiceProvider provider;
public Service GetService()
{
return provider.GetService();
}
}
答案 4 :(得分:0)
服务工厂实现(带有我们将在具体类中使用的接口):
class ServiceFactory
{
private static $BASE_PATH = './dirname/';
private $m_aServices;
function __construct()
{
$this->m_aServices = array();
$h = opendir(ServiceFactory::$BASE_PATH);
while(false !== ($file = readdir($h)))
{
if($file != '.' && $file != '..')
{
require_once(ServiceFactory::$BASE_PATH.$file);
$class_name = substr($file, 0, strrpos($file, '.'));
$tokens = call_user_func(array($class_name, 'get_tokens'));
foreach($tokens as &$token)
{
$this->m_aServices[$token] = $class_name;
}
}
}
}
public function getInstance($token)
{
if(isset($this->m_aServices[$token]))
{
return new $this->m_aServices[$token]();
}
return null;
}
}
interface IService
{
public static function get_tokens();
}
$ BASE_PATH.'UserService.php':
class UserService implements IService
{
function __construct()
{
echo '__construct()';
}
public static function get_tokens()
{
return array('user', 'some_other');
}
}
因此,我们所做的基本上是为任何具体的类实现自行注册所有令牌。只要您的类驻留在$ BASE_PATH中,它们将在实例化时自动由ServiceFactory加载(当然,如果您愿意,可以通过静态方法更改ServiceFactory来提供此功能)。
无需使用大型switch语句提供对具体实现的访问,因为它们在由具体类级别实现的get_tokens()函数构建的内部映射中都有帮助。 token->类关系存储在服务工厂内的1:1映射中,因此如果您因任何原因链接令牌,则需要重构它。
答案 5 :(得分:0)
一些将是特定服务(例如,UserService),它将是该令牌的1:1而不会被重用。所以这 对于少量服务似乎是一种好的方法(如果不是,请提出建议)。但是什么时候结束了 时间和我的网站增长,我最终有100个可能性。这似乎不再是一个好方法。我离开了 开始或是否有另一种更适合的设计模式?感谢。
很抱歉,但我想你现在正试图解决你为自己创造的问题。
令牌实际上是在uri中发送的,例如mydomain.com/location会想要一个特定的服务 loction和mydomain.com/news需要特定于新闻的服务。现在,对于很多这些, 服务将是通用的。例如,很多页面都会调用StaticTemplatePageService 令牌传递给服务。该服务反过来将获取“位置”模板或 “链接”模板,然后将其吐出来。
有些人已经建议使用依赖注入容器解决整个工厂问题,但我想知道为什么首先需要工厂?您似乎正在编写一个Controller(我猜),它可以为多种不同类型的请求生成响应,并且您尝试在一个类中解决所有。我会确保不同的请求(/ location,/ news)映射到专用的,小的,可读的控制器(LocationController,NewsController)。由于一个控制器只需要一个服务,因此编写,维护和扩展应该更容易。
通过这种方式,您可以在专用,简洁,可读的类中解决依赖关系,而不是一个巨大的God类。这意味着你也不会有数百行的切换问题,你应该将“location”映射到LocationController,将“news”映射到NewsController等等。现在很多PHP框架都使用了FrontController,而我想象一下这也是你的方式。
PS:为了确保NewsService实际进入NewsController,我建议使用依赖注入容器。它让你的生活更轻松;)