【问题标题】:Organizing Classes in PHP在 PHP 中组织类
【发布时间】:2009-10-05 20:25:23
【问题描述】:

假设我的项目中有以下类:

  • class Is // 验证类
  • class Math // 数字操作类

现在,如果我想验证给定数字的素数,那么插入我的 Prime() 方法的逻辑位置在哪里?我可以想到以下选项:

  • Is_Math::Prime()
  • Math_Is::Prime()

我讨厌这些模棱两可的东西,它们会减慢我的思维过程并经常导致我犯错误。更多示例:

  • Is::Image() 还是 Image::Is() ?
  • Is_Image::PNG() 还是 Image_Is::PNG() ?
  • Is_i18n_US::ZipCode() 或 i18n_Is_US::ZipCode() 或 i18n_US_Is::ZipCode() ?

在 Image 示例中,第一个选择对我来说更有意义,而在 i18n 示例中,我更喜欢最后一个。没有标准让我觉得整个代码库很乱。

是否有组织课程的圣杯解决方案?也许是不同的范式?

【问题讨论】:

    标签: php oop class organization code-organization


    【解决方案1】:

    对于数学示例,我将检查数字是否为素数的实际功能放在Math 类中。在您的Is 类中,您将放置一个在需要进行验证时调用的方法。然后你可以从那里使用Math::Prime()

    对于Image,这是一个类型检查。除非您确保已上传有效的图像数据,否则您可能不需要为其创建方法。

    使用PNG 方法,与Math 相同。将实际的 PNG 数据检查器算法放在 Image 中,并让您在 Is 中的验证器方法调用它。

    邮政编码示例应该仅在您的Is 类中,因为它对字符串原语进行操作,并且可能只使用正则表达式(阅读:它不会是一个复杂的方法,不像您的 PNG 检查器可能会)。

    【讨论】:

    • +1 - 非常清晰的解释,应该可以帮助他解决未来的问题。
    • 其实和我做的基本一样,反之反而让我更加困惑。
    • 我不确定我理解你的意思。我的回答对你有帮助还是让你困惑?
    • 它实际上让我更加困惑,因为你没有建议任何标准和你的做事方式(将复杂的方法放在一个地方,将更简单的方法放在另一个地方)只是相反我做什么。
    • 它不是将复杂方法与简单方法分开,而是保持关注点的分离。为什么验证器类应该知道如何验证 PNG 图像数据的复杂性?这应该是 Image 对象本身的工作。 Is 中的方法仅作为验证服务的入口点,应该利用它验证的对象中实现的方法来执行实际验证。
    【解决方案2】:

    如果您想尊重 SRP (http://en.wikipedia.org/wiki/Single_responsibility_principle),请做以下小练习:

    选择你的班级并尝试描述它可以做什么/可以做什么。 如果您的描述中有“AND”,则必须将该方法移至其他类。

    见第 36 页:http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf

    其他有助于组织课程的法律(还有更多):得墨忒耳法则 (http://en.wikipedia.org/wiki/Law_of_Demeter)。

    要学到很多东西并帮助您做出正确的选择,我建议您访问 Misko 的博客(谷歌传道者):http://misko.hevery.com

    希望这会有所帮助。

    【讨论】:

    • 我想我已经这样做了......“类 Image 执行几个图像操作和几个图像验证”,我正在努力解决的问题是我在哪里存储验证?在“Image_Validation 扩展图像”类或“Validation_Image 扩展验证”类中?
    • 永远不要忘记:你应该只扩展抽象类。否则,您应该使用“类注入”(参见 Misko 的博尔格)。你的问题,翻译后,是关于类依赖的(同样:Misko 的博客是一个参考)。老实说,如果没有看到你的整个代码,你的约束,就不可能回答你的问题。等等。如果你所有的类都可以通过“NO AND”测试并且你尊重依赖注入的最佳实践,你的类应该很容易在需求发生变化的情况下重构/重组。 => 您选择的“依赖”架构很好。 :)
    【解决方案3】:

    我不认为这是模棱两可的。 “Is”应该在每个示例中都放在首位,我会告诉你原因:“Is”是验证操作的超集,其中 Is::Math 是其中的成员。

    对于 Is::Math,你在做什么?你在做数学运算吗?或者你在验证数学实体?显然是后者,否则它只是“数学”。

    这两个操作中哪一个的范围更大?是?还是数学?显然,因为 Is 在概念上适用于许多非数学实体,而数学是特定于数学的。 (同样,在 Math::Factor 的情况下,它不会是 Factor::Math,因为 Math 是 Factor 所属的超集。)

    这种类型的 OOPing 的全部目的是以一种有意义的方式对事物进行分组。验证函数,即使它们适用于完全不同类型的实体(质数与 PNG 图像),它们彼此之间的相似性也比它们与所比较的事物的相似性要多。它们将返回相同类型的数据,它们在相同的情况下被调用。

    【讨论】:

      【解决方案4】:

      关于处理验证本身的所有内容都适合您的Is-classes:

      • 通过了吗?
      • 哪些部分没有通过?
      • 是否应在某处记录验证错误?

      Zend Framework 中的Zend_Validate 提供了这样一种方法,也许你可以从中得到一些启发。由于这种方法会让您在所有验证类中实现相同的接口,因此您可以轻松

      • 使用相同的语法进行验证,独立于验证哪些数据
      • 通过检查所有名为Is_PrimeIs_Image 的类,而不是到处检查Math_IsImage_Is,轻松识别您可用的验证规则。

      编辑:
      为什么不使用这样的语法:

      class Math {
          public function isPrime() {
              $validation_rule = new Is_Prime();
              return (bool) $validation_rule->validates($this->getValue());
          }
      }
      

      因此也允许

      class Problem {
          public function solveProblem(Math $math) {
              $validation_rule = new Is_Prime();
              if($validation_rule->validates($math->getValue())) {
                  return $this->handlePrime($math);
              } else {
                  return $this->handleNonPrime($math);
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        我认为您所说的问题没有“正确答案”。有些人会将 Prime 放在 Is 中,而有些人会将 Prime 放在 Math 中。有歧义。否则你不会问这个问题。

        现在,您必须以某种方式解决歧义。您可以考虑一些规则和约定,这将说明哪个类/方法在哪里。但这可能很脆弱,因为规则并不总是显而易见的,而且它们可能会变得非常复杂,到那时它们就不再有用了。

        我建议您设计这些类,以便通过查看某些方法的名称可以很明显地看到。

        不要将您的验证包命名为。这个名字太笼统了,几乎所有东西都在那里。 IsFile、IsImage、IsLocked、IsAvailable、IsFull - 听起来不太好,好吗?这种设计没有凝聚力

        最好让验证组件在子系统边界(您必须执行安全和业务规则)过滤数据,仅此而已。

        做出决定后,您的榜样就变得显而易见了。 Prime 属于数学。 Is::Image 可能太笼统了。我更喜欢 Image::IsValid,因为您可能还会有其他方法对图像进行操作(更具凝聚力)。 否则“是”就成了包罗万象的包,正如我在开头所说的那样。

        【讨论】:

        • 我喜欢你的回答,但我实际上有这样的事情:Is::Image($arg), Is::Number($arg) 然后其他一些方法按以下方式组织是::Image::PNG($arg) 和 Is::Number::Float($arg) 和 Is::Number::Integer($arg) 我想的主要方法是 Is::Number::Integer:: Prime($arg) 我觉得这里有凝聚力,我错了吗?
        • @eyze 我希望你同意“Is”是这里所有东西的包。如果验证是您正在做的一切,那么没关系。但是你不应该对数学有任何怀疑(没有它的地方)。如果验证不是您所做的一切,那么我建议您找到更好的架构。你能举个例子,你打算如何使用 Is::Number::Integer::Prime?
        【解决方案6】:

        我认为“is”根本不属于类名。我认为这是针对方法的。

        abstract class Validator {}
        
        class Math_Validator extends Validator
        {
          public static function isPrime( $number )
          {
            // whatever
          }
        }
        
        class I18N_US_Validator extends Validator
        {
          public static function isZipCode( $input )
          {
            // whatever
          }
        }
        
        class Image_Validator extends Validator
        {
          public static function isPng( $path )
          {
            // whatever
          }
        }
        
        Math_Validator::isPrime( 1 );
        I18N_US_Validator::isZipCode( '90210' );
        Image_Validator::isPng( '/path/to/image.png' );
        

        【讨论】:

        • 为什么 I18N_US_Validator 类不扩展 I18N_US?
        • 为什么?好吧,我认为 Satanipuppy 说得最好,“验证函数,即使它们适用于完全不同类型的实体(质数与 PNG 图像),它们之间的相似性也比它们所比较的东西要多。它们会返回相同类型的数据,它们在相同的情况下被调用。”
        【解决方案7】:

        是否有组织课程的圣杯解决方案?也许是不同的范式?

        不,这是基于类的 oop 的一个基本缺陷。这是主观的。

        函数式编程(不要与过程式编程混淆)在这方面的问题较少,主要是因为主要的构建块要小得多。无类 oop 也处理得更好,它是 oop 和函数式编程的混合体。

        【讨论】:

        • 我想知道什么是无类 oop ......也许你在乎,所以给我们举个例子?
        • Javascript、Lua、各种 Scheme 实现——仅举几例——提供了一个基于原型的对象系统。在 Ruby 中,类是可以操作的运行时实体。这些是我称之为无类对象系统的示例。
        【解决方案8】:

        类可以被认为是做事的花哨类型,比如验证自己。

        abstract class ValidatingType 
        {
          protected $val;
          public function __construct($val)
          {
             if(!self::isValid($val))
             {  // complain, perhaps by throwing exception
                throw new Exception("No, you can't do that!");
             }
             $this->val = $val;
        
          }
          abstract static protected function isValid($val);
        }
        

        我们扩展 ValidatingType 来创建一个验证类型。这迫使我们创建一个 isValid 方法。

        class ValidatingNumber extends ValidatingType
        {
           ...
           static protected function isValid($val)
           {
              return is_numeric($val);
           }
        }
        
        class ValidatingPrimeNumber extends ValidatingNumber
        {
           /*
            * If your PHP doesn't have late-binding statics, then don't make the abstract 
            * or overridden methods isValid() static.
            */
           static protected function isValid($val)
           {
              return parent::isValid($val) 
                     or self::isPrime($val); // defined separately
           }
        }
        
        class ValidatingImage extends ValidatingType
        {
           ...
           static protected function isValid($val)
           {
              // figure it out, return boolean
           }
        }
        

        这种方法的一个优点是您可以继续创建新的验证类型,并且不会得到一个膨胀的 Is 类。

        这种方法有更优雅的变体。这是一个简单的变化。语法可能需要清理。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-05
          • 1970-01-01
          • 2018-04-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多