现在的位置: 首页 > 综合 > 正文

关注分离

2013年06月07日 ⁄ 综合 ⁄ 共 2395字 ⁄ 字号 评论关闭

   这两天在工作中经常会提到spearation of concern这个概念。我想在这篇博客里讲一下我对这个概念的理解。实际上关注分离并不是一个专属于IT领域的概念。在各个领域我们都在不自觉的考虑如何将任务切割,分配。记得在我读书的时候,看一些开源代码,就是不理解为什么这些所谓的牛逼代码写得那么绕:明明一个简单的new就能创建一个实例,偏要用一个工厂类来生成;明明一个new就能解决的问题,偏偏要拆成allocator和constuctor两个步骤;明明只为实现一个简单的逻辑,偏要在前面,后面包很多层。当时不理解为什么要这样,只是觉得这样很高端大气上档次。后来自己代码写得多了,书也看了不少,才慢慢体会到这些牛逼代码的设计初衷,懂得欣赏它们。我们创造出那么多种模式,发明了那么多编程理念,实际上核心都围绕着一点:关注分离。下面我们从最简单的例子慢慢衍生。

例子1:想要实现一个逻辑,给每一个员工配一辆车。我们最直观的想法就是需要车,就new一个实例出来呗:

Employee * AllocateCar()

{

        Employee *employee = new Employee();

        ...

        employee->Car = new Car();

        return employee;

}

例子2:我们的需求有一点小小的变化:显然不同的员工统一的配相同的车是不合适的。经理要有经理的车,HR要有HR的车。

Manager * AllocateCar()

{

        Manager *manager = new Manager();

       ...

        manager->Car = new Car1();

        return manager;

}

HR * AllocateCar()

{

        HR * hr = new HR();

        hr->Car = new Car2();

        ...

       return hr;

}

这样写代码显然是不好的:但凡有一个新的工种,需要配备一个新车,就得增加一个新的函数。考虑到经理和HR大多数属性及行为是一致的,车也是如此,我们很容易想到抽象出一个基类:

EmployeeBase * AllocateCar(EmployeeType type)
{

     EmployeeBase *employee;

     if (type == Type.Manager)

     {

             employee = new Manager();

             ...

             employee->Car = new Car1();

     }

     else if (type == Type.HR)

     {

             employee = new HR();

            ...

            employee->Car = new Car2();

      }

      ...

      return employee;

}
显然这样比刚才好很多的,至少统一了。但是还是不太好:这个函数本应该就负责分配小车,它并不需要关心不同工种配不同的车。它要做得太多了。为了解决这个,我们可以创建工厂类(如下):

EmployeeBase * AllocateCar(EmployeeType type)
{

      EmployeeBase * employee =  EmployeeFactory.Create(type);

      ...

      employee->Car = EmployeeBase.SelectCar(type);

      return employee;

}

引入工厂类,这个函数不会再变化了:所有变化的东西都转移到了另外两个函数中去了。这样的好处是显而易见的:关注分离了。 分车的只要管好你分车的逻辑就好了。

其他的事交给其他的模块。

现在还有问题么? 实际上这个函数只想做一件事, 给员工分配车。但事实上它做了两件事:1)配车前创建了员工的实例, 2)配车。显然通过函数名是无法预料到还有步骤1,这很有可能会导致内存泄漏。鉴于此,我们应该将创建员工上移:

EmployeeBase * AllocateCar(EmployeeBase *employee)

{

      if (employee == NULL) return NULL;

     employee->Car = EmployeeBase.SelectCar(employee->Type);

      return employee;

}

EmployeeBase * CreateEmployee(EmployeeType *type)
{

     EmployeeBase * employee = EmployeeFactory.Create(type);

     ...

     return AllocateCar(employee);

}

这样关注点就继续分离了:每一个函数都只负责一个逻辑。

但是这样还是不够好。我们想想创建完employee实例后(CreateEmployee) 我们是需要使用它的,用完还需要释放该实例。于是我们还需要编写一个ReleaseEmployee函数,并且想着调用它。 这样并不好。使用它的人往往只关注关于employee的业务逻辑,他不应该管理employee的生命周期。如果employee的生命周期非常长,想管好它也是非常不容易的。于是我们可以引入了依赖注入(Dependency Injection)这个概念。依赖注入容器负责管理依赖关系,生命周期,让业务逻辑将关注点从全部放在“业务”上。当代码量庞大的时候这种关注分离的威力就能体现出来了。因为任何地方出错,或者需要更新,我们只需要分析,修改,替换一小块模块,而不影响其他模块。从代码层面上说,这样的代码更容易维护,更容易扩展。从公司层面上说,招人更容易:)

抱歉!评论已关闭.