变量复用
变量复用,适用于以下场景: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 "; } else { echo "Not allowed to send "; } } 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函数;