原文地址: 【技术文章】《记一次意外的技术讨论收获—策略模式》
本文地址:https://www.cnblogs.com/aiweixiao/p/9460933.html
扫描关注公众号
最近公司有和第三方合作的项目,于是想到了使用策略模式去实现,到时候有别的第三方来走他自己的策略去完成相关的业务流程就行了。
gitHub地址: http://t.cn/RDO9luX
1)类加载的问题--命名空间
但是我们在做的过程中遇到的第一个问题就是命名空间的问题,因为公司老的框架是不支持也没有使用命名空间的,我仿照之前的一个业务实现的策略,在一个php文件中有多个类文件,但是使用了php的命名空间之后,外部就调用不到这些类了,否则是多个类,命名多个命名空间,但是因为新框架使用的自动加载是默认文件名和类名是一致的,否则也是不好加载进来。于是想到了拆分类,将基类BaceStrategyClass,BaceCooperationClass,接口Interface,和工厂类ClassFactory放到了一个文件ClassFactory.php中,将与第三方进行连接交互的类Cooperation放到了一个文件xxxCooperatioin.php中,还有一个这次合作的策略类XXXStrategy 放到了一个文件XXXStrategy.php中,这样就解决了类加载的问题。
1 php 2 //测试脚本 3 include_once("init.php"); 4 use \Strategy\FacultyClass; 5 6 7 //1.工厂类是唯一入口,构造方法返回的是类自己实例 8 $res = (new FacultyClass('XXX'))->strategyName->testStrategy('Hello', 'World');//echo Hello World 9 10 //2.走单例模式 11 //$res = FacultyClass::getStrategyInstance('XXX')->testStrategy('Hello', 'World');//echo Hello World 12 13 //3.直接访问 XXXStrategy 不行,错误是 Class 'Strategy\BaseStrategyClass' not found 14 //$res = new XXXStrategy();
2)公司内部外系统的访问—层级调用
我们遇到了第二个问题就是我们这个系统需要公司其他系统,比如用户系统的数据,但是新框架定义与公司外系统的交互,都在service层完成,领域层domian中只完成与自己系统业务相关的内容,所以为了符合层级调用和符合规范,就将这些domian中的策略整体进行了迁移,重新定义了命名空间,也修改了相关地方的调用的命名空间类use引用。
1 php 2 namespace Strategy; 3 4 //工厂类 5 class FacultyClass 6 {/*{{{*/ 7 public $strategyName; 8 public function __construct($strategyName) 9 { 10 $strategyClass = 'Strategy\\'.$strategyName.'Strategy'; 11 $this->strategyName = new $strategyClass; 12 return $this->strategyName;//外部接收不到这个,还是工厂类自己的实例 13 } 14 15 public static function getStrategyInstance($strategyName) 16 { 17 static $ins; 18 $strategyClass = 'Strategy\\'.$strategyName.'Strategy'; 19 //单例模式 20 if(false == $ins instanceOf $strategyClass) 21 { 22 $ins = new $strategyClass; 23 } 24 25 return $ins; 26 } 27 }/*}}}*/ 28 29 //策略基类 30 class BaseStrategyClass 31 { 32 33 } 34 35 //第三方交互基类 36 class BaseCooperationClass 37 { 38 39 } 40 41 42 //策略接口 43 interface IStrategy 44 { 45 public function testStrategy($argsOne, $argsTwo); 46 }
3)必须走工厂方法—唯一入口,封装性
然后我们遇到的第三个问题是,之前都是通过工厂方法的构造方法,将策略类的实例作为其属性,使用 (new classFaculty(‘xxx’))->strategyClass 得到策略实例,然后这个策略实例里,可以调用 xxxCooperatioin类中的相关方法,这个是可以走通的,但是如果我只是想测试 xxxCooperatioin类中的方法,就不行,包括测试xxxStrategy类中的方法都是不行的,都分别报 不存在其 基类 BaceCooperationClass 和BaceStrategyClass不存在,这个就比较尴尬了,百思不得其解,我和潇同学“抓耳挠腮”,再仔细看看 三个文件 ClassFactory.php , xxxCooperatioin.php 和 XXXStrategy.php, 终于发现原来走工厂那种访问的时候,ClassFactory.php 中已经包含了 xxxCooperatioin 类和xxxStrategy类所需要的基类了,所以一路走下来,是可以加载各个类的,但是如果是 直接外部测试脚本调用 xxxCooperatioin类 和xxxStrategy类是不行的,因为他们的基类在 ClassFactory.php中,而你又无法使用命名空间将其加载进来(因为BaceCooperationClass 和BaceStrategyClass 和文件ClassFactory.php的文件名不一样,无法加载),这样就强制外部必须走工厂ClassFactory类 来访问到 xxxCooperatioin类,和xxxStrategy类,这样就提高了安全性,实现了封装性。
1 php 2 namespace Strategy; 3 4 5 //XXXStrategy 具体策略类 6 class XXXStrategy extends BaseStrategyClass implements IStrategy 7 {/*{{{*/ 8 public $cooperation; 9 10 public function __construct() 11 { 12 $this->cooperation = new XXXCooperation(); 13 } 14 15 //实现策略 16 public function testStrategy($argsOne, $argsTwo) 17 { 18 echo $argsOne." ".$argsTwo; 19 } 20 }/*}}}*/
4)构造方法返回的永远是自己—走单例模式吧
还有就是看走工厂方法 (new classFaculty(‘xxx’))->strategyClass 得到策略实例,实际上我们直接new classFaculty类的时候,在构造中直接return strategyClass 不就直接得到这个策略类示例了吗,不需要在多一层属性的指向调用,后来发现我们在构造里error_log输出的好好的,strategyClass 就是xxxStrategy的示例,但是走完工厂的构造方法返回的还是 工厂 classFaculty的实例,后来仔细一思考,才发现其实 构造方法永远返回的是类自己的实例,而不管你在 构造方法里return 返回的是什么。
1 php 2 namespace Strategy; 3 4 //XXXCooperation 具体第三方链接类 5 class XXXCooperation extends BaseCooperationClass 6 {/*{{{*/ 7 8 }/*}}}*/
我们实际上也只是想走单例模式,不用重复实例化类,没想到发现构造中返回不了策略类的实例,于是在工厂类classFaculty中写了一个 getStrategyInstance的一个静态方法,在那里实现单例模式,并返回了这策略类的实例。惠同学说还有就是这个策略类的实例的一个属性cooperation就是 xxxCooperatioin类的一个实例。这样就能拿到 策略类在脚本里做一些测试,也能拿到xxxCooperatioin类的一些方法做测试,避免直接 去实例化这两个类时,报他们的基类不存在的尴尬问题。
1 php 2 3 spl_autoload_register('autoInclude'); 4 //自动加载 5 function autoInclude($className) 6 { 7 if(false == class_exists($className)) 8 { 9 $arr = explode('\\', $className); 10 $fileName = end($arr); 11 include_once($fileName.'.php'); 12 } 13 }
至此,我们的这一次的技术讨论完美结束了,潇同学和惠同学和我都露出了欣慰的笑容,虽然已经是晚上11点多了,但是能有这样自发的深入的细致的卓有成效的讨论还是很值得的,哪怕将来我们各奔东西了,但是一想到那天晚上我们的这次有意义的,精彩的技术探究之旅,便会觉得格外亲切,技术在我们看来也变得熠熠生辉起来。
虽然在大神或者外人看来我们的这次讨论一般般或者是我们应该早掌握的东西,但是毕竟我们整日在位实现业务需求去并行着,加班加点完成业务的需求,鲜有时间仔细认真去思考这技术其中的细节,也就无法体会到思考技术,形成“心流”,最终解决问题,有所收获和提高的那种乐趣了,如果我们能每天或者每周多一些这样的讨论和收获,那我们的技术水平的提高就指日可待了。
可以,可以,一次偶然的讨论竟然也成为了彼此心中探究钻研技术的起点,不错啊。
作文纪念之。
2018.08.11 周六
于北京西直门
扫描关注微信公众号