PHP设计模式之:观察者模式学习笔记
一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力。当对象们连接在一起时,它们就可以相互提供服务和信息。这个模式对于大型系统项目来说应该是挺挺有用的,通俗的讲,这种模式允许某个类去观察另一个类。当一个类被改变时,观察类就会收到通知并且做出相应的动作。
在平时的项目中还是挺有用的,比如一个用户下了一笔订单,下单成功后,就需要去发送短信/邮件的通知,库存的修改,账户余额的修改等等很多操作。
在之后的PHP5.0起,内置的SPL标准库中就提供了这种设计模式接口供大家使用,接下了就通过实例来学习一下。
SPL 提供了 SplSubject 和 SplObserver 接口。
SplSubject 接口提供了attach()、detach()、notify() 三个方法。而 SplObserver 接口则提供了 update()方法。
<?php /** * 这一模式的概念是SplSubject类维护了一个特定状态,当这个状态发生变化时,它就会调用notify()方法。 * 调用notify()方法时,所有之前使用attach()方法注册的SplObserver实例的update方法都会被调用。 * */ interface SplSubject{ public function attach(SplObserver $observer);//注册观察者 public function detach(SplObserver $observer);//释放观察者 public function notify();//通知所有注册的观察者 } interface SplObserver{ public function update(SplSubject $subject);//观察者进行更新状态 } ?>
使用所提供的接口,来实现观察者模式
<?php /** *具体目标 */ class Salary implements SplSubject { private $observers, $money; public function __construct() { $this->observers = array(); } public function attach(SplObserver $observer) { //注册观察者 $this->observers[] = $observer; } public function detach(SplObserver $observer) { //释放观察者 if($idx = array_search($observer,$this->observers,true)) { unset($this->observers[$idx]); } } public function notify() { //通知所有观察者 foreach($this->observers as $observer) { $observer->update($this); } } public function payoff($money) { //发工资方法 $this->money = $money; $this->notify(); //通知观察者 } } /** * 具体观察者 */ class Programmer1 implements SplObserver { public function update(SplSubject $subject) { echo 'Programmer1 发工资了!<br/>'; } } class Programmer2 implements SplObserver { public function update(SplSubject $subject) { echo 'Programmer2 也发工资了!<br/>'; } } $subject = new Salary(); $observer1 = new Programmer1(); $observer2 = new Programmer2(); //注册观察者 $subject->attach($observer1); $subject->attach($observer2); //发工资操作,发起通知 $subject->payoff('20K'); ?>
通过Observer模式,把一对多对象之间的通知依赖关系的变得更为松散,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。东西是不错,如何能够更好的去使用它,仍需要多加实践、联系。
php中的设计模式中有很多的各种模式了,在这里我们来为各位介绍一个不常用的数据映射模式吧,希望文章能够帮助到各位。
之前的几种设计模式,都是大大提高了PHP代码的可读性、可维护性。但是,在WEB应用中还有更重要的需求与挑战,那就是:数据库应用。可之前的设计模式,都没有涉及于此。今天写到的,数据映射模式就是能够更好的组织应用程序与数据库进行交互。
博主这两天也是花了点时间对,这种模式有了那么一点的了解。斗胆在这个里献丑,按照自己的理解,写一点东西与大家分享,互相学习。
当然说到数据映射模式,就不得不提到对象关系映射(Object Relational Mapping,简称ORM),用于实现面向对象编程语言里不同类型系统的数据之间的转换。一般ORM框架对付简单的应用系统来说都能满足基本需求,可以大大降低开发难度,提高开发效率,但是它在SQL优化方面,肯定是比纯SQL语言要差很多,对复杂关联、SQL内嵌表达式的处理都不是很理想。
对于博主目前使用的TP框架,其核心文件Model.class.php就是实现了ORM和ActiveRecords模式,在项目中所有的模型也都是继承这个模型类。
好吧,还是不丢人说这些废话了,自己参考编写整理了一份实例,给大家分享一下,互相交流。
首先我们需要一个数据库中间层实现类,使用pdo进行数据库访问。当然这个类不是今天的重点,我也是从网上拷来的,可以直接忽略。
创建一个DB类文件 Db.class.php
<?php /* * 数据库中间层实现类 */ class Db { public static $db = null; private $_dbh = null; public static function getInstance() { if( self::$db == null ){ self::$db = new self(BACKEND_DBHOST ,BACKEND_DBUSER ,BACKEND_DBPW ,BACKEND_DBNAME); } return self::$db; } private function __construct( $host ,$user ,$pass ,$dbname ){ try { $this->_dbh = new PDO('mysql:dbname='.$dbname.';host='.$host,$user,$pass); $this->_dbh->query('SET NAMES '. BACKEND_DBCHARSET); $this->_dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); $this->_dbh->setAttribute(PDO::ATTR_ERRMODE, true); } catch (PDOException $e) { throw new Exception('Can not connect db'); } } public function getOne($sql){ try { $rs = $this->_dbh->query($sql); $result = $rs->fetch(PDO::FETCH_ASSOC); if(!empty($result)) { return $result; } } catch (PDOException $e) { throw new Exception($this->_dbh->errorInfo()); } return false; } public function getAll($sql){ try { $rs = $this->_dbh->query($sql); $result = $rs->fetchAll(PDO::FETCH_ASSOC); if(!empty($result)) { return $result; } } catch (PDOException $e) { throw new Exception($this->_dbh->errorInfo()); } return false; } public function exec($sql){ try { $exec = $this->_dbh->exec($sql); } catch (PDOException $e){ throw new Exception($this->_dbh->errorInfo()); } return $exec; } public function getLastId() { return $this->_dbh->lastInsertId(); } } ?>
数据映射类 Table.class.php
<?php /** * 数据映射类 * 部分代码来源TP框架 * 使用相关魔术方法 则映射的表修改字段后无需修改属性值 */ class Table{ // 数据信息 protected $data = array(); // 数据信息 protected $db = null; // 表信息 protected $tableName = ''; public function __construct() { $this->db = Db::getInstance(); } /** * 设置数据对象的值 */ public function __set($name,$value) { // 设置数据对象属性 $this->data[$name] = $value; } /** * 获取数据对象的值 */ public function __get($name) { return isset($this->data[$name])?$this->data[$name]:null; } /* * 添加 * 修改、删除也和添加类似,就不一一列举了 */ public function add() { $data = $this->data; foreach($data as $k=>$v) { $fieldArr[] = $k; $valueArr[] = "'".$v."'"; } $fields = implode(',', $fieldArr); $values = implode(',', $valueArr); $sql = 'INSERT INTO '.$this->tableName.' ('.$fields.') VALUES ('.$values.')'; $result = $this->db->exec($sql); if($result) { return $this->db->getLastId(); } else { return false; } } } ?>
表对应的类文件 UserTable.class.php
<?php /** * 数据映射到表 * 一般根据表的结构由工具自动生成,比如Yii框架等。 */ class UserTable extends Table { protected $tableName = 'user'; } ?> 使用方式 index.php <?php /** * 数据库配置文件 */ define('BACKEND_DBHOST', 'localhost'); define('BACKEND_DBUSER', 'root'); define('BACKEND_DBPW', ''); define('BACKEND_DBNAME', 'test'); define('BACKEND_DBCHARSET', 'utf-8'); /* * 这里实例化对象时可以使用之前介绍的工厂模式和注册模式,来实例化和管理实例化对象 * TP框架中的D方法就是做了这部分工作 */ $UserTable = new UserTable(); $UserTable->username = 'Anrai'; $UserTable->mobile = '123456789'; $UserTable->email = 'huanglei.web@gmail.com'; echo $UserTable->add(); /* 数据表sql CREATE TABLE `user` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(30) NOT NULL, `mobile` varchar(11) NOT NULL DEFAULT '0', `email` varchar(60) NOT NULL DEFAULT '0', PRIMARY KEY (`uid`), KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 */ ?>原型模式和其它的模式相对有一些不同之处了,这个也是当然了所有的设计模式中的各种模式都有自己的特色了,下面一起来看看。
原型模式其实和工厂模式比较类似,都是用来创建对象的,只不过与工厂模式的实现不同。原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。这样就免去了类创建时重复的初始化操作了。原型模式适用于大对象的创建,因为在创建一个大对象时,需要很大的开销。如果每次都去new就会消耗很大,原型模式仅需从内存拷贝既可。
还是继续通过实例来向大家演示一下
<?php /** * 抽象原型角色 */ interface Prototype { public function clone_obj(); } /** * 具体原型角色 */ class Concrete implements Prototype{ private $data; public function __construct($data) { $this->data = $data; } public function get_data() { return $this->data; } public function clone_obj() { /* * 深拷贝实现 */ /*$serialize_obj = serialize($this); // 序列化 $clone_obj = unserialize($serialize_obj); // 反序列化 return $clone_obj;*/ return clone $this; // 浅拷贝 } } /** * 测试深拷贝用的引用类 */ class Demo { public $arr; } $demo = new Demo(); $demo->arr = array(1, 2); $concrete = new Concrete($demo); $object1 = $concrete->clone_obj(); var_dump($concrete->get_data()); echo '<br />'; var_dump($object1->get_data()); echo '<br />'; //测试深拷贝 $demo->arr = array(3, 4); var_dump($concrete->get_data()); echo '<br />'; var_dump($object1->get_data()); echo '<br />'; ?>
更多的详细说明和实例
php面向对象的设计模式中有很多种模式了,今天我们为各位介绍的是装饰器模式的一个学习笔记了,有需要了解php装饰器模式的朋友可以和小编来看看。
我们在使用面向对象的日常开发过程中,或许会碰见需要对某个方法或者某个对象,添加新的行为。然而常见的做法是,写一个子类继承需要改写的类,然后去重新实现类的方法。
但是装饰器模式(Decorator),可以动态地添加修改类的功能,在使用装饰器模式,仅需在运行时添加一个装饰器对象既可实现,相对与生成子类更加的灵活。
在我们需要改写一个类的时候通常的做法是采用继承的方式来重新方法,如下代码
/* * 比如我们需要改写一串字符串的样式,采用继承的写法。 */ class Canvas { function draw($width = 20, $height = 10) { for($i = 0; $i < $height; $i++) { for($j = 0; $j < $width; $j++) { echo '*'; } echo '<br/>'; } } } class Canvas2 extends Canvas { function draw($width = 20, $height = 10) { echo "<div style='color: red;'>"; parent::draw($width, $height); echo "</div>"; } } $Canvas2 = new Canvas2(); $Canvas2->draw();
对于上面的这种写法,假如我们需要多增加一个一种样式就需要多一个继承。接下来使用装饰器模式(Decorator)就会方便很多。
/* * 首先声明一个装饰器的接口 */ interface DrawDecorator { function beforeDraw(); function afterDraw(); }
接下来再分别添加两个装饰类,来继承接口,实现接口中的方法
/* * 颜色装饰 */ class ColorDrawDecorator implements DrawDecorator { protected $color; function __construct($color = 'red') { $this->color = $color; } function beforeDraw() { echo "<div style='color: {$this->color};'>"; } function afterDraw() { echo "</div>"; } } /* * 字体大小装饰 */ class SizeDrawDecorator implements DrawDecorator { protected $size; function __construct($size = '14px') { $this->size = $size; } function beforeDraw() { echo "<div style='font-size: {$this->size};'>"; } function afterDraw() { echo "</div>"; } }
接下来就是使用我们前面所创建的装饰类
/* * 创建一个画布类 */ class Canvas { protected $decorators = array(); //用来存放装饰的数组 function draw($width = 20, $height = 10) { $this->beforeDraw(); for($i = 0; $i < $height; $i++) { for($j = 0; $j < $width; $j++) { echo '*'; } echo '<br/>'; } $this->afterDraw(); } //添加装饰器的方法 function addDecorator(DrawDecorator $decorator) { $this->decorators[] = $decorator; } function beforeDraw() { foreach($this->decorators as $decorator) { $decorator->beforeDraw(); } } function afterDraw() { $decorators = array_reverse($this->decorators); foreach($decorators as $decorator) { $decorator->afterDraw(); } } } $Canvas = new Canvas(); $Canvas->addDecorator(new ColorDrawDecorator('red')); $Canvas->addDecorator(new SizeDrawDecorator('9px')); $Canvas->draw(20, 10);Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,下面我们就一起来看一篇简单的solr从数据库导入数据的例子
1. 先在本地的solr_db库下建一张solr_tb表(字段:id,name,price,desc),添加了5条数据,如下图。
建立数据表
2. 还记得上篇中安装solr的目录结构吗?打开D:\phpServer\solr\solr1\home\collection1\conf目录,暂且称这个目录为“配置文件目录”了。打开配置文件目录下的solrconfig.xml文件,在此文件的<config></config>标签之间的任一地方(最好是前面)添加如下代码:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
</lst>
</requestHandler>
配置dataimport
3. 在当前目录下新建一个名为data-config.xml的文件,在文件中添加如下配置信息,此信息为你要连接的mysql库的信息,请如实填写(数据库为solr_db,数据表为solr_tb)。
<?xml version="1.0" encoding="UTF-8" ?>
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://127.0.0.1/solr_db"
user="root"
password="root" />
<document>
<entity name="solr_tb" transformer="DateFormatTransformer"
query="select id,name FROM solr_tb where id >= ${dataimporter.request.id}">
</entity>
</document>
</dataConfig>
4. 找到同级目录下面的schema.xml(字段配置)文件,先将此文件备份一下,然后打开schema.xml,全选->删除,再将下面这段粘贴到schema.xml文件中。因为我们暂时用不到那么多参数,所以为了便于初学和理解,简化了一下。
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.5">
<!-- 要建立的索引,_version_这个不能省略 -->
<fields>
<field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
<field name="name" type="text_general" indexed="true" stored="true" />
<field name="_version_" type="long" indexed="true" stored="true"/>
</fields>
<!-- 字段类型,配置上面字段<field>中出现的类型即可 -->
<types>
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
<!-- text_general能对中文分词 -->
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
</types>
<!-- 唯一字段 -->
<uniqueKey>id</uniqueKey>
<!-- 设置默认查询字段,设置此处为name后,还要在solrconfig.xml中设置name="/select"中为<str name="df">name</str>。-->
<defaultSearchField>name</defaultSearchField>
<!-- 查询转换模式,是并且还是或者(and/or) -->
<solrQueryParser defaultOperator="OR"/>
</schema>
说明一下:“<fields>”标签,就是配置你要建立索引的字段。我现在需要建立id,name两个字段,
即 <field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/> 和 <field name="name" type="text_general" indexed="true" stored="true" /> 也就是对应第3步中select id,name FROM solr_tb这里的字段。了解一下fields标签。
5.在D:\phpServer\solr\dist目录中复制solr-dataimporthandler-4.7.0.jar、solr-dataimporthandler-extras-4.7.0.jar和mysql-connector-java-5.1.7-bin.jar(这个没有?点击下载)复制到D:\phpServer\solr\solr1\tomcat\webapps\solr\WEB-INF\lib目录中。
6.重启tomcat服务,访问http://localhost:8080/solr/,在左侧的菜单中的下拉选择collection1,然后再载入的菜单中选择Dataimport选项,是不是已经出现了如下图所示的界面呢。如果出现则表示成功了。接着按如下图的指示,现在开始导入数据吧!
Clean选项表示是否删除未匹配到的数据,也就是在数据库select结果中没有,而solr索引库中存在的时候,则删除。
相关文章
- 神马是“解释器模式”?先翻开《GOF》看看Definition:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在开篇之前还是要科普几个概念: 抽象语法树: 解释器模式并未解释如...2014-06-07
- 这篇文章主要为大家介绍了JavaScript设计模式中的装饰者模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
- 这篇文章主要介绍了JavaScript设计模式之职责链模式,对设计模式感兴趣的同学,可以参考下...2021-04-25
- 这篇文章主要为大家介绍了JavaScript设计模式中的状态模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-12
- 当我们在星际中开地图和几家电脑作战的时候,电脑的几个玩家相当于结盟,一旦我们出兵进攻某一家电脑,其余的电脑会出兵救援。 那么如何让各家电脑知道自己的盟友被攻击了...2016-11-25
- 这篇文章主要为大家介绍了JavaScript设计模式中的单例模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
- 这篇文章主要介绍了JavaScript设计模式之命令模式,对设计模式感兴趣的同学,可以参考下...2021-04-25
- 这篇文章主要为大家介绍了JavaScript设计模式中的责任链模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
- 这篇文章主要为大家介绍了JavaScript设计模式中的状态模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-14
- 这篇文章主要为大家介绍了JavaScript设计模式中的观察者模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
- 这篇文章主要给大家介绍的是JavaScript中的单例模式,设计模式代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,需要的朋友可以参考一下...2021-09-25
- 组合模式可以使客户端调用简单,它可以一致使用组合结构或是其中单个对象,简化了客户端代码。...2020-06-25
- 单例模式防止在应用程序中实例化多个对象。这就节约了开销,每个实例都要占用一定的内存,创建对象时需要时间和空间。...2020-06-25
- 这篇文章主要介绍了Javascript设计模式之原型模式,原型模式用于在创建对象时,通过共享某个对象原型的属性和方法,从而达到提高性能、降低内存占用、代码复用的效果。下面小编将详细介绍,需要的朋友可以参考下...2021-09-30
- 这篇文章主要介绍了设计模式中的模板方法模式及在C++中的使用,模板方法将逻辑封装到一个类中,并采取组合(委托)的方式解决这个问题,需要的朋友可以参考下...2020-04-25
- 本文给大家详解23种设计模式,理解设计模式有助于在程序开发过程中灵活应用,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了C++设计模式编程中简单工厂模式的采用实例,在简单工厂模式中程序往往利用封装继承来降低耦合度,需要的朋友可以参考下...2020-04-25
- 这篇文章主要介绍了C++设计模式编程中对状态模式的运用,状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类,需要的朋友可以参考下...2020-04-25
- php面向对象的设计模式中有很多种模式了,今天我们为各位介绍的是装饰器模式的一个学习笔记了,有需要了解php装饰器模式的朋友可以和小编来看看。 我们在使用面向对...2016-11-25
- 这篇文章主要介绍了详解设计模式中的Command命令模式及相关C++实现,命令模式强调调用操作的对象和操作的具体实现者之间的解耦,需要的朋友可以参考下...2020-04-25