Мобильная
версия

Тестовое задание - ZOON.ru (часть 2)

Дата: Категория: PHP

В прошлой статье мной описывалось одно очень интересное тестовое задание

Если вы не ознакомились с первой статьей, то самое время это сделать здесь

Я показал само тестовое задание (простая ссылка) и благодаря некоторым усилиям я получил описание и само тестовое задание.
В прошлой статье я обещал рассказать как его делал и привести примеры кода.

Для самых нетерпеливых - github

Ну чтож, приступим.

Первым делом опишем наши команды (IF, DROP, итп)
Создаем абстрактный класс command

abstract class commands{
    protected $stek=[],$out=[];
    protected $number=NULL;
    protected $IP=1;
    protected $task=true;
    
    public function __construct(array $stek) {
        $this->stek=$stek;
        $this->out=[];
        if(!sizeof($stek))$this->task=false;
    }
    function task(){ // проверка, можноли продолжнать выполнение этой задачи или переход к следующей
        return $this->task;
    }
    function get_out(){ // считываем выход команды
        return $this->out;
    }
    function get_iteration(){ // номер итерации (для команд G и _IF)
        return $this->IP;
    }
    abstract function execute();
}

Так как класс абстрактный - то нам нужен как минимум один абстрактный метод - execute. В нем и будет происходить выполнение всех команд
В данном решении я буду применять паттерн "абстрактная фабрика"

Абстрактный класс у нас есть - теперь осталось унаследовать от него все остальные классы команды.
Здесь я приведу только несколько команд. С остальными вы можете ознакомиться здесь

//  [число] добавляет число на вершину стека; $IP++
class NUMBER extends commands{
    // констурктор переопределен, тк стек при вызове может быть пустой (конструктор родителя вернет false)
    // И нам нужно число положить на верх стека.
    public function __construct(array $stek,$number) {
        $this->stek=$stek;
        $this->out=[];
        $this->number=$number;
        $this->task=true;
    }
    
    function execute(){
        array_unshift($this->stek, $this->number);
        return $this->stek;
    }
}
//  IF(заменяем на _IF)      снимает с вершины стека $q и затем снимает с вершины $p. если $p==0, то $IP = $IP+$q+3; иначе $IP++
class _IF extends commands{
    function execute() {
        $q=array_shift($this->stek);
        $p=array_shift($this->stek);
        if($p==0)$this->IP=$q+3;
        return $this->stek;
    }
}
//  G       снимает элемент $p с вершины стека и перемещает указатель команд: $IP+=$p-2
class G extends commands{
    function execute() {
        $p=array_shift($this->stek);
        $this->IP=$p-2;
        return $this->stek;
    }
}

Тут все настолько просто прозрачно и прокоментировано, что я даже не буду задерживаться на описании файла command.php

Теперь, когда у нас есть все готовые команды, их надо объединить и заставить работать.
Сделаем все это с помощью класса stek

class stek{
    
    public $stek=[];
    private $out=[];
    private $commands=[];
    private $IP=0;
    
    public function __construct(array $arr) {
        $this->stek=$arr;
    }
    
    function command(array $arr){
        $this->commands=$arr;
        while(true){ // бесконечный цикл с проверкой в фуенкции execute
            if(!$this->execute())break;
        }
        // возвращаем выход задачи
        return $this->out;
    }
    
    function execute(){
        // если нет команды в задаче то переходим к следующей
        if(!isset($this->commands[$this->IP]))return false;
        
        $item=$this->commands[$this->IP];
        $command=$this->get_command($item);
        // если стек пустой, то переходим к следующей зхадачи
        if(!$command->task())return false;
        $this->stek=$command->execute();
        // забивает выход команды в массив
        $this->out=array_merge($this->out,$command->get_out());
        // увеличиваем номер команды на...
        $this->IP+=$command->get_iteration();
        
        return true;
    }
    
    private function get_command($name){
        // все команды это классы - поэтому заменяем названия команд чтобы можно было назвать ими классы
        $name=str_replace(['+','*','IF'],['PLUS','SHARE','_IF'],$name);
        if(!class_exists($name)){
            // если такого класса не существует то создаем класс ЧИСЛО
            $number=$name;
            $name='NUMBER';
        }
        else{
            $number=NULL;
        }
        return new $name($this->stek,$number);
    }
    
}

Что здесь происходит?

Создаем класс и в конструкторе задается общий стек
После создания класса вызываем метод command, который в бесконечном цикле выполняет текущую задачу
Бесконечной цикл нужен для некоторых условий - например:
    Первое число в задаче - 10, и выполняем DEC (отнимаем 1 от первого элемента) пока он не будет равен 0
    Конечно такого легкого примера в задании не будет - они там намного сложнее.
Если команды нет, то завершаем выполнение текущей задачи, возвращаем вывод и переходим к следующей.

Функция get_command как раз и есть та абстрактная фабрика.

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

Осталось дело за малым - перебрать все задачи и выполнить их в классе stek

class main{
    
    private $text=[];
    private $main_array=[];
    
    public function __construct() {
        $quest_url="http://zoon.ru/job.php?name=rijen&contact=false&cv=false";
        $response=json_decode(file_get_contents($quest_url));
        $this->main_array=$response->task; // получаем массив с 3мя задачами
    }
    
    function start(){
        $return=[];
        foreach($this->main_array as $key=>$check){
            echo 'Iteration: '.$key."\n";
            // для каждой задачи создаем свой класс
            $stek=new stek($return);
            $return=$stek->command($check); // выход из каждой задачи идет на стек следующей
        }
        $this->text=$return;
        return $return;
    }
    
    public function var_dump(){ // на последнем выходе мы должны получить ссылку. После выполнения вызываем этот метод.
        return join('',$this->text);
    }
}
echo "start \n";
$main=new main();
$main->start();
echo "result:\n";
echo $main->var_dump();

В функции start я перебираем весь массив с задачами и отправляем их в stek

На выходе у нас получится ссылка, перейдя по которой можно увидеть поздравления. Ура, тест пройден.

Еще раз ссылка на gitbub

PS
Если вы измените параметры ссылки, то данный код скорее всего работать не будет так как там появятся новые команды.
В этом случае просто допишите их в command.php в виде классов.

Теги: #php, #тестовое задание

Ваша оценка:

Рейтинг: 9.5 (Оценок: 2)

Комментарий:

Copyright © DOC_tr 2015-2017 г. Все права защищены
Яндекс.Метрика
Перейти к мобильной версии