本文将分享如何在laravel工程里面实践工厂模式。
什么是工厂模式
工厂模式是对一组类的实例化管理方法的实践总结。所谓一组类是这些类属于一个领域体内,具有一些共性,也有不同的个性, 往往在实际的业务场景之下,会需要用到不同类的实例去做业务逻辑处理。为了避免重复性的类实例过程,就需要一个工厂类 去管理和协调类的实例化。就像一个实际的工厂那样,每个生产线,会根据不同的设计磨具去生产对应的产品那样。
工程模式的优点
- 能减少重复代码量。
- 能解耦类的实例化和类使用过程。
- 提高了系统的灵活性,需要添加新类时可灵活扩展。
Laravel框架里面的工厂模式
其实在Laravel的框架源码里面有很多地方用到了工厂模式。比如:Illuminate\Auth模块
AuthManager作为工厂角色承担 对AuthFacade类的管理,可以看到SessionGuard, TokenGuard在AuthManager中有对应的实例化处理:
/**
* Create a session based authentication guard.
*
* @param string $name
* @param array $config
* @return \Illuminate\Auth\SessionGuard
*/
public function createSessionDriver($name, $config)
{
$provider = $this->createUserProvider($config['provider'] ?? null);
$guard = new SessionGuard($name, $provider, $this->app['session.store']);
// When using the remember me functionality of the authentication services we
// will need to be set the encryption instance of the guard, which allows
// secure, encrypted cookie values to get generated for those cookies.
if (method_exists($guard, 'setCookieJar')) {
$guard->setCookieJar($this->app['cookie']);
}
if (method_exists($guard, 'setDispatcher')) {
$guard->setDispatcher($this->app['events']);
}
if (method_exists($guard, 'setRequest')) {
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
}
return $guard;
}
/**
* Create a token based authentication guard.
*
* @param string $name
* @param array $config
* @return \Illuminate\Auth\TokenGuard
*/
public function createTokenDriver($name, $config)
{
// The token guard implements a basic API token based guard implementation
// that takes an API token field from the request and matches it to the
// user in the database or another persistence layer where users are.
$guard = new TokenGuard(
$this->createUserProvider($config['provider'] ?? null),
$this->app['request']
);
$this->app->refresh('request', $guard, 'setRequest');
return $guard;
}
AuthManager通过guard方法来实例化不同的AuthGuard类:
/**
* Attempt to get the guard from the local cache.
*
* @param string $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*/
public function guard($name = null)
{
$name = $name ?: $this->getDefaultDriver();
return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
}
...
// 再通过resolve方法做最终的实例化
/**
* Resolve the given guard.
*
* @param string $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*
* @throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
}
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($name, $config);
}
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($name, $config);
}
throw new InvalidArgumentException("Auth driver [{$config['driver']}] for guard [{$name}] is not defined.");
}
同时利用魔术方法__call实现对实现类方法的代理调用。
/**
* Dynamically call the default driver instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->guard()->{$method}(...$parameters);
}
最后通过Laravel工厂管理类AuthManager以单例的形式注入到Laravel的容器内:
/**
* Register the authenticator services.
*
* @return void
*/
protected function registerAuthenticator()
{
$this->app->singleton('auth', function ($app) {
// Once the authentication service has actually been requested by the developer
// we will set a variable in the application indicating such. This helps us
// know that we need to set any queued cookies in the after event later.
$app['auth.loaded'] = true;
return new AuthManager($app);
});
...
}
这样处理的目的是可以方便的建立AuthFacade,从而可以直接 实现下面极其简洁而直观的写法:
Auth::guard("session")->check()
在Illuminate\Cache通过CacheManager实现多个缓存驱动, 在Illuminate\Filesystem通过FilesystemManager实现多个文件驱动, 均是通过上面的思路实现了工厂模式。
如何优雅的实践
Laravel框架的源码里面包含了很多可以学习和借鉴的设计模式实现范例,甚至上面所写的工厂模式,Laravel框架源码里面已经提供了一个通用的工厂实现抽象类Manager来方便开发者利用它实现工厂模式。仔细看它的内部结构, 其实和上面的几个Manager类的思路是一致的。我们在业务开发中,如果需要实现工厂模式时,完全可以继承Manager抽象类,来管理自己的类实例化。
例如下面的写法:
use Illuminate\Support\Manager;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
// 工厂管理类
class DemoManager extends Manager
{
public function getDefaultDriver()
{
return 'default';
}
public function createDefaultDriver()
{
return new DefaultDemo;
}
public function createDemo1Driver()
{
return new OneDemo;
}
}
// DefaultDemo
class DefaultDemo
{
public function foo()
{
return 'bar';
}
}
// DefaultDemo
class OneDemo
{
public function foo()
{
return 'bar1';
}
}
// 工厂门面
class DemoFacade extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'demo';
}
}
// 在ServiceProvider实例工厂管理类单例
class DemoServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->singleton('demo', function ($app) {
return new DemoManager($app);
});
// 类名转名
if (! class_exists('Demo')) {
class_alias(DemoFacade::class, 'Demo')
}
}
}
这样我们便可方便的使用如下的代码:
// 使用示例
Demo::driver()->foo(); // 输出 'bar'
Demo::driver("demo1")->foo(); // 输出 'bar1'