2012-04-24 3 views
3

У меня есть анализатор с тремя классами объектов: сам анализатор, Token и State. Парсер генерирует токены из лексера. Все черное окно, поэтому токены ничего не знают о состоянии парсера или парсере, и государство ничего не знает о токенах. Довольно простой вариант расположения:Комбинация против инъекции зависимостей наследования

class Parser { 
    public function parse() { 
     $this->state = new StEmpty; 
     while ($token = $this->lexer->get()) { 
     $this->state = $this->token->expect($this); 
     } 
    } 
    public function stateStart() { 
     return $this->state->stateStart(); 
    } 
} 
class StartToken { 
    public function expect(Parser $parser) { 
     return $parser->stateStart(); 
    } 
} 
class StEmpty { 
    public function stateStart() { 
     return new StStart; 
    } 
} 

Проблема я бегу в том, что иногда при изменении состояния, синтаксический анализатор должен предпринять определенные действия (например, добавить правило к дереву, когда ending- маркер правила достигается). Только State знает, что, так что до состояния нужно сказать парсеру, что делать. Проблема заключается в том, что Parser относится к State. Я мог бы ввести Parser в конструктор состояний, но не каждый State нуждается в синтаксическом анализаторе, и это приведет к большому дублированию кода (если бы у меня не был базовый класс для State s, а Parser был защищенным членом, но я хочу чтобы не расширять что-либо). Я мог бы также ввести Parser в методы state, которые ему нужны, но у меня есть аналогичная проблема: это было бы много дублирования, и не всем реализациям State понадобился бы парсер для данных методов.

Так что мой вопрос: как я могу получить State, чтобы узнать о Parser, когда это необходимо, без ненужного наследования или дублирования кода? Если мне нужен другой класс, это вполне приемлемо.


В случае, если это трудно следовать, вот «разгадана» версия:

class Parser { 
    public function parse() { 
     $this->state = 'StEmpty'; 

     while ($token = $this->lexer->get()) { 
     switch ($token) { 
      case 'StartToken': 
       switch ($this->state) { 
        case 'StEmpty': 
        $this->state = 'StStart'; 
        break; 
       } 
       break; 
     } 
     } 
    } 
} 

Ответ на этот вопрос может применяться и к другим языкам, а также, но я знаю, что это было бы легче делать на языках, допускающих перегрузку. PHP не делает.

+0

Можете ли вы объяснить, почему вы «хотите избежать распространения чего-либо». Это выглядит очень странно, если говорить о коде OO. – FtDRbwLXw6

+0

@ drrcknlsn есть небольшой (я думаю) авангард людей, которые ненавидят наследование настолько, что они никогда не хотят использовать его и предпочитают композицию во всех случаях. Я буду смягчать, если это необходимо, но имейте в виду, что не все государства нуждаются в доступе к синтаксическому анализатору в любом случае, поэтому это может быть даже не уместным. –

+0

Я никогда не слышал об этом. Одним из основных преимуществ дизайна OO является наследование. Что касается детей, нуждающихся в доступе или нет, то здесь начинают действовать несколько уровней наследования. Те, кому нужен доступ, будут расширять класс, в который вводится Parser (который сам расширяет базовый класс). Те, кому не нужен доступ, просто расширят базовый класс. Вы также можете решить это с помощью композиции, если хотите, но meh ... – FtDRbwLXw6

ответ

1

PHP 5.4 вводит черту: http://php.net/manual/en/language.oop5.traits.php

Может быть, вы могли бы использовать черты как на полпути между наследованием и инъекцией.

+0

Черты были бы удивительными .. если бы только я * мог * использовать PHP 5.4 –

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