假设我的项目中有以下类:
- 类是 // 验证类
- class 数学 // 数字操纵类
现在,如果我想验证给定数字的素数,那么插入我的Prime()方法的逻辑位置是什么?我可以想到以下几个选项:
- 的 Is_Math ::素()
- 的 Math_Is ::素()
我讨厌这些含糊不清的东西,这会减慢我的思维过程并经常让我误入歧途。还有一些例子:
- 是:: Image()或Image :: Is()?
- Is_Image :: PNG()或Image_Is :: PNG()?
- Is_i18n_US :: ZipCode()或i18n_Is_US :: ZipCode()或 i18n_US_Is :: ZipCode()?
在Image示例中,第一选择对我来说更有意义,而在i18n示例中,我更喜欢最后一个。没有标准让我觉得整个代码库都很混乱。
组织课程是否有圣杯解决方案?也许是一个不同的范例?
答案 0 :(得分:10)
对于Math示例,我将实际检查Math
类中的数字是否为素数。在Is
类中,您将放置一个在需要进行验证时调用的方法。然后,您将从那里使用Math::Prime()
。
使用Image
,这是一种类型检查。除非您确保上传了有效的图像数据,否则您可能不需要为它制作方法。
使用PNG
方法,与Math
相同。将实际的PNG数据检查器算法放在Image
中,并在Is
中调用验证器方法。
邮政编码示例应该只在你的Is
类中,因为它在字符串原语上运行,可能只使用正则表达式(读取:它不是一个复杂的方法,不像你的PNG检查器可能将是)。
答案 1 :(得分:4)
如果你想尊重SRP(http://en.wikipedia.org/wiki/Single_responsibility_principle),那就去做一点:
选择你的课程并尝试描述它的作用/可以做什么。 如果您的说明中包含“与”,则必须将该方法移至其他类。
见第36页:http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf
其他法律(还有更多)将帮助您组织课程:得墨忒耳法律(http://en.wikipedia.org/wiki/Law_of_Demeter)。
为了学到很多并帮助你做出正确的选择,我建议你Misko的博客(谷歌传教士):http://misko.hevery.com
希望这有帮助。
答案 2 :(得分:3)
处理验证本身的所有内容都适合您的Is
- 类:
Zend_Validate提供了这样一种方法,也许你可以从中获得一些灵感。由于这种方法可以让您在所有验证类中实现相同的接口,因此您可以轻松地
Is_Prime
,Is_Image
的所有类,而不是在整个地方检查Math_Is
,Image_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);
}
}
}
答案 3 :(得分:3)
我认为它根本不含糊。 “是”应该是每个例子中的第一个,我会告诉你原因:“是”是验证操作的超集,其中Is :: Math是其成员。
在Is :: Math的情况下,你在做什么?你在做数学运算吗?或者您正在验证数学实体?后者,显然,否则它只是“数学”。
这两项行动中哪一项的范围更广?是什么?还是数学?很明显,因为Is在概念上适用于许多非数学实体,而数学是数学特定的。 (同样在Math :: Factor的情况下,它不会是Factor :: Math,因为Math是Factor所属的超集。)
这种OOPing的全部目的是以有意义的方式对事物进行分组。验证函数,即使它们适用于截然不同类型的实体(素数与PNG图像),它们之间的相似性也比它们所比较的事物更相似。它们将返回相同类型的数据,它们在相同的情况下被调用。
答案 4 :(得分:1)
我认为你所说的问题没有“正确答案”。有些人会把Prime放在Is中,有些人会放在Math中。有歧义。否则你不会问这个问题。
现在,你必须以某种方式解决歧义。你可以考虑一些规则和约定,即哪个类/方法去哪里。但这可能是脆弱的,因为规则并不总是很明显,而且它们可能会变得非常复杂,并且在那时它们不再有用。
我建议您设计这些类,以便通过查看某些方法应该去的名称来显而易见。
不要将您的验证包命名为。它是如此通用的名称,几乎一切都在那里。 IsFile,IsImage,IsLocked,IsAvailable,IsFull - 听起来不太好,好吗?该设计没有凝聚力。
最好让验证组件在子系统边界(必须强制执行安全和业务规则)过滤数据,而不是其他任何内容。
做出决定后,你的榜样变得明显。 Prime属于数学。是::图像可能太笼统了。我更喜欢Image :: IsValid,因为你可能还有其他方法在图像上运行(更多的凝聚力)。 否则正如我在开头所说的那样,“Is”会成为所有内容的包包。
答案 5 :(得分:1)
我认为“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' );
答案 6 :(得分:0)
组织课程是否有圣杯解决方案?也许是一个不同的范例?
不,这是基于类的oop的基本缺陷。这是主观的。
函数式编程(不要与过程式编程混淆)这个问题的问题较少,主要是因为主要构建块要小得多。无类别的oop也更好,是oop和各种函数式编程的混合。
答案 7 :(得分:0)
类可以被认为是做事物的花哨类型,比如验证自己。
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类。
这种方法有更多优雅的变化。这是一个简单的变化。语法可能需要清理。