观察者模式解决的问题
在我们的开发过程中,应该都或多或少的碰到过改动其中一部分代码会引起其他一连串改变的问题,显然想要完全避免这种情况不太可能,但我们也应答尽量减少对其他组件的依赖,而观察者模式就是为了解决这个问题。
举个例子来说,我们有一个帖子对象,代码如下:
class Post
{
protected $_userid = null;
protected $_ip = null;
protected $_content = null;
function __construct()
{
// ...
}
// 发帖方法
public function addPost()
{
// ... 发帖逻辑
}
}
在上面是一个普通的帖子对象,随着发帖量和访问量越来越大,运营们开始不干了,公司也经常会接到投诉电话,说我们的网站有许多敏感内容和垃圾广告,因此我们需要做内容审核:首先是对用户的审核,一些黑名单用户应该被禁止发帖;二是对IP的审核;三是对内容敏感词的审核。因此我们的代码就成了如下的样子:
class Post
{
protected $_userid = null;
protected $_ip = null;
protected $_content = null;
function __construct()
{
}
public function addPost()
{
if (!Postscan::checkUserid($tihs->_userid)) {
return false;
}
if (!Postscan::ipUserid($tihs->_ip)) {
return false;
}
if (!Postscan::checkContent($tihs->_content)) {
return false;
}
// ...
}
}
随着需要审核的字段越来越多,addPost方法变得越来越长,发布对象被也只能紧紧的被嵌入到该系统中。
观察者模式的实现
观察者模式的核心是把观察者从主体中分离开来,当主体知道事件发生时,观察需要被通知到,同时我们也不想把主体和观察者之间的关系写死,于是我们来修改下我们上面的代码:
//主体必须实现的接口
interface Observable {
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}
//观察者必须实现的接口
interface Observer {
public function do(Observable $subject);
}
class Post implements Observable
{
protected $_userid = null;
protected $_ip = null;
protected $_content = null;
protected $_observerlist = array();
function __construct()
{
}
public function attach(Observer $observer)
{
$this->_observerlist[] = $observer;
}
public function detach(Observer $observer)
{
foreach ($this->_observerlist as $key => $value) {
if ($observer === $value) {
unset($this->_observerlist[$key])
}
}
}
public function notify()
{
foreach ($this->_observerlist as $value) {
if (!$value->do($this)) {
return false;
}
}
return true;
}
public function addPost()
{
if (!$this->notify()) {
return false;
}
// ...
}
}
通过上面的代码,我们可以再很容易的加入审核规则。
SPL代码
观察者模式是一个很常见和常用的设计模式,以至于SPL扩展已经为我们封装好了对应的类和方法,下面的代码是根据SPL提供的3个元素:SplObserver,SplSubject,SplObjectStorage来实现的代码
class Post implements SplSubject
{
protected $_userid = null;
protected $_ip = null;
protected $_content = null;
protected $_storage = new SplObjectStorage();
function __construct()
{
}
public function attach(SplObject $observer)
{
$this->_storage->attach($observer);
}
public function detach(SplObject $observer)
{
$this->_storage->detach($observer);
}
public function notify()
{
foreach ($this->_storage as $value) {
if (!$value->update($this)) {
return false;
}
}
return true;
}
public function addPost()
{
if (!$this->notify()) {
return false;
}
// ...
}
}
很简单吧,最重要的是理解,在这个例子中,我们把一些审核的方法从帖子类中剥离了开来,而且该帖子对象也可以用来作为其他的发布类型。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。