everon/factory 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

everon/factory

Composer 安装命令:

composer require everon/factory

包简介

Everon Factory Component

README 文档

README

Library to handle dependency injection and instantiation. Allows to produce code that is easy to test.

Versions

  • Use v1.x and v2.x with PHP 7.2+
  • Use v3.x with PHP 8+

Features

  • One line, lazy loaded dependency injection (via setters or constructors)
  • Trait based, simple to use and implement, no hassle with config files, pure PHP
  • Factory/FactoryWorker gives full control and overview, on how each and every object is created
  • FactoryWorker allows for custom implementation of Factory methods
  • Full control when a dependency should be reused (via Dependency Container) or created from scratch (eg. Logger vs Value Object)
  • Minimized file/memory access/usage due to callbacks and lazy load
  • Intuitive Interface: clear, small and simple API
  • Convention over configuration
  • Clean code

How it works

Every instantiation should happen only inside of the FactoryWorker class. Its role is similar of AbstractFactory. It sits between Everon Factory Component and the client code that uses it, so there is no direct coupling. It's easy to implement and manage, because injection is cheap and dependency setup happens in one place.

This makes testing much easier, as everything can be easily mocked/stubbed/faked. It's ok to use new operator outside of Factory methods for simple value object like classes. For example new Collection().

For a particular module or application, the registerBeforeWork method of FactoryWorker is one of a places where you could setup your dependency tree. Or you could use Root Composition pattern and handle your whole dependency graph outside of your application.

Easy Dependency Injection

To use dependency injection, register it with Dependency Container and use one line trait to inject it.

For example, this will auto inject a predefined Logger instance via setter injection into Foo class.

class Foo
{
    use Dependency\Setter\Logger;
}

Register with Dependency Container

Use register method to register the dependency under Logger name.

$Container->register('Logger', function () use ($FactoryWorker) {
    return $FactoryWorker->buildLogger();
});

Define the traits and interface

Example of Logger dependency trait, which is reused between all of the classes that use Dependency\Setter\Logger trait. The only thing to remember is that, the name of the trait should be the same, as the name under which the dependency was registered with the Dependency Container.

trait Logger
{
    /**
     * @var LoggerInterface
     */
    protected $Logger;

    /**
     * @inheritdoc
     */
    public function getLogger()
    {
        return $this->Logger;
    }

    /**
     * @inheritdoc
     */
    public function setLogger(LoggerInterface $Logger)
    {
        $this->Logger = $Logger;
    }
}

Bonus: You can also define and assign the LoggerAwareInterface too all classes that are being injected with Logger instance.

interface LoggerAwareInterface
{
    /**
     * @return LoggerInterface
     */
    public function getLogger();

    /**
     * @param Logger LoggerInterface
     */
    public function setLogger(LoggerInterface $Logger);
}

Define the setter injection trait. The only requirement is that the name ends with Dependency\Setter\<dependency name>. You can reuse already defined Dependency\Logger trait, in every class that implements LoggerAwareInterface.

namespace Application\Modules\Logger\Dependency\Setter;

use Application\Modules\Logger\Dependency;

trait Logger
{
    use Dependency\Logger;
}

Register with Factory

Use registerWorkerCallback to register callback which will return instance of required FactoryWorker.

$Factory->registerWorkerCallback('ApplicationFactoryWorker', function() use ($Factory) {
    return $Factory->buildWorker(Application::class);
});

Build with FactoryWorker

To build your dependencies use the FactoryWorker classes.

class ApplicationFactoryWorker extends AbstractWorker implements FactoryWorkerInterface
{
    /**
     * @inheritdoc
     */
    protected function registerBeforeWork()
    {
        $this->getFactory()->registerWorkerCallback('ApplicationFactoryWorker', function () {
            return $this->getFactory()->buildWorker(self::class);
        });
    }

    /**
     * @return Logger
     */
    public function buildLogger()
    {
        $Logger = new Logger();
        $this->getFactory()->injectDependencies(Logger::class, $Logger);
        return $Logger;
    }

    /**
     * @param LoggerInterface $Logger
     * @param string $anotherArgument
     * @param array $data
     *
     * @return ApplicationInterface
     */
    public function buildApplication(LoggerInterface $Logger)
    {
        $Application = new Application($Logger);
        $this->getFactory()->injectDependencies(Application::class, $Application);
        return $Application;
    }

    /**
     * @param LoggerInterface $Logger
     *
     * @return UserManagerInterface
     */
    public function buildUserManager(LoggerInterface $Logger)
    {
        $UserManager = new UserManager($Logger);
        $this->getFactory()->injectDependencies(UserManager::class, $UserManager);
        return $UserManager;
    }
}

Resolve with Dependency Container

Use resolve to receive dependency defined earlier with register or propose. So you can pass the same instance to another class via constructor injection.

$Container->register('Logger', function () use ($FactoryWorker) {
    return $FactoryWorker->buildLogger();
});

$Container->register('UserManager', function () use ($FactoryWorker, $Container) {
    $Logger = $Container->resolve('Logger');
    return $FactoryWorker->buildUserManager($Logger);
});

$Container->register('Application', function () use ($FactoryWorker, $Container) {
    $Logger = $Container->resolve('Logger');
    return $FactoryWorker->buildApplication($UserManager, $Logger);
});

Now Application and UserManager will share the same instance of Logger class.

$Application->getLogger()->log('It works');
$UserManager->getLogger()->log('It works, too');

If you don't do any work in constructors, and you shouldn't, and only require the Logger functionality later, it would be easier to just use the Logger as the infrastructure type dependency and just inject it via setter injection with one line. The end result is the same.

Every required class will be injected with the same Logger instance, that was registered with the Dependency Container and assembled by FactoryWorker in Factory.

Ensures Tests Ready Code (TM)

Writing tests of classes that use Everon Factory for the dependency injection and instantiation removes the hassle of dealing with dependency problems since everything is so easy to mock.

Dependency Container, Factory and FactoryWorker

Instantiate new Dependency Container and assign it to Factory. Use Factory to get instance of your specific FactoryWorker.

The best thing is, that the classes which are being instantiated with the FactoryWorker are not aware about the Dependency Container at all.

It could be in separate files, obviously, split by the application type and the dependencies it needs.

An example, of using the same instance of Logger, in every class, through out whole application, which required Logger dependency.

$Container = new Dependency\Container();
$Factory = new Factory($Container);
$Factory->registerWorkerCallback('ApplicationFactoryWorker', function() use ($Factory) {
    return $Factory->buildWorker(Application::class);
});

$FactoryWorker = $Factory->getWorkerByName('ApplicationFactoryWorker');

$Container->register('Application', function () use ($FactoryWorker, $Container) {
    $UserManager = $Container->resolve('UserManager');
    $Logger = $Container->resolve('Logger');
    return $FactoryWorker->buildApplication($UserManager, $Logger);
});

$Container->register('UserManager', function () use ($FactoryWorker) {
    $Logger = $FactoryWorker->getFactory()->getDependencyContainer()->resolve('Logger');
    return $FactoryWorker->buildUserManager($UserRepository, $Logger);
});

$Container->register('Logger', function () use ($FactoryWorker) {
    return $FactoryWorker->buildLogger();
});

//..
//.. Instantiate your application, and proceed as usual
//..
$Application = $Container->resolve('Application');
$Application
    ->bootstrap()
    ->run();

What's the best way to inject dependencies?

Use constructor for dependencies that are part of what the class is doing, and use setters/getters for infrastructure type dependencies. In general, a Logger or FactoryWorker could be good examples of infrastructure type dependencies.

Test Driven

See tests for more examples with trait dependencies.

Example

Check Everon Criteria Builder to see how to use Everon Factory by example.

everon/factory 适用场景与选型建议

everon/factory 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 3.35M 次下载、GitHub Stars 达 0, 最近一次更新时间为 2015 年 11 月 21 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 everon/factory 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 everon/factory 我们能提供哪些服务?
定制开发 / 二次开发

基于 everon/factory 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 3.35M
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 15
  • 依赖项目数: 3
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-11-21