正确的方法来实现“分层”类逻辑

时间:2014-02-03 10:45:33

标签: php oop design-patterns inheritance override

我必须承认,我不知道我的设计模式是否有问题,过度思考甚至只是课程命名,所以欢迎任何建议。这一定是一个简单的问题,但我甚至很难用文字(语言障碍)解释它,所以我会尝试在代码中进行。

例如,我试图逐字节地迭代网络数据流(该任务与问题完全无关,只是为了更好地解释伪代码,请不要说我使用错误的工具来做到这一点) 。基类看起来像这样:

class BaseNetworkIterator implements Iterator {

    protected $stream;
    protected $currentByte;

    public function __construct() {
        $this->stream = open_network_stream();
    }

    /** .... * */
    public function next() {
        $this->currentByte = $this->stream->getByte();
    }

    public function current() {
        return $this->currentByte;
    }

}

现在想象一下这个类中的逻辑本身是复杂的(每个方法不是一行,还有更多辅助方法)但我需要做更多。例如,我需要在每个字节中反转位顺序。我不想在基类中包含这个逻辑,因为它已经很大了,所以我扩展它:

class ReverseByte extends BaseNetworkIterator {
    /** .... * */
    public function next() {
        parent::next();
        $this->currentByte = $this->reverseStreamByte($this->currentByte);
    }

    protected function reverseStreamByte($byte) {
        /** .... * */
    }
}

此外,我想完全跳过此流中的所有换行符(反向):

class SkipNewLine extends ReverseByte {
    /** .... * */
    public function next() {
        do {
            parent::next();
        } while ($this->currentByte === "\n");
    }

}

如果我们得到两个连续的字节(在所有这些操作之后 - 它们可能被新行等分开)等于“NO”,那么我们必须再次反转一个后续字节(不要考虑它:只是某些东西)它使用以前的所有逻辑和在以前的类之一中定义的受保护方法:

class HandleDoubleReverseKeyword extends SkipNewLine {

    const DOUBLE_REVERSE_KEYWORD = 'NO';

    /** .... * */
    public function next() {
        parent::next();
        if ($this->getTwoCharsCache() === self::DOUBLE_REVERSE_KEYWORD) {
            $this->currentByte = $this->reverseStreamByte($this->currentByte);
            $this->clearTwoCharsCache();
        } else {
            $this->saveMaxTwoCharsCache($this->currentByte);
        }
    }

    private function saveMaxTwoCharsCache($newChar) {
        /** .... * */
    }

    private function getTwoCharsCache() {
        /** .... * */
    }

    private function clearTwoCharsCache() {
        /** .... * */
    }

}

现在虽然逻辑在类之间分配得相当不错,但我的口味(每个连续的类都做了一些完全不同且孤立的东西,并且很容易看出每个层中做了什么),这是一个混乱的工作。如果我们在中间添加新的“过滤器”,我们必须改变以后类扩展的内容...命名开始变得疯狂,因为“HandleDoubleReverseKeyword”没有解释这个类甚至远程实际做了什么(即使我们在“NetworkIterator”中“命名空间”。我真的不明白“最终”课程应该是什么样的。

我正在考虑使用特征,因为命名更容易,但是它们会导致更多问题然后解决(执行顺序,代码完成等)。

我还在考虑使用类似Yii的“行为/事件”模式(类似:base next()方法连续调用类/方法的数组,并且一些外部源可以向该数组添加更多的类/方法而不是扩展基类)但是属性/方法可见性存在很大问题(这是一个问题,基本上我必须公开所有属性和方法),更重要的是,不清楚如何正确访问其他附加类的方法。 / p>

好的,这仍然可能不太清楚,所以任何反馈都会受到赞赏。我知道这个“问题”中没有问号,但我希望我的意图是明确的,唯一想到的是:如何正确地进行这种设计?

2 个答案:

答案 0 :(得分:1)

我的2美分:

  1. 应该将流与迭代器类及其子类分开。 Iterator定义了如何访问数据,每个迭代器实例应该只保存对数据的引用,而不是实际数据。

  2. HandleDoubleReverseKeyword类中的逻辑是关于如何处理数据,而不是如何访问数据;并根据数据来决定操作,因此该类不应该从迭代器类继承;在这里,我认为我们可以使用状态模式。

  3. 因此处理流的代码可能是这样的:

    $stream = open_network_stream();
    $iterator it = new Iterator(&stream);
    $data = it->next(); 
    while(data){
      switch(data->value){
         case 'A': AcitionA->action();break;
         case 'B': AcitionB->action();break;
         }
    $data = it->next();
    }
    

答案 1 :(得分:1)

我认为你是从错误的角度来看待这个问题。

您现有的当前层次结构极大地限制了每个组件的可重用性,因为它们都是为了只对字节流迭代器的结果起作用。例如,如果您需要实现文件迭代器,然后跳过每个新行,则无法重用现有的SkipNewLine,因为它仅适用于反向字节迭代器的结果。

另一个谬论是,如果不重写整个类结构,就无法控制每个步骤的发生顺序。即使只是在SkipNewLine之前(或在ReverseByte之后)移动HandleDoubleReverseKeyword课程就像重构噩梦一样。

我建议您将迭代中的每个步骤实现为一个单独的类,它只作用于通用流,并且不对源流的状态做出任何假设。您可以将每个组件的实例注入BaseNetworkIterator,并相互独立地使用它们。

这将进一步为您提供在了解迭代的全局状态的情况下对每个状态采取行动的能力。