面向对象的五大设计原则之单一职责原则

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luyaran/article/details/84580519

我们都知道,面向对象是一种高度抽象的思维,我们在面向对象设计中,类是最基本的单位,我们的各种设计都是围绕着类来进行的,可以这么说,类与类之间的关系,构成了设计模式的大部分内容,我么可能认为,类是属性+函数构成的,事实上在底层存储上确实也是这么来搞的,但是这些仅仅只是确定一个独立的类,而类与类之间的关系,才是设计模式所需要关心的内容,咱们对别的太多的东西也不废话,这篇文章只是来看下设计模式的五大原则之一的单一职责原则。

我们来通过一个工厂分工产生效率的例子来感受下这个单一职责原则。

假设,一个新手工人去一个汽车制造厂来上班,一辆汽车姑且分为36个部件,如果你让这个人去干的话,估计一天他也造不出来一辆车,但是我们如果把这三十六个部件,划分为三十六条工序,完事每个工序单独保存一条流水线,那么,只需要36的整数倍的工人来制造的话,每个人单独负责自己所在的流水线,学习自己所在的流水线所需要掌握的知识,一天就可以造出很多辆车,这就是企业管理中的分工,我们在面向对象设计中,叫做单一职责原则(single pesponsibility principle ,简称SRP)。

我们可以把职责,看做变化的原因,也就是说,就一个类而言,应该有且仅有一个原因能引起它的变化,这是最简单,最容易理解,但是最不容易做到的一个设计原则。

简单来说,就是怎样设计这个类,以及类的方法的一个界定的问题,这种问题很普遍,比如MVC框架中,很多人会疑惑,我们对于表单插入数据库字段过滤和安全检查应该是放在C层,还是放在M层,这就可以把问题归到单一职责的范围里。

再举个例子,就是员工类别,我们如果把,工程师,销售,运营等职业全部放在一个类别中,其结果必然是混乱的,我们需要用一个臃肿且不必要的代码段来判断员工的职业,这其中,不管是哪一种职业发生改变,都需要我们来进行一个改变,这就是搞事情了,我们其实应该明白这个单一职责的原则,如下两点:

  1. 避免相同的职责,分散到不同的类中
  2. 避免一个类承担太多的职责

那么,关于这个单一职责原则的优点也是有的,我们来看下:

1、减少类之间的耦合

如果我们减少类之间的耦合,那么在需求发生变化的时候,只需要修改一个类,就可以格力这种变化,如果此类含有多种职责,它们耦合在一起, 那么,但需要变化的时候,就是一个噩梦。

2、提高类的复用性

我们应该了解,修电脑比修电视机容易,因为电视机各个部件之间的耦合度太高了,而电脑就不一样了,内存条,硬盘啥的都是可以单独拆卸和组装的,所以,那个东西坏了,直接换了就行。

这就是单一职责原则的优势,我们可以使得各个部件之间,实现方便快捷的拆卸和组装,而违反了此原则,则会导致该类,在需要修改的时候,分离困难,影响较多的功能模块。

咱们来以数据持久层为例,这个数据持久层就是指数据库操作,当然,还有缓存管理等,我们进行数据操作时,如果是一个复杂的系统,那么就会涉及到多种数据库之间的相互读写操作,这个时候,就需要我们来设置数据持久层来支持多种数据库,该怎么来进行这些操作呢,答案很简单,使用工厂模式(Factory)。

工厂模式(Factory)允许我们在代码执行时实例化对象,之所以被称为工厂模式,就是因为这个设计模式负责生产对象啊,以咱们刚刚的操作为例子,咱们的这个工厂模式,需要根据我们传递的参数的不同,来实例化不同的对象,,就是我们传入mysql就给我们实例化mysql的对象,使得我们可以根据这个对象来进行相关操作,但是这个工厂类啊,只负责生产对象,而之后的这个对象的具体内容,就不应该归这个工厂类管了。

来看段代码感受下:

//定义一个适配器
interface db_adapter{
    //连接数据库
    public function connect($config);

    //执行sql
    public function query($query,$handle);
}

/**
* 操作mysql
*/
class db_mysql implements db_adapter
{

    public function connect($config)
    {
        echo "mysql_connect";
    }

    public function query($query,$handle)
    {
        echo "mysql_query";
    }
}

/**
* 操作sqlite
*/
class db_sqlite implements db_adapter
{
    
    public function connect($config)
    {
        echo "sqlite_connect";
    }

    public function query($query,$handle)
    {
        echo "sqlite_query";
    }
}

/**
* 定义工厂类来处理
*/
class factory_handle
{
    
    public static function factory($classname)
    {
        return new $classname;
    }
}

$db = new factory_handle();
$mysql = $db::factory("db_mysql");
$mysql->connect();
$sqlite = $db::factory("db_sqlite");
$sqlite->query();

上述代码呢,首先是定义了一个简单的接口,完事包含了一些简单的方法,之后就是db_mysql类实现了接口,再来就是db_sqlite类,也是实现了接口,在这种情况下,如果是要灵活调用这两个类的话,我们就定义了一个简单的工厂类,这样一来,我们在程序调用的时候,就不需要管是什么类以及类里面包含何种方法,我们只需要实例化工厂类,完事按着规范来使用相对应的操作就好了。

工厂类呢,它把具体的对象给解救出来了,使得它们不在必须依靠具体的类,而是抽象,除了这个mysql操作之外,还有就是SNS中的动态实现,也用到了工厂类,但是咱们这次要说的就是单一职责原则,不跑题了啊。

在面向对象的设计模式中,命令模式,也是体现了单一职责原则,这个命令模式会分离命令请求者和命令实现者方面的职责,这么来看哈,我们要去一个餐馆吃饭,餐馆有服务员,顾客,厨师三个角色,我们作为顾客,只需要列出菜单给服务员,完事由服务员通知厨师来实现这个菜单,厨师就只需要在接到这个通知的时候,去做饭,就算是完事类,在这个过程中,命令的请求和实现就完成了解耦,神奇不。。。

咱们来模拟下这个过程哈,代码如下:

//厨师类,接受命令
class cooker
{
    public function food()
    {
        echo "food";
    }

    public function drink()
    {
        echo "drink";
    }

    public function end()
    {
        echo "end";
    }
}

//命令接口
interface command
{
    public function execute();
}

//模拟服务员与厨师之间的过程
class food_cooker implements command
{
    private $cooker;

    //绑定命令接受者
    public function __construct(cooker $cooker)
    {
        $this->cooker = $cooker;
    }

    public function execute()
    {
        // TODO: Implement execute() method.
        $this->cooker->food();//让厨师做饭
    }
}

class drink_cooker implements command
{
    private $cooker;

    //绑定命令接受者
    public function __construct(cooker $cooker)
    {
        $this->cooker = $cooker;
    }

    public function execute()
    {
        // TODO: Implement execute() method.
        $this->cooker->drink();//让厨师做饭
    }
}

//模拟顾客与服务员之间的过程
class customer
{
    private $food_cooker;
    private $drink_cooker;

    //将命令发送者绑定到命令接受器上
    public function add_command(command $food_cooker,command $drink_cooker)
    {
        $this->food_cooker = $food_cooker;
        $this->drink_cooker = $drink_cooker;
    }

    public function call_food()
    {
        $this->food_cooker->execute();
    }

    public function call_drink()
    {
        $this->drink_cooker->execute();
    }
}

$obj = new customer();
$cooker = new cooker();
$food_cooker = new food_cooker($cooker);
$drink_cooker = new drink_cooker($cooker);
$obj->add_command($food_cooker,$drink_cooker);
$obj->call_food();
$obj->call_drink();

咱们来简单梳理下这些代码哈,首先咱们要知道,咱们是来模拟个啥的,具体就是我们点菜,完事给服务员,之后让服务员通知厨师做饭,首先呢,我们先来定义个厨师类(cooker),赋予它做饭的能力,它可以做食物,也可以做喝的,完事呢,我们来定义一个通知的接口(command),给服务员和厨师之间搭建起一个沟通的桥梁,之后就到了服务员出场的时候了,我们平常去餐馆,肯定是先叫服务员,不可能直接叫厨师,所以嘞,这个服务员就相当于是我们顾客和厨师之间的一个命令的传送者,我们接下来就创建做食物的类(food_cooker)和做喝的的类(drink_cooker),有这两个类,决定服务员可以向厨师传递的信息,最后就是我们顾客类(customer)了,这是我们和服务员沟通的过程,我们可以叫吃的或者是喝的,但是这些都是跟服务员说的。

从上述的实例,我们可以看出,设计模式并非只是一个简单的理论上的知识,它来源于生活,还有就是不仅仅咱们刚刚说的那两个设计模式,还有其他的也是包含着单一职责原则,咱就不一一例举了哈。

我们需要知道的就是,单一职责原则不仅仅对类的设计有意义,还会对我们的模块、子系统为单位的系统架构的设计具有同样的意义,模块、子系统也应该有且仅有一个引起它变化的原因,像MVC所倡导的各个层之间的相互分离,其实就是单一职责原则在系统总体设计中的具体应用,这个单一职责原则啊,是最简单也是最难做到的原则之一,因为我们会很自然的将职责连接在一起,而找到并且分离这些职责,就是软件设计需要达到的目的。

我们来看下在软件设计中,根据单一职责原则所需要遵循的一些做法。

我们根据业务流程,把业务对象提取出来,如果业务流程的链路太过复杂,我们就要把这个业务对象分离为多个单一的业务对象,当我们的业务链标准化之后,我们可以对业务对象内部的情况做进一步的处理,把第一次标准化作为最高层抽象,第二次标准化作为次高层抽象,以此类推呢,直到达到恰如其分的设计层次。

我们对于职责的分类,需要注意的是,首先,有业务职责,还需要脱离业务的抽象职责,从认识业务到抽象算法是一个层层递进的过程,就好比命令模式中的顾客,服务员以及厨师的职责,我们作为老板,或者说设计师,只需要规划好各自的职责范围就好,既需要防止越俎代庖,也需要防止互相推诿。

好啦,本次记录就到这里了。

如果感觉不错的话,请多多点赞支持哦。。。

猜你喜欢

转载自blog.csdn.net/luyaran/article/details/84580519