Php 组合与继承依赖注入

Php 组合与继承依赖注入,php,language-agnostic,Php,Language Agnostic,我有一个具有三类对象的解析器:解析器本身、Tokens和States。解析器从lexer生成令牌。所有内容都是黑盒的,因此标记对解析器状态或解析器一无所知,而状态对标记一无所知。该安排的一个相当简单的版本: class Parser { public function parse() { $this->state = new StEmpty; while ($token = $this->lexer->get()) { $this

我有一个具有三类对象的解析器:解析器本身、
Token
s和
State
s。解析器从lexer生成令牌。所有内容都是黑盒的,因此标记对解析器状态或解析器一无所知,而状态对标记一无所知。该安排的一个相当简单的版本:

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;
   }
}
我遇到的问题是,有时当一个状态发生变化时,解析器需要采取一些措施(例如在到达结束规则标记时向树中添加一个规则)。只有
状态
知道这一点,因此由状态来告诉解析器该做什么。问题是将
解析器
置于
状态
。我可以在状态构造函数中插入
解析器
,但不是每个
状态
都需要解析器,这将导致大量重复代码(除非我为
状态
创建了基类,并且
解析器
是受保护的成员,但我希望避免扩展任何内容)。我还可以将
解析器
注入需要它的
状态
方法中,但我有一个类似的问题:这将是大量重复,并且不是所有的
状态
实现都需要给定方法的解析器

因此,我的问题是,当需要
解析器时,如何让
状态
了解它,而不需要不必要的继承或代码复制?如果我需要另一门课,那是完全可以接受的


如果这很难理解,这里有一个“解开的”版本:


这个问题的答案也适用于其他语言,但我知道这在允许重载的语言中更容易实现。PHP没有。PHP 5.4引入了以下特性:


也许你可以使用traits作为继承和注入之间的一个中间环节。

你能解释一下为什么你“想要避免扩展任何东西”。对于OO代码来说,这似乎是一个非常奇怪的说法。@drrcknlsn有一个小问题(我想)那些非常讨厌继承的人的先锋,他们永远不想使用它,并且在所有情况下都喜欢组合。如果有必要,我会松口,但请记住,并非所有状态都需要访问解析器,因此这可能并不合适。我从未听说过这样的事情。面向对象设计的主要好处之一是继承。至于是否需要访问的孩子,这就是多层次继承发挥作用的地方。那些需要访问的将扩展一个具有注入解析器的类(解析器本身将扩展基类)。那些不需要访问的只需要扩展基类。如果您愿意,您也可以通过组合来解决这个问题,但是meh…@drrcknlsn
解析器不能也不应该知道何时注入和何时不注入。这意味着它将在不适当的时间尝试注入(这并不重要。未使用的参数将被忽略)。扩展单独类的问题是,如果必须更改一个类来使用解析器,那么我认为在这种情况下,最好始终具有访问权限,尽管仍然不理想。当然,
解析器可以而且应该知道何时注入。如果它正在实例化需要它的子类,那么注入;否则,不要。它知道根据当前处于的状态实例化哪个子类。如果它不知道当前处于什么状态,那么您就有一个功能失调的解析器-那就太棒了。。如果我能使用PHP5.4就好了
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;
         }
      }
   }
}