问题
MVC架构中的model层主要负责和数据直接交互,如果没有使用ORM框架,那么很多原子操作(CRUD等)需要自己实现。一般来说一个系统会有多个Model,每一个Model都需要重复实现这些原子操作,而这些代码往往除了表名外都是一样的:
class User { public static function findById ( $id ) { return DBProxy::getDb( 'user' )->findOne( array( '_id' => new MongoId( $id ) ) ); } } class Video { public static function findById ( $id ) { return DBProxy::getDb( 'video' )->findOne( array( '_id' => new MongoId( $id ) ) ); } }
当我们增加一个Model类的时候,比如Article,就要从其他类复制这些原子操作(比如find、findOne、findById、update、save、count等等)过来。如果有一个方法需要变更,比如增加一个参数或者添加log日志,就得修改所有Model类中的同名方法。这实在是一件很痛苦很无奈的事情。
《重构》一书告诉我们,重复的代码是坏味道,那么我们该怎么改良呢?
PHP5.2下的解决方案
首先想到的方案是,让所有Model类都继承一个父类,把重复的方法都提到父类中让子类自动继承,在方法的实现中引用子类中声明的表名。
class Model { public static function findById ( $id ) {} } class User extends Model { public static $collection = 'user'; } class Video extends Model { public static $collection = 'video'; }
但是经过试验发现,调用继承自父类的静态方法时,无法取得子类名以及子类中的属性。
1、调用静态方法时没有Object上下文,$this不能使用
2、父类方法中使用self::$var取得的是父类的属性而不是子类的属性
3、父类方法中使用__CLASS__获取到的是父类而不是子类的名称
结果只能用很别扭的方式来实现:
class Model { public static function findById ( $collection, $id ) {} return DBProxy::getDb( $collection )->findOne( array( '_id' => new MongoId( $id ) ) ); } class User extends Model { public static $collection = 'user'; public static function findById ( $id ) { parent::findById( self::$collection, $id ); } } class Video extends Model { public static $collection = 'video'; public static function findById ( $id ) { parent::findById( self::$collection, $id ); } }
这样虽然把实现的细节提炼到了父类,但每个子类还需要对每个方法进行声明。可维护性是有所提高,但每个子类中仍然存在重复的代码。有没有更好的解决方案呢?
PHP5.3下的解决方案
PHP5.3提供了一个新特性“后期静态绑定”,可以在调用继承自父类的静态方法时,通过static::$var的形式取得子类的属性,或者通过static::method()的形式调用子类的放非啊。这下问题就得到了完美的解决:
class Model { public static function findById ( $id ) { return DBProxy::getDb( static::$collection )->findOne( array( '_id' => new MongoId( $id ) ) ); } } class User extends Model { public static $collection = 'user'; } class Video extends Model { public static $collection = 'video'; }
除了一行对表名的声明外,完全没有重复的代码,这就是我想要的理想结果,目前准备在项目中使用。如果你连表名都懒得改,还可以这样:
class User extends Model { public static $collection = __CLASS__; }
PHP5.4下的解决方案
PHP5.4增加的特性traits可以将一些方法提炼出来,在需要的类中用use来引用。相当于可以在class的定义中include了(你别说,天真的我还真试过)。
trait CRUD { public static function findById ( $id ) {} return DBProxy::getDb( self::$collection )->findOne( array( '_id' => new MongoId( $id ) ) ); } } class User { public static $collection = 'user'; use CRUD; }
这个方案的好处是不用引入父类和继承,而且可以更加灵活,一个方法可以在不同的类中引用,而无需继承特定的类。问题是5.4作为新版本,在生产环境中部署还不是很放心。
赞,看来新版扫雷网要用php mongodb了?
ps:这个blog在google reader下标题显示不了,正文无换行。
我设置了全文输出,你再试试看应该可以换行了。标题显示不了就不知道了,我这里订阅自己是正常的。