mario-legenda/code-workflow

对象执行的一致设计模式

0.0.1 2015-03-30 10:56 UTC

This package is auto-updated.

Last update: 2024-09-09 02:44:18 UTC


README

## PHP 代码工作流程

在过去几个月里,我一直在进行一个SPA项目,该项目在服务器端使用Symfony2,客户端使用Angular。大部分代码都是在浏览器中调用控制器。然后控制器从数据库中获取数据并将其返回给客户端。我试图使用一些设计模式使其保持一致,但是来自客户端的不同数据需要使用不同的设计模式。一些是一致的、易于阅读的,但有些是杂乱无章、难以阅读的。因此,我决定创建一个工作流程设计模式,它将对每次代码执行都是一样的。

我从ASP.NET世界得到了这个想法,主要是Microsoft Workflow Foundation。这个项目绝对不是Workflow Foundation的类似物,但它激发了我的思考。为什么不做一个PHP的类似物呢?你们大多数人都会认为这是完全不必要的+过度设计。我实际上同意。使用这种设计模式,需要编写更多的代码,但也有一些好处。

例如,Code Workflow会检查您要执行的方法的返回类型,因此当某个方法没有返回所需类型时,会抛出错误。但这也可能是一种过度设计,因为PHP不是设计为强类型编程语言。所有这些都抛开不谈,这可能是最无意义的项目之一,但在我开始工作后,我变得固执己见,必须让它工作。可能会有人喜欢它,但大多数人会讨厌它。这就是武士的方式:)

以下示例将使一切变得清晰,所以请继续阅读。

示例对象位于src/Demo目录中。

####基本方法

在src/Demo目录中的示例中,您可以找到Company和Person对象。使用这些对象的基本方法如下...

$company = new Company();
$company->setCompanyName(new String('Shady Kaymans Company'));

$john = new Person(new Unique());
$john->setName(new String('John'))
$john->setLastname(new String('Doe'))
$john->setAge(new Integer(28));

$company->hireEmployee($john);

$john->foundJob($company);

这完全可以。它易于阅读和维护。但是,当您需要根据客户端发送的某些JSON数据从数据库中获取公司数据时会发生什么?当您需要创建一个基于数据库数据的Person对象的Factory时会发生什么?即使在使用设计模式方面很干净,这段代码也可能变得难以阅读。

使用Code workflow,上面的代码可以重写为以下代码...

$company = new Company();
$john = new Person(new Unique());

$compiler
   ->runObject($company)
   ->withMethods(
       $compiler->method()->name('setCompanyName')->withParameters(new String('Shady Kaymans Company'))->void()->save()
   )
   ->runObject($john)
   ->withMethods(
       $compiler->method()->name('setName')->withParameters(new String('John'))->void()->save(),
       $compiler->method()->name('setLastname')->withParameters(new String('Doe'))->void()->save(),
       $compiler->method()->name('setAge')->withParameters(new Integer(28))->void()->save()
   )
   ->then()
   ->runObject($company)
   ->withMethods(
       $compiler->method()->name('hireEmployee')->withParameters($john)->void()->save()
   )
   ->then()
   ->runObject($john)
   ->withMethods(
       $compiler->method()->name('foundJob')->withParameters(new Job($company))->void()->save()
   )
   ->compile();

这是相同的代码执行,但使用了易于阅读的代码。这可以读作以下内容

Run the object $company with method 'setCompanyName' that doesn't return anything. 
Then, run object #john with methods 'setName', 'setLastname' and 'setAge' with the 
desired parameters and void method return. Then, run the object $company 
again with method 'hireEmployee' with $john object as a parameter.  
Then, run object $john again with method 'foundJob' that accepts  
Job object and returns void. Compile the entire code. 

注意

If you have multiple 'set' methods in an object, 
you can execute the above code like this...

->runObject($john)
    ->withMethods(array(
       'setName' => new String('John'),
       'setLastname' => new String('Doe'),
       'setAge' => new Integer(28)
    ))

Compiler::withMethods() here accepts an array 
with the method name as key and method parameters as value

当您希望查看方法的返回值时会发生什么?这也是可能的...

$compiler
    ->runObject($company)
    ->withMethods(
        $compiler->method()->name('setCompanyName')->withParameters(new String('Shady Kaymans Company'))->void()->save(),
        $compiler->method()->name('getCompanyName')->string()->save()
    )
    ->ifMethod('getCompanyName')->fails()->thenRun(function() {
       return 'getCompanyName failed to return a string';
    })
    ->ifMethod('getCompanyName')->succedes()->thenRun(function(){
       return 'success';
    })
    ->compile()
    
    $response = $compiler->runResponseFor($company, 'getCompanyName');
    
    // if successfull, prints 'success', if not then prints 'getCompanyName failed to return a string'
    var_dump($response);

如果您希望在成功匿名函数中获取方法的返回值,则使用此...

->ifMethod('getCompanyName')->succedes()->thenRun(function(){
      return $context->getObjectStorage()->retreiveUnit($company)->retreive('getCompanyName')->getValue();
 })

您可以链尽可能多的对象,工作流程编译器都将正常工作。

就是这样。我不会详细编写文档,因为我不确定这会对任何人有用。如果只有一名开发者喜欢它,我将使用phpdocumentator编写详细的文档,并在github上提供完整的文档。我还会对所有代码进行彻底的注释,以便您可以了解它是如何工作的。

祝大家好运,生活愉快。