2012-04-24 3 views
3

J'ai un analyseur avec trois classes d'objets: l'analyseur lui-même, Token s, et State s. L'analyseur génère des jetons à partir de la lexer. Tout est en boîte noire, donc les jetons ne savent rien de l'état de l'analyseur ou de l'analyseur, et l'état ne sait rien des jetons. Une version assez simple de l'arrangement:Injection de dépendance de composition ou d'héritage

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

Le problème que je suis en cours d'exécution en est que, parfois, quand un changement d'état, l'analyseur doit prendre des mesures (par exemple ajouter une règle à l'arbre lorsque le Ending- le jeton de règle est atteint). Seul le State le sait, donc c'est à l'État de dire à l'analyseur quoi faire. Le problème est d'obtenir le Parser au State. Je pourrais injecter le Parser dans le constructeur d'état, mais pas tous les State besoin de l'analyseur, et cela conduirait à beaucoup de code en double (sauf si j'avais une classe de base pour le State s et le Parser était un membre protégé, mais je veux pour éviter d'étendre quoi que ce soit). Je pourrais également injecter le Parser dans les méthodes state qui en ont besoin, mais j'ai un problème similaire: ce serait beaucoup de duplication, et pas toutes les implémentations State auraient besoin de l'analyseur pour les méthodes données.

Donc, ma question est de savoir comment puis-je obtenir le State savoir sur le Parser quand il doit sans héritage ou duplication de code inutiles? Si j'ai besoin d'une autre classe, c'est parfaitement acceptable.


Dans le cas où il est difficile de suivre, voici une version « effilochée »:

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

La réponse à cette question pourrait appliquer à d'autres langues, mais je sais que ce serait plus facile à faire dans les langues qui permettent la surcharge. PHP ne le fait pas.

+0

Pouvez-vous expliquer pourquoi vous «voulez éviter d'étendre quoi que ce soit». Cela semble être une chose très étrange à dire en ce qui concerne le code OO. – FtDRbwLXw6

+0

@drrcknlsn Il ya une petite (je pense) avant-garde des gens qui détestent tellement l'héritage qu'ils ne veulent jamais l'utiliser et favoriser la composition dans tous les cas. Je vais céder si c'est nécessaire, mais gardez à l'esprit que tous les états n'ont pas besoin d'accéder à l'analyseur de toute façon, donc ce n'est peut-être même pas approprié. –

+0

Je n'ai jamais entendu parler d'une telle chose. L'un des principaux avantages de la conception OO est l'héritage. En ce qui concerne les enfants ayant besoin d'un accès ou non, c'est là que plusieurs niveaux d'héritage entrent en jeu. Ceux qui ont besoin d'accéder étendraient une classe qui a l'analyseur injecté (qui lui-même étendrait la classe de base). Ceux qui n'ont pas besoin d'accéder étendraient simplement la classe de base. Vous pouvez également résoudre ceci avec la composition si vous voulez, mais meh ... – FtDRbwLXw6

Répondre

1

PHP 5.4 présente des traits: http://php.net/manual/en/language.oop5.traits.php

Peut-être que vous pourriez utiliser des traits comme à mi-chemin entre l'héritage et l'injection.

+0

Les traits seraient géniaux .. si seulement je * pouvais * utiliser PHP 5.4 –