1. 养乐多|杨城的个人博客首页
  2. 技术杂谈
  3. PHP

面向对象的设计原则简述

面向对象的设计共有五大设计原则(有说六大也有说七大),分别是单一职责原则、接口隔离原则、开放-封闭原则、替换原则、依赖倒置原则。这五大设计原则是23种设计模式的基础。
单一职责原则(Single Responsiblity Principle , SRP)
单一职责有两个含义:一是避免相同的职责分散到不同的类中;二是避免一个类承担太多职责。核心:减少类之间的耦合度,提高类的复用性。
遵守SRP在实际代码开发中的好处是显而易见的,这里我们以数据持久层为例来说明。所谓数据持久层主要指的是数据库操作,也包括缓存管理等等。以数据库操作为例,如果是一个复杂的系统,涉及到多种数据库的相互读写等,这时就需要数据持久层支持多种数据库。我们可以通过使用工厂模式,定义多个数据库操作类来实现。
最简单的工厂模式,就是根据传入的类型名来实例化对象,如传MySQL,就调用MySQL的类并实例化,如果是sqlite,则调用sqlite的类并实例化,甚至可以处理txt,excel等“类数据库”。工厂类也就是这样的一个类,它只负责生产对象,而不负责对象的具体内容。
先定一个接口,规定一些通用的方法,代码如下:

<?php
interface Db_Adapter
{
    /**
     * 数据库连接
     * @param $config 数据库配置
     * @return resource
     */

    public function connect($config);

    /**
     * 执行数据库查询
     * @param string $query 数据库查询sql字符串
     * @param mixed $handle 连接对象
     * @return resource
    */

    public function query($query, $handle);
}

这是一个简化的接口,并没有提供所有方法,其定义了MySQL数据库操作类,这个类实现了Db_Adapter接口。下面定义MySQL数据库操作类,代码如下:

<?php
class Db_Adapter_MySql implements Db_Adapter
{
    private $_dbLink;   //数据库连接字符串

    /**
     * 数据库连接函数
     * @param $config 数据库配置
     * @throws Db_Exception
     * @return resource
    */

    public function connect($config)
    {
        if ($this->_dbLink = @mysql_connect($config->host.(empty($config->port) ? '' : ':'.$config->port), $config->user, $config->password, true)) {
            if (@mysql_select_db($config->database, $this->_dbLink)) {
                if ($config->charset) {
                    mysql_query("SET NAMES '{$config->charset}'", $this->_dbLink);
                }
                return $this->_dbLink;
            }
        }

        // 数据库异常
        throw new Db_Exception(@mysql_error($this->_dbLink));
    }

    /**
     * 执行数据库查询
     * @param string $query 数据库查询sql字符串
     * @param mixed $handle 连接对象
     * @return resource
    */

    public function query($query, $handle)
    {
        if ($resource = @mysql_query($query, $handle)) {
            return $resource;
        }
    }
}

接下来是SQLite数据库操作类,代码如下:

<?php
class Db_Adapter_SQLite implements Db_Adapter
{
    private $_dbLink;   //数据库连接字符串

    /**
     * 数据库连接函数
     * @param $config 数据库配置
     * @throws Db_Exception
     * @return resource
    */

    public function connect($config)
    {
        if ($this->_dbLink = sqlite_open($config->file, 0666, $error)) {
            return $this->_dbLink;
        }

        // 数据库异常
        throw new Db_Exception($error);
    }

    /**
     * 执行数据库查询
     * @param string $query 数据库查询sql字符串
     * @param mixed $handle 连接对象
     * @return resource
    */

    public function query($query, $handle)
    {
        if ($resource = @sqlite_query($query, $handle)) {
            return $resource;
        }
    }
}

到这里,数据库操作类已准备好,现在只需要定义一个工厂类,根据转入不同的参数实例化需要的类即可,代码如下:

<?php
class sqlFactory
{
    public static function factory($type)
    {
        if (require_once 'Drivers/'.$type.'.php') {
            $className = 'Db_Adapter_'.$type;
            return $className;
        } else {
            throw new Exception ('Driver not found');
        }
    }
}

需要调用时,这样写即可。代码如下:

$db = sqlFactory::factory('MySql');
$db = sqlFactory::factory('SQLite');

我们把创建数据库连接这块的程序单独拿出来,程序中的CURD操作就不用关心调用的是什么数据库了,只要按照规范使用对应的方法即可。工厂方法让具体的对象解脱了出来,使其不再依赖具体的类,而是抽象。

接口隔离原则(Interface Segregation Principle, ISP)
设计应用程序的时候,如果一个模块包含多个子模块,那么我们应该小心对该模块做出抽象。设想该模块由一个类实现,我们可以把系统抽象成一个接口。
ISP的主要思想如下:
(1)一个类对另外一个类的依赖性应当是建立在最小的接口上的
ISP可以达到不强迫客户(接口使用方)依赖于他们不用的方法,接口的实现类应该只呈现为单一职责的角色;
降低客户之间的相互影响—当某个客户程序要求提供新的职责(需求变化)而迫使接口发生改变时,影响到其他客户程序的可能性会最小。
(2)客户端程序不应该依赖它不需要的接口方法或功能

对接口的污染
过于臃肿的接口设计是对接口的污染。所谓接口的污染就是为接口添加不必要的职责,如果开发人员在接口中增加一个新功能的主要目的只是减少接口实现类的数目,则此设计将导致接口被不断的“污染”而“变胖”。
接口污染会给系统带来维护困难和重用性差等方面的问题。为了能够重用被污染的接口,接口的实现类就被迫要实现并维护不必要的功能方法。
“接口隔离”其实就是定制化服务设计的原则。使用接口的多重继承实现对不同的接口的组合,从而对外提供组合功能,实现按需提供服务。如下图:
面向对象的设计原则简述

对于接口的污染,可以考虑两种处理方式:一是利用委托分离接口;二是利用多继承分离接口。
委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理,如策略模式、代理模式等都应用到了委托的概念。

开放-封闭原则(OCP,Open Closed Principle)
基本思想:Open模块的行为必须是开放的、支持扩展的,而不是僵化的;Closed在对模块的功能进行扩展时,不应该影响或大规模地影响已有的程序模块。

替换原则(里氏替换原则,Liskov Substitution Principle LSP)
LSP思想:子类型必须能够替换掉它们的父类型、并出现在父类能够出现的任何地方。遵循LSP设计的抽象基类如下:

<?php
abstract class Cache
{
    /**
     * 设置一个缓存变量
     *
     * @param string $key   缓存key
     * @param mixed $vale   缓存内容
     * @param int $expire   缓存时间(秒)
     * @return boolean      是否缓存成功
    */

    public abstract function set($key, $vale, $expire = 60);

    /**
     * 获取缓存变量
     *
     * @param string $key   缓存key
     * @return mixed        缓存内容
    */

    public abstract function get($key);

   
    /**
     * 删除缓存变量
     *
     * @param string $key   缓存key
     * @return boolean      是否删除成功
    */

    public abstract function del($key);

    /**
     * 删除所有缓存变量
     *
     * @return boolean      是否删除成功
    */

    public abstract function delAll();

    /**
     * 检测是否存在相应的缓存
     *
     * @param string $key   缓存key
     * @return boolean      是否存在
    */

    public abstract function has($key);
}

如果现在要求实现文件、memcache、accelerator及redis等各种机制下的缓存,只需要继承这个抽象类并实现其抽象方法即可。

依赖倒置原则(Dependence Inversion Principle)
简单讲就是将依赖关系倒置为依赖接口,具体概念如下:
1. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象类(父类不能依赖于子类,它们都要依赖抽象类)
2. 抽象不能依赖于具体,具体应该要依赖于抽象
注:这里的接口不是狭义的接口。

如何满足DIP:
1. 每个较高层次类都为它所需要的服务提出一个接口声明,较低层次类实现这个接口;
2. 每个高层次类都通过该抽象接口事宜服务。

原创文章,作者:iConan,如若转载,请注明出处:https://www.aspyc.com/archives/669.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据