我们有一个CMS,在标准的Admin_Model_Whatever, Admin_Model_Mapper_Whatever (Zend Framework 1目录结构)下,有模型,映射器,表单和DbTables的标准实现。
然而,某些项目共享这个CMS目录,有时需要添加额外的字段(例如,一个页面视图的自定义"颜色"字段)。因此,在原始CMS目录中,我们有Admin_Model_Page,现在我想在项目目录中使用一个名为Project_Model_Page的类(以及新的页面映射器和dbtable类)扩展这个类。
如何从共享后端实现返回正确的类?也就是说,如果Project_Class被检测到并且存在于某个项目的目录中,返回它,如果没有返回Admin_Class?
我想避免做如果class_exists(类)检查,每次我需要一个不同的形式,模型,映射器等每一个创建,读取,更新,删除操作的每一个页面,张贴,等等。
是否有一个定义的模式来处理这个?我一直在看工厂,服务定位器,提供者等等,但我不完全确定这些是否适合这种情况-这只是让自动加载器检测某个类是否存在于一个目录中,如果不从默认路径返回一个类。
Factory-Pattern
你已经在正确的轨道上。您可能想看一下Factory-Pattern
根据工厂模式的定义,它是一种不是通过类的构造函数实例化对象,而是通过另一个类或方法实例化对象的方法。
这就是你想要的滚动方式。通过最简单的实现,您需要在这里创建的工厂需要接受两个参数。要使用的主类和在主类不存在的情况下的回退类。
稍后我将展示一些如何改进工厂的选项,以便它能够进一步实现自动化。
设置所有
为简单起见,这里我不会使用自动加载器,但使用自动加载器也可以。
<<p> 文件系统结构/strong>-Dependencies
-- DependencyRouter.php
-Fallbacks
-- FallbackRouter.php
-Interfaces
-- RouterInterface.php
-FallbackFactory.php
-index.php
Dependencies
目录包含您首先要实例化的主要类。Fallbacks
目录包含相应的回退类,以防主类无法实例化。
由于主类和回退类的对象都应该能够以我们为它们定义契约的相同方式使用。我们通过创建Interfaces
来实现这一点。这就是最后一个文件夹的用途
为了不占用太多空间,可以在这里找到实际实现的要点(这不是问题的一部分)。
现在让我们看一下实际的Factory。
<?php
class ClassNotFoundException extends 'Exception {}
class FallbackFactory {
public function createInstance( $main, $fallback, $instanceArgs = [] )
{
if( class_exists( $main) )
$reflectionClass = new 'ReflectionClass( $main );
else if ( class_exists( $fallback ) )
$reflectionClass = new 'ReflectionClass( $fallback );
else
throw new ClassNotFoundException('The Class ' . $main . ' does not exist and neither does the fallback class ' . $fallback);
return $reflectionClass->newInstanceArgs($instanceArgs);
}
}
真的没什么特别的。首先,我们看看实际的主类是否存在。如果是,我们将存储一个ReflectionClass
的实例。如果不存在,则检查回退类是否存在。如果是这样的话,我们就和以前一样做。你可以直接实例化一个对象,但让我们在这里使用一个ReflectionClass来保持一些整洁的魔术,我们可以稍后添加。
当主类和异常类都不存在时,我们应该抛出一个异常,并将处理错误的责任传递给开发人员。
这才是真正的魔力。
index . php
<?php
require_once 'FallbackFactory.php';
require_once 'Interfaces/RouterInterface.php';
require_once 'Dependencies/DependencyRouter.php';
require_once 'Fallbacks/FallbackRouter.php';
$factory = new FallbackFactory();
$router = $factory->createInstance(
''Dependencies'DependencyRouter',
''Fallbacks'FallbackRouter'
);
$router->route();
这就是如何使用工厂。如果可以找到主DependencyRouter
,输出将是:
I am the main Router
如果没有找到DependencyRouter
,但FallbackRouter
,输出将是:
I am the Fallback router
否则会抛出异常。
扩展工厂
如果你想让工厂更动态地运行,你可以做一些事情。
1。使用命名空间并将类命名为一致的
如果你想避免每次都指定回退,你可以将主类和回退类命名为相同,但为它们指定不同的命名空间。所以你只需要传递主类。回退类将自动确定。如
$router = $factory->createInstance(
''Dependencies'Router',
);
如果'Dependencies'Router'
不存在,工厂将自动查找'Fallbacks'Router
类。
2。自动解析依赖
现在我们将构造函数实参作为形参传递给工厂方法。但是通过使用类型提示和反射,你可以自动解析依赖关系。
3。指定回退层次
不是传递一个回退类,而是传递一个由多个类组成的数组,如果找到这些类,就查找并解析它们。