1、PHP设计模式及在框架设计中的经典应用,目标,了解设计模式与框架 给出一个可行的学习设计模式的方法 介绍几种常见的设计模式,Agenda,设计模式简述 框架简述 设计模式与框架 软件开发演进过程 框架要解决的问题 Factory Singleton Register Adapter Proxy Active Record MVC,什么是模式?,demo for(int i=0;i100;i+) 主谓宾 我爱你 计算机领域:在特定场景下、解决某一类问题的通用方法 模式分类(参见:POSA-1) 架构模式 设计模式 惯用法,为什么要关注设计模式?,向专家学习 向历史学习 大型软件设计、实施必备 沟
2、通交流的语言 薪水+,模式四要素,模式名称(pattern name) 交流、标记 问题(problem) 场景、上下文 解决方案(solution) 解决方法、方案 效果(consequences) 模式应用的效果及使用模式应权衡的问题,框架是什么?,可复用的面向“对象“软件系统 应用程序工具箱 api,函数 框架(Framework) 框架 解决特定领域里面具有类似问题的一组相互协作的类 提供解决常见问题的通用组件 为了“复用”已有的解决方案 为了生产效率和可维护性,软件开发演进过程,机器语言、汇编语言 高级语言,面向过程编程 OOP、设计模式、元编程 框架、DSL 智能组件? 只描述需求
3、、软件就给您实现了?:) 程序员担当“智能”,设计自己的框架可能面临的问题,创建大量的复杂对象 比较耗资源的对象只希望初始化一次 动态处理大量的配置 适应不同的数据库 希望延迟初始化数据库连接 日志处理 性能监控 .,Warning:接下来的代码供示例使用,不具备产品级可用性,场景一,需要在很多函数中调用数据库的操作 假设对数据库的操作已经封装在一个类里面 实现 v0 可以在函数里面调用该类 function foo() $db = new Driver_DB_Mysql(); function bar() $db = new Driver_DB_Mysql(); 问题 如果Driver_DB
4、_Mysql(改名了呢?参数变化了呢?,模式一:Factory,工厂模式V0.1 Impl class DB_Factory public static function getInstance() $db =new Driver_DB_Mysql()() return $db; 使用 function foo() $db = DB_Factory:getInstance() 使用一致的、简单的方式来初始化复杂的对象,场景二,在一个业务流程中要访问数据库资源,发现多次连接数据库,消耗资源。希望只连接接数据库一次 实现v0 全局变量中初始化连接 $db = Db_Factory:getInsta
5、nce() 业务访问全局变量 function foo() global $db; 问题 bad smells ?,模式二:singleton,单例模式+工厂模式 Impl class DB_Factory private static $_db;private function _clone() ;private function _construct()public static function getInstance() if(! (self:$_db instanceof Driver_DB_Mysql) self:$_db = new Driver_DB_Mysql();retur
6、n self:$_db; 使用 function foo() $db = DB_Factory:getInstance(); 实例化一个对象、共享连接,场景三,框架中需要保存一些全局的设置或类的实例,保存框架上下文 实现v0 使用系统$GLOBALS $GLOBALSdebug = xxx; 使用 function foo() if($GLOBALSdebug) . 问题 bad smells 违反职责SRP,模式三:Registry,Registry+Factory Impl class Registry private static $_instance; / private static
7、 $_data = array(); private $_data = array(); /感谢一位同学指出 。用实例变量还是静态变量,依赖于整体的设计。需要对应修改set,get方法.public static function getInstance() return self:$_instance;.public function set($key, $value) $this-_data$key = $value;public function get($key) return isset($this-_data$key) ? $this-_data$key : null; 使用 $c
8、tx = Reistry:getInstance(); $ctx-set(xxxx,oooo) $value = $ctx-get(xxxx“) 使用单实例的全局对象来代替全局的变量,模式三:Registry + Factory(续),保存读写分离的两个mysql连接 保存 $ctx = Registry:getInstance(); $ctx-set(db_read)=Db_Factory:getInstance(db_read) $ctx -set(db_write)=Db_Factory:getInstance(db_read)使用 $dbReadObj = $ctx-get(db_re
9、ad) $dbWriteObj = $ctx-get(db_write),场景四,需要同时支持mysql和postgresql连接 实现 针对每个数据库,单独写一个驱动实例 Db_Factory:getMysqlDB() Db_Factory:getPostgresqlDB() 问题 如何保持不同DB接口的一致性?,模式四:Adapter Pattern,定义接口 interface IDB public function connect();public function error();public function errno();public static function escap
10、e_string($string);public function query($query);public function fetchArray($result);public function fetchRow($result);public function fetchAssoc($result);public function fetchObject($result);public function numRows($result);public function close();/接口定义供示例用 ,模式四:Adapter Pattern(续),针对不同的DB实现 Driver_D
11、B_Mysql implements IDB private $link; public function connect($server=, $username=, $password=, $new_link=true, $client_flags=0)$this-link = mysql_connect($server, $username, $password, $new_link, $client_flags); public function errno()return mysql_errno($this-link); / ,模式四:Adapter Pattern(续),Adpate
12、r+Factory 实现 DB_Factory public static function getInstance($adapter=Mysql) /参数化工厂if(!isset(self:$_db$adapter) | ($_db$adapter instanceof IDB) if (include_once Drivers/DB/ . $type . .php) $classname = Driver_DB_ .ucfirst( $type);self:$_db$adapter = new $classname; else throw new Exception(Driver not
13、found);return self:$_db$adapter; 使用 DB_Factory:getInstance(mysql),场景五,需要延迟初始化一些资源 class UserController private $db; /构造函数中初始化资源 function _construct() $this-db = DB_Factory:getInstance(mysql) public function manages() $sql = “select * from managers“; $users = $tthis-db-getResults($sql); public functi
14、on foo() echo “haha“ 问题 foo()方法不需要DB连接,在构造函数里面去连接数据库是不是额外消耗连接资源?,模式五:Proxy,代理模式v1 实现共同的接口 class Proxy_DB_Mysql implements IDB private $db;public function query($query) if(!$this-db) $this-db= DB_Factory:getInstance(mysql) return $this-db-query($query); 延迟初始化、把耗费资源的操作延迟到必须的时候 问题 每个方法都需重写一次?,Proxy,代理模
15、式v2 使用magic方法 class Proxy_DB_Mysql private $mysql;public function _call($method,$args) if(!$this-mysql) $this-mysql = DB_Factory:getInstance(mysql) retrun call_user_func_array(array($this-mysql,$method),$args); 使用:function foo() $this-proxy-query($sql);v1 和v2这两种方式哪一个更好呢?,场景六,每次我们都需要原生的sql ? function
16、 foo() $sql = “select * from users“; 我希望OO,不要有那么多SQL 我不懂SQL how to ? nosql:),模式六:Active Record,使用示例 $User = new User(); $User-name = hanyh; $User-address = 北京; $User-save(); or $data = array(name=hanyh,address=beijing); $User-save($data);,Active Record,实现 class User extend ActiveBase CONST INSERT_SQ
17、L = “insert into user(name,address) values(?,?)“;private $data;protected $fields = array(name,address);public function save($data=array() if(empty($data) $this-db-save(SELF:INSERT_SQL,$this-data); else $this-db-save(SELF:INSERT_SQL,$data);private function _set($field,$value) if(in_array($field,$this
18、-fields) $this-data$field = $value; else throw new Exception(“$field does not exist“); 基类负责数据库连接、参数合法性校验等等,MVC模式,架构模式中的一种 属于交互系统 场景 用户界面会变换 界面的变换不影响核心功能性代码 有一致的方式来区分并组织好存储、业务、显示相关代码,MVC,MVC框架诞生的11个步骤,区分核心功能和用户交互 Model 实现变化通知功能,需要吗? 参考Observer模式 设计和实现view 设计和实现Controller 响应用户的输入、事件 把View和Controller关联起来(怎么相互调用?) MVC框架的初始化 动态创建View ? 动态可扩展的插件系统 controller不绑定到特定的view ? 复用一些基础库,建立层次性的继承体系? 与当前业务解耦合,变成一个通用的框架? 推广 好酒也怕巷子深,抽象,学习方法,读好书 站在巨人肩膀上 Pattern-Oriented Software Architecture(5本) 聆听大师的教诲 多实践 纸上得来终觉浅,绝知此事要躬行 做一个micro framework 多交流 碰撞,谢谢大家!Q&A,