设计类要求多个类执行相同的任务

时间:2015-12-18 19:50:26

标签: php oop laravel design-patterns observer-pattern

我有一个Laravel应用程序,我正在创建一个导入数据的类。数据存储在大约15个不同的模型中,因此我有一个Class来处理每个模型,因为每个模型都有自己的实现。此外,未来可能会添加更多型号。

我想在$importer->import()类中使用一行代码Importer,然后获取所有15个实现类,并调用其import()方法。

然而,通用$importer->import()方法将如下所示:

public function __construct(ImporterOne $one, ImporterTwo $two, ImporterThree $three, etc..)
{
   $this->importerOne = $importerOne;
   $this->importerTwo = $importerTwo;
   // etc ...
   $this->importerFifteen = $importerFifteen;
}

public function import()
{
    $this->importerOne->import();
    $this->importerTwo->importer();
    // etc...
}

这似乎并不是很好,因为这个类将有15个以上的依赖,所有基本上做同样的事情。然后为了更新这个,我需要进入这里并添加/删除依赖项和import()方法调用。

所以我的解决方案是委托注册'每个实现的导入程序,而不是让Generic Importer类负责。实际上,一个观察者的种类。但是,不是客户端将Observer附加到Subject,而是每个实现都将自己附加到Subject。

use Importer as GenericImporter

public class importerOne {

   public function __construct(SomeModel $model, GenericImporter $importer)
   {
      $this->importer = $importer;
      $this->importer->attach($this);
   }
}

// Do the same for each implementation

// and then the generic importer

public class Importer {

   protected $importables = [];

   public function attach($import_implementation)
   {
      array_push($importables, $import_implementation); 
   }

   public function import()
   {
      foreach($this->importables as $importable)
      {
          $importable->import();
      }
   }

}

这看起来不错而且很实用。但是,问题是每个实现现在都使用GenericImporter的OWN实例。那么最好的方法是什么呢?我是否将Generic Importer实现为Singleton?另外,为了我的研究目的,这是否属于某种设计模式?它似乎与ObservorPattern相似,只是每个Observer都在注册。

2 个答案:

答案 0 :(得分:0)

很少有事情...... 1)请注意......自从我接触PHP以来已经有好几年......但设计模式是我博士学位的一部分......但是......我建议你尽量坚持下去尽可能接近模式的命名约定(或者至少将类/方法记录为模式中的角色。因为我觉得如果你这样做,你将更容易实现它。

您实际上似乎正在实施-inverse- observer pattern。传统上,Subject对象保持一组观察者“正在”观察它(你只有一个观察者,但模式支持很多)。然后调用“notifyObservers()”循环遍历所有Observer并为每个Observer调用'notify()'。您似乎试图以“其他方式”发送通知。这可能是您获得'GenericImporter'的多个实例的原因。你希望'importerOne''通知''导入者',但Observer模式会让你反过来做,你会希望'importerOne'等是'主题'和'进口商' '成为(单一)'观察者'。

这种设计在您希望程序运行的方式上可能不那么自然,但这就是模式的设计方式。你有“观察者”做得太多了。 'Observer'是一个被动的actor,只是在Observed(Subject)类调用“notifyObservers()”时调用它们的'update(Observable o,Object arg)'方法。如果你这样设计它,你的“观察者”类可以非常简单......(如下面的Java示例)只要你创建一个实现所有Subjects类扩展的'Observable'功能的基类(importerOne, importerTwo等。)

[Observer]实现'notify()'方法/函数。这个'notify()'是Observer被'通知'时调用的,而不是它想要'通知'另一端。这是带有详细注释的wiki Java示例(我将解释这个,因为我多年没有触及PHP,并且可以更好地解释维基页面上的java示例)

import java.util.Observable;
import java.util.Scanner;

// This is the [Subject](Observed) class
class EventSource extends Observable implements Runnable {
    public void run() {
        while (true) {
            String response = new Scanner(System.in).next();
            setChanged();
            notifyObservers(response);
        }
    }
    // Notice that EventSource 'extends' [Observable], which means it has...
    // the following methods available to it also...(among others)...
    //
    // addObserver(Observer o) // adds a new Observer
    // deleteObserver(Observer o) // deletes a specific Observer
    // notifyObservers(Object arg) // Notifies all observers with some 'arg'
    //
    // Notice that 'notifyObservers(    )' is the only one called
}
import java.util.Observable;
import static java.lang.System.out;

class MyApp {
    public static void main(String[] args) {
    out.println("Enter Text >");

    // This is the [Subject](Observed class/object)
    EventSource eventSource = new EventSource();

    // here the 'Subject' is registering the observer
    eventSource.addObserver(
        // This is java 8 notation for a new anonymous class
        // This creates an instance of the [Observer](link below) Interface
        // The Observer Object only has one method...
        // update(Observable o, Object arg){}
        // The below code is the 'implementation' of the...
        // 'update(Observable o, Object arg)' method
        // 'obj' is the object that was being observed, 
        // 'arg' is the object passed into 'notifyObservers(Object)'...
        // from within the [Subject](observed) object.
        (Observable obj, Object arg) -> 
        {  
             // This code here is what is called when an Observed object 
             // calls 'notifyObservers()' (in this case only '1' observer)
             out.println("\nReceived response: " + arg);
        } 
    );
}

Observer JavaDoc

希望这可以帮助您调整程序以更传统的方式使用观察者模式,并允许您在文档中记录您正在做什么/等。

答案 1 :(得分:0)

你这么说:

  

问题是每个实现现在都在使用他们的OWN   GenericImporter的实例

我不认为这会是一个问题,因为GenericImporter只是您在实现中注入的依赖项。此外,您传递的GenericImporter 始终是同一个对象,因为它将通过'reference'传递

修改

关于以下评论,当你说:

  

当依赖关系通过IoC容器解决时,它不是   通过引用传递。它正在实例化该类的新实例

这取决于你在ioC容器中如何进行绑定:如果你使用instance(),以这种方式绑定Importer:

$importer = new Importer();
$this->app->instance( Importer::class, $importer );

然后,当您的应用程序中请求$importer依赖项时,将从ioC容器中解析出相同的Importer实例。

结束编辑

无论如何,我会通过添加界面来改进设计;像这样的东西:

//INTERFACE FOR IMPORTABLES
interface Importable
{
    public function __construct( Model $model ); 
    public function import();
}

//IMPLEMENTATION OF IMPORTABLES
class importerOne implements Importable 
{   
   public function __construct( SomeModel $model )
   {
   }

   public function import()
   {
       //logic
   }
}

//THE IMPORTER CLASS
public class Importer 
{    
   protected $importables = [];

   //attach any Importable
   public function attach( Importable $import_implementation )
   {
      array_push( $this->importables, $import_implementation) ; 
   }

   //trigger Importable::import
   public function import()
   {
      foreach($this->importables as $importable)
      {
          $importable->import();
      }
   }   
}

如果由于某些特定原因您不想将Importer的依赖项传递给您的可导入项,为什么不从客户端附加可导入项?像这样:

$importer = new Importer();

$importer->attach( $importable_1 );
$importer->attach( $importable_2 );
//$importer->attach( $importable_n );

$importer->import();

这样导入者就不需要传递Importer依赖关系

根据您构建importable的方式,您还可以考虑在数组中构建和存储所有这些内容并将数组传递给Importer:

$importer->attachAll( $importables_array );