变量复用
变量复用,适用于以下场景:1,整个项目公用的部分(比如errors);2,一组类要通信,或者同一个类的两个函数之间要通信,通信数据可以用类来定义和约束;
场景1
所有错误相关的部分要放在一个类里面,方便查找和使用;
<?php class errors {const ERROR_PARAMS = 'params error';const ERROR_EMPTY_PARAM = 'empty param'; }class TestModel {public function run(){ $error = errors::ERROR_PARAMS;var_dump($error);return $error;} }$model = new TestModel(); $model->run();
解释,errors是整个项目都可以访问的,可以添加更多的错误消息在里面。
场景2:
同一个类的两个函数中间需要通信,我们需要对这种通信定义一下数据结构。
<?php class DataMeta {public $mobile = '';public $message = ''; }class Sms {public function send($mobile, $message){ $data = new DataMeta();$data->mobile = $mobile;$data->message = $message;if ($this->checkValid($data)) {echo "Allow to send\n";} else {echo "Not allowed to send\n";} } public function checkValid(DataMeta $data){ if ($data->mobile && $data->message) {return true;} else {return false;}} }$model = new Sms(); $model->send(0, ''); $model->send(12345678910, 'my test');
checkValid函数对参数进行了校验,我们使用了一个用于通信的数据类型DataMeta,里面包含了我们所需要的结构化数据。显示定义DataMeta,是为了更好地理解。
当然使用DataMeta这个类型是有些弊端的,如果这个数据结构有很大的变动(比如字段名相同,但实际的含义已经变化了),那么用数组和注释可能比DataMeta这样的约束要更好一些。
总结下:
情况1,有一组数据,需要在函数间传递,并且结构不会有变化的;办法是:可以定义一个MetaClass来约束这组数据。
情况2,有一组数据,需要在函数间传递,但是结构会经常性变化,则不能使用MetaClass来约束;办法是:用数组和注释来说明;特别地,Stdclass也适合这个场景;
情况3,有一组数据,需要在函数间传递,结构上只会新增字段,原始字段含义保持;办法是:依然可以用MetaClass来约束。
以上解释了PHP之间传递某个有结构约束的变量,可以有的方法。
除此之外,我们希望关心,有些类是完成类似的功能,也遵循一些共同的动作,这些类的函数名是一致的。
函数约束
函数约束的解决方案:1,基类派生类;2,接口interface。
如何区分哪种场景下使用基类派生类,哪种场景下使用interface。
我个人看法是:
1,如果能用接口实现最好优先使用接口,继承尽量不要使用;
2,接口只定义约束,并不包含实现,所以各个类动作很不相同,只用接口来约束需要实现的函数即可(大多数的业务代码就是很不同,用接口比较ok);
3,如果基类已经有完整的功能,派生类需要自己个性化完成的动作并不太多,使用基类继承类。
代码片段1:
实现interface时,未实现interface的函数,会报错。所以使用interface时,定义的函数是必须实现的。
<?php interface SmsInterface {public function sendSms($abc); }class m1 implements SmsInterface {public function sendSms($aaa = 'cc'){var_dump(__CLASS__);} }class m2 implements SmsInterface {public function __construct(){var_dump(__CLASS__);} }$a = new m1(); $a->sendSms(); $b = new m2();
执行时,报错:Fatal error: Class m2 contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (SmsInterface::sendSms) in
代码片段2:
interface定义的函数不携带参数。
<?php interface SmsInterface { public function sendSms(); } class m1 implements SmsInterface {public function sendSms(){var_dump(__CLASS__);} } class m2 implements SmsInterface { public function __construct(){var_dump(__CLASS__);}public function sendSms(){var_dump(__CLASS__);} }$a = new m1(); $a->sendSms();$b = new m2(); $b->sendSms();
这段代码,没问题,可以正常执行。
代码片段3:
如果interface的函数是携带参数的,那么实现类必须是和interface的函数原型保持一致,也需要携带参数才可以。
<?php interface SmsInterface {public function sendSms($mobile, $message); }class m1 implements SmsInterface {public function sendSms(){var_dump(__CLASS__);} }$a = new m1(); $a->sendSms();
执行时候报错:Fatal error: Declaration of m1::sendSms() must be compatible with SmsInterface::sendSms($mobile, $message)
代码片段4:
<?php interface SmsInterface {public function sendSms($mobile, $message); }class m1 implements SmsInterface {public function sendSms($mobile, $message){var_dump(__CLASS__);} }$a = new m1(); $a->sendSms(123, 'message');
这段代码没问题,可正常执行。
代码片段5:
不再详细展开,有兴趣的自己动手实践下之后的结论。
关于默认参数:
a,如果interface的函数有默认参数,实现类也必须实现自己的函数,所以interface的默认参数并不起作用;
b,实现类如果有默认参数,调用时候走默认参数逻辑没问题;
关于参数列表不一致:
如果实现类的参数列表比interface定义的要少,是会报错的;
如果实现类的参数列表比interface定义的要多,语法上不会报错,也可以使用,另外的解决方案是setField()这样的set函数;