2010-03-02 5 views
1

Я хочу написать простой класс (PHP5), который может «запускать» неизвестное количество подклассов. Эти подклассы лучше всего переводить как «чеки»; все они будут более или менее делать то же самое и давать ответ (true/false). Подумайте об этом как о проверке запуска системы.Что такое хороший PHP-дизайн (шаблон?) Для моего тестового приложения?

С течением времени в каталог будут добавлены новые подписи (подклассы), и они должны автоматически запускаться при вызове основного класса. Другие могут писать проверки, но им придется следовать функциям, продиктованным основным классом.

Что было бы простым, но простым способом построить это? Я нашел шаблон Factory и в сочетании с интерфейсом, он кажется хорошей отправной точкой. Однако я не уверен, и я был бы признателен за советы по этому поводу.

EDIT: предоставленные ответы здесь все работают, однако ответ Гордона дает дополнительные возможности партий и укладки, о чем я не думал во время запроса, но теперь я довольно доволен.

ответ

3

Если вы хотите создать пакетную функцию, используйте Command Pattern.

Ниже представлена ​​очень простая реализация шаблона. Идея состоит в том, чтобы иметь унифицированный интерфейс для всех классов, которые вы хотите вызвать. Каждый класс инкапсулирует одну операцию в пакете:

interface BatchCommand 
{ 
    public function execute(); 
    public function undo(); 
} 

Один класс, реализующий интерфейс будет командиром всех подклассов:

class BatchCommander implements BatchCommand 
{ 
    protected $commands; 

    public function add(BatchCommand $command) 
    { 
     $this->commands[] = $command; 
    } 
    public function execute() 
    { 
     foreach($this->commands as $command) { 
      $command->execute(); 
     } 
    } 
    public function undo() 
    { 
     foreach($this->commands as $command) { 
      $command->undo(); 
     } 
    } 
} 

простая команда может выглядеть следующим образом:

class FileRename implements BatchCommand 
{ 
    protected $src; 
    protected $dest; 

    public function __construct($src, $dest) 
    { 
     $this->$src; 
     $this->dest; 
    } 

    public function execute() 
    { 
     rename($this->src, $this->dest); 
    } 

    public function undo() 
    { 
     rename($this->dest, $this->src); 
    } 
} 

Затем вы можете использовать его следующим образом:

$commander = new BatchCommander; 
$commander->add(new FileRename('foo.txt', 'bar.txt')); 
$commander->add(/* other commands */); 
$commander->execute(); 

Поскольку BatchCommander является самой BatchCommand, вы можете легко складывать партии, принадлежащие друг другу, в другие партии, создавая тем самым очень гибкую древовидную структуру, например.

$batch1 = new BatchCommander; 
$batch1->add(/* some command */); 
$batch1->add(/* some other command */); 

$batch2 = new BatchCommander; 
$batch2->add(/* some command */); 
$batch2->add(/* some other command */); 

$main = new BatchCommander; 
$main->add($batch1); 
$main->add($batch2); 
$main->execute(); 

В контексте проверки/тестирования это означает, что вы можете сгруппировать концептуально принадлежащие одиночные тесты в тестовый набор. И вы можете создавать тестовые комплекты тестовых наборов, складывая один набор в другой.

Конечно, вы также можете дать BatchCommander путь к файлу для проверки на экземпляр, а также запустить все BatchCommands, выполнив файлы в пути к файлу. Или передайте экземпляр Factory для создания команд проверки.

Для имен методов не требуется execute и undo. Назовите его check, если хотите. Оставьте undo, если вам это не нужно. Основная идея по-прежнему остается прежней: один интерфейс для всех команд, которые будут командовать, что бы это ни казалось. Не стесняйтесь адаптироваться.

Альтернатива с несколько иной UseCase - это шаблон Chain of Reponsibility. Проверьте это, чтобы убедиться, что это тоже полезно.

+0

Благодарим вас за ответ. Что-то я не совсем понимаю; вы пишете * Один класс, реализующий интерфейс, будет командиром *, а затем запустите код с помощью * public function BatchCommander реализует BatchCommand * Это опечатка или я не понимаю принцип? – MattW

+0

@ Matt это специально. BatchCommander управляет всеми BatchCommands, но это также сама команда. Это позволяет создать произвольную древовидную структуру, сгруппировав BatchCommands в BatchCommander. Я буду обновлять сообщение для примера. – Gordon

+0

Еще раз спасибо Гордон, я ценю ваше время и дополнения. Я действительно пропустил точку (группировку и укладку), которую я сейчас понимаю (и я в восторге от этого :)) Однако я до сих пор не понимаю, где _invoke_ _public function batchCommander_ К моему (действительно ограниченному) знание это метод, который должен находиться в классе, но какой класс? Если я использую ваш пример, я получаю * Ошибка синтаксиса: ошибка синтаксиса, непредвиденная ошибка T_PUBLIC * относительно строки _public function_ nr. – MattW

1

Я думаю, проблема в том, как вы находите классы, которые вы должны создавать и запускать, поскольку вам нужно знать имя класса.

Я предлагаю вам загрузить каждый * .php-файл в каталог и использовать простое соглашение об именах. Например, имя файла (минус расширение) - это имя класса. Тогда просто:

// foreach $file in glob("*.php"): 
    $classname = basename($file, ".php"); 
    $instance = new $classname(); 
    $result = $instance->runCheck(); 

Где runCheck() фактический метод, который должен обеспечить каждый класс.

Вы ничего не говорили о запуске чеков в каком-либо конкретном порядке, но это можно было бы исправить, добавив префикс nn_ к каждому файлу и загрузив его по порядку. Например, 05_CheckFoo.php.

+0

Это несколько неэффективно. Если файлы следуют шаблону именования (например, вы предложили), например. '01_individual-filename.checkcommand.php', он может лучше делать' glob ('*. Checkcommand.php') 'вместо того, чтобы проходить через все файлы. – Gordon

+0

Исправлено, хотя я не думаю, что проблема с бритьем в несколько миллисекунд здесь. –

0

Проверка автозагрузки классов, поэтому вам не нужно заботиться о том, сколько подклассов существует. PHP возьмет все: http://php.net/manual/en/language.oop5.autoload.php

+0

Вам все равно нужно знать фактическое имя класса для загрузки, и для этого вам все равно нужно проверять существующие файлы (или поддерживать отдельный список классов). –

Смежные вопросы