Дизайн для класса, запрашивающего несколько классов для выполнения одной и той же задачи

У меня есть приложение Laravel, и я создаю класс, который импортирует данные. Данные хранятся в 15 различных моделях, поэтому у меня есть класс для обработки, так как у каждой своя реализация. Кроме того, в будущем может быть добавлено больше моделей.

Я хотел бы иметь строку кода, как $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. Так каков наилучший способ сделать это? Реализую ли я общий импортер как синглтон? Кроме того, для моих исследовательских целей, это попадает в определенный шаблон дизайна? Это похоже на ObservorPattern за исключением того, что каждый Observer регистрирует себя.

1

Решение

Вы говорите, что:

проблема в том, что каждая реализация теперь использует свой собственный
экземпляр GenericImporter

Я не думаю, что это будет проблемой, потому что GenericImporter это только зависимость, которую вы вводите в реализации. Кроме того, GenericImporter вы проходите вокруг всегда один и тот же объект, как это будет передано «ссылка»

РЕДАКТИРОВАТЬ

Что касается комментария ниже, когда вы говорите:

когда зависимость разрешается через контейнер IoC, это не
проходя по ссылке. Это создание нового экземпляра этого класса

Это зависит от того, как вы делаете привязку в контейнере ioC: если вы используете instance(), чтобы связать импортер следующим образом:

$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 зависимость

В зависимости от того, как у вас построены ваши импортируемые файлы, вы также можете рассмотреть возможность создания и хранения всех их в массиве и передать массив в Importer:

$importer->attachAll( $importables_array );
0

Другие решения

Несколько вещей … 1) Имейте в виду … Прошло много лет с тех пор, как я коснулся PHP … но шаблоны проектирования являются частью моей кандидатуры PhD … Но … Я действительно рекомендую вам придерживаться как можно ближе к соглашениям об именах шаблонов (или, по крайней мере, документируйте классы / методы как роли, которые они играют в шаблоне. Потому что я чувствую, что вам было бы легче реализовать это, если бы вы это сделали.

Вы на самом деле, кажется, реализуете -inverse- схема наблюдателя. Традиционно объект Subject хранит массив наблюдателей, которые «наблюдают» за ним (у вас будет только 1 наблюдатель, но шаблон поддерживает многих). Затем вызывает «notifyObservers ()», который проходит через весь Observer и вызывает «notify ()» для каждого. Похоже, вы пытаетесь отправить уведомление «другим способом». Вероятно, поэтому вы получаете несколько экземпляров GenericImporter. Вы хотите, чтобы ‘importerOne’ уведомлял ‘импортера’, но шаблон Observer заставил бы вас сделать это наоборот, вы бы хотели, чтобы ‘importorterOne’ и т. Д. Были ‘субъектами’ и ‘Importer’ «быть (одиноким)« наблюдателем ».

Этот дизайн может быть немного менее естественным, если вы хотите, чтобы ваша программа работала, но так устроен шаблон. У вас «Наблюдатель» делает слишком много. Observer является пассивным актором и просто получает метод update (Observable o, Object arg), вызываемый, когда класс Observed (Subject) вызывает «notifyObservers ()». Если вы спроектируете его таким образом, ваш класс-наблюдатель может быть ОЧЕНЬ простым … (как пример Java ниже). Пока вы создаете базовый класс, который реализует функциональность Observable, которую расширяют все ваши классы Subjects (importerOne, импортер два и т. д.).

[Observer] реализует метод / функцию notify (). Это «notify ()» — это то, что вызывается, когда наблюдатель «уведомляется», а не то, что он хочет «уведомить» другой конец. Вот пример вики Java с подробными комментариями (я объясню это, так как я не касался PHP годами, и могу лучше объяснить этот пример на странице вики)

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);
}
);
}

Наблюдатель JavaDoc

Надеемся, что это поможет вам настроить вашу программу для использования шаблона наблюдателя более традиционным способом и позволит вам отметить в своей документации, что вы делаете / etc.

0

По вопросам рекламы [email protected]