设计模式:灵活编程(观察者模式)

系统中的每个类应将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,有可能导致其它依赖对象的修改更新,那么开发任务会很快变成一个产生bug和消除bug的恶性循环。当我们创建一个对象的时候,一个对象的创建应当尽可能减少和其它对象间的耦合!一个对象的改变尽可能的不会引起代码库其它地方的修改。使用观察者模式能有效的解决此问题,一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知并被自动更新。

观察者模式(有时又被称为模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

问题

假设一个负责处理用户登录的类:

class Login
{
    const LOGIN_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS   = 2;
    const LOGIN_ACCESS       = 3;
    private $_status = array();

    public function handleLogin($user, $pass, $ip)
    {
        switch (rand(1,3))
        {
            case self::LOGIN_ACCESS:
                $this->setStatus(self::LOGIN_ACCESS, $user, $ip);
                $ret = true; break;
            case self::LOGIN_WRONG_PASS:
                $this->setStatus(self::LOGIN_WRONG_PASS, $user, $ip);
                $ret = false; break;
            case self::LOGIN_USER_UNKNOWN:
            default:
                $this->setStatus(self::LOGIN_USER_UNKNOWN, $user, $ip);
                $ret = false; break;
        }
        return $ret;
    }

    private function setStatus($status, $user, $ip)
    {
        $this->_status = array($status, $user, $ip);
    }

    public function getStatus()
    {
        return $this->_status;
    }
}

$login = new Login();
$login->handleLogin('BNDong', '123456', '127.0.0.1');
var_dump($login->getStatus());

当然这个类并没有实际功能, handleLogin 方法会存储验证用户数据,该方法有3个潜在的结果。状态标签会被设置为 LOGIN_USER_UNKNOWN 、 LOGIN_WRONG_PASS 或 LOGIN_ACCESS 。

现在看上去还可以,但是一个登录组件可不可能只有这面点东西,我们试着增加需求:(代码的腐败就是不断的迭代出来的)

记录登录IP地址

public function handleLogin($user, $pass, $ip)
{
    ...
    Logger::logIp($user, $ip, $this->getStatus());
    ...
}

登录失败发送邮件通知管理员

public function handleLogin($user, $pass, $ip)
{
    ...