设计一个 Class 要求几个 Class 执行相同的任务
Design For A Class Asking Several Classes to Perform The Same Task
我有一个 Laravel 应用程序,我正在创建一个 class 来导入数据。数据存储在大约 15 个不同的模型中,因此我有一个 Class 来处理每个模型,因为每个模型都有自己的实现。此外,未来可能会添加更多模型。
我想在 Importer
class 中有一行代码,例如 $importer->import()
,然后它会占用所有 15 个实现 classes,并且调用他们的 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...
}
这看起来不太好,因为这个 class 将有 15 个以上的依赖项,所有这些基本上都在做同样的事情。然后要更新它,我需要进入此处并添加/删除依赖项和 import()
方法调用。
所以我的解决方案是将 'registration' 导入器委托给每个实现,而不是让通用导入器 class 负责。实际上,是某种观察者。但是不是客户端将观察者附加到主题,而是每个实现都将自己附加到主题。
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 实例。那么最好的方法是什么?我是否将 Generic Importer 实现为 Singleton?另外,出于我的研究目的,这是否属于某种设计模式?它看起来类似于 ObservorPattern,只是每个 Observer 都在注册自己。
几件事... 1) 请注意...我接触 PHP 已经很多年了...但是设计模式是我博士研究重点的一部分...但是...我确实建议您尽量遵循模式的命名约定(或者至少将 classes/methods 记录为它们在模式中扮演的角色。因为我觉得如果您可以更轻松地实现这一点做到了。
您实际上似乎在实施-逆向-observer pattern。传统上,Subject 对象保留一组 'are' 观察它的观察者(您将只有 1 个观察者,但该模式支持很多)。然后调用 "notifyObservers()" 循环遍历所有 Observer 并为每个 Observer 调用 'notify()' 。您似乎正试图向 'other way' 发送通知。这可能就是您获得 'GenericImporter' 的多个实例的原因。你希望 'importerOne' 到 'notify' 'importer' 但观察者模式会让你反过来做,你会希望 'importerOne' 等成为 'Subject'(s) 和 'Importer' 成为(单个)'Observer'。
这种设计可能不太符合您希望程序运行的方式,但这就是模式的设计方式。你让 "Observer" 做得太多了。 'Observer' 是一个被动的参与者,当 Observed(主题)class 调用 "notifyObservers()" 时,只会调用他们的 'update(Observable o, Object arg)' 方法。如果你这样设计,你的 "observer" class 可以非常简单......(就像下面的 Java 例子)只要你创建一个实现的基础 class 'Observable' 您的所有主题 class 扩展的功能(importerOne、importerTwo 等)。
[Observer] 实现了 'notify()' method/function。这个 'notify()' 是 Observer 在 'notified' 时调用的,而不是它想要 'notify' 另一端。这是带有详细注释的 wiki Java 示例(我将对此进行解释,因为我已经多年没有接触 PHP,并且可以更好地解释 wiki 上的 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);
}
);
}
希望这会帮助您调整程序以更传统的方式使用观察者模式,并允许您在文档中注明您的身份 doing/etc。
你这样说:
the problem is that each implementation is now using their OWN
instance of the GenericImporter
我认为这不是问题,因为 GenericImporter
只是您在实现中注入的依赖项。此外,您传递给 的 GenericImporter
始终是同一个对象 ,因为它会被 'reference'
传递
编辑
关于下面的评论,当你说:
when a dependency is resolved through the IoC Container, it isn't
passing by reference. It's instantiating a new instance of that Class
这取决于你如何在 ioC 容器中进行绑定:如果你使用 instance()
,以这种方式绑定 Importer:
$importer = new Importer();
$this->app->instance( Importer::class, $importer );
然后当在您的应用程序中请求 Importer
依赖项时,相同的 $importer
实例将从 ioC 容器中解析出来。
编辑结束
无论如何,我会通过添加一个界面来改进设计;像这样:
//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
依赖项传递给他们
根据您构建可导入项的方式,您还可以考虑构建并将所有这些项存储在一个数组中,并将该数组传递给导入器:
$importer->attachAll( $importables_array );
我有一个 Laravel 应用程序,我正在创建一个 class 来导入数据。数据存储在大约 15 个不同的模型中,因此我有一个 Class 来处理每个模型,因为每个模型都有自己的实现。此外,未来可能会添加更多模型。
我想在 Importer
class 中有一行代码,例如 $importer->import()
,然后它会占用所有 15 个实现 classes,并且调用他们的 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...
}
这看起来不太好,因为这个 class 将有 15 个以上的依赖项,所有这些基本上都在做同样的事情。然后要更新它,我需要进入此处并添加/删除依赖项和 import()
方法调用。
所以我的解决方案是将 'registration' 导入器委托给每个实现,而不是让通用导入器 class 负责。实际上,是某种观察者。但是不是客户端将观察者附加到主题,而是每个实现都将自己附加到主题。
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 实例。那么最好的方法是什么?我是否将 Generic Importer 实现为 Singleton?另外,出于我的研究目的,这是否属于某种设计模式?它看起来类似于 ObservorPattern,只是每个 Observer 都在注册自己。
几件事... 1) 请注意...我接触 PHP 已经很多年了...但是设计模式是我博士研究重点的一部分...但是...我确实建议您尽量遵循模式的命名约定(或者至少将 classes/methods 记录为它们在模式中扮演的角色。因为我觉得如果您可以更轻松地实现这一点做到了。
您实际上似乎在实施-逆向-observer pattern。传统上,Subject 对象保留一组 'are' 观察它的观察者(您将只有 1 个观察者,但该模式支持很多)。然后调用 "notifyObservers()" 循环遍历所有 Observer 并为每个 Observer 调用 'notify()' 。您似乎正试图向 'other way' 发送通知。这可能就是您获得 'GenericImporter' 的多个实例的原因。你希望 'importerOne' 到 'notify' 'importer' 但观察者模式会让你反过来做,你会希望 'importerOne' 等成为 'Subject'(s) 和 'Importer' 成为(单个)'Observer'。
这种设计可能不太符合您希望程序运行的方式,但这就是模式的设计方式。你让 "Observer" 做得太多了。 'Observer' 是一个被动的参与者,当 Observed(主题)class 调用 "notifyObservers()" 时,只会调用他们的 'update(Observable o, Object arg)' 方法。如果你这样设计,你的 "observer" class 可以非常简单......(就像下面的 Java 例子)只要你创建一个实现的基础 class 'Observable' 您的所有主题 class 扩展的功能(importerOne、importerTwo 等)。
[Observer] 实现了 'notify()' method/function。这个 'notify()' 是 Observer 在 'notified' 时调用的,而不是它想要 'notify' 另一端。这是带有详细注释的 wiki Java 示例(我将对此进行解释,因为我已经多年没有接触 PHP,并且可以更好地解释 wiki 上的 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);
}
);
}
希望这会帮助您调整程序以更传统的方式使用观察者模式,并允许您在文档中注明您的身份 doing/etc。
你这样说:
the problem is that each implementation is now using their OWN instance of the GenericImporter
我认为这不是问题,因为 GenericImporter
只是您在实现中注入的依赖项。此外,您传递给 的 GenericImporter
始终是同一个对象 ,因为它会被 'reference'
编辑
关于下面的评论,当你说:
when a dependency is resolved through the IoC Container, it isn't passing by reference. It's instantiating a new instance of that Class
这取决于你如何在 ioC 容器中进行绑定:如果你使用 instance()
,以这种方式绑定 Importer:
$importer = new Importer();
$this->app->instance( Importer::class, $importer );
然后当在您的应用程序中请求 Importer
依赖项时,相同的 $importer
实例将从 ioC 容器中解析出来。
编辑结束
无论如何,我会通过添加一个界面来改进设计;像这样:
//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
依赖项传递给他们
根据您构建可导入项的方式,您还可以考虑构建并将所有这些项存储在一个数组中,并将该数组传递给导入器:
$importer->attachAll( $importables_array );