面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。笔者从产品的角度入手,对面向对象中的一些基本名词,比如类和对象,进行了发散式的思考,在此和大家分享。 面向对象(Object-Oriented ,OO)是目前从事软件开发行业的人必备的基础思维。之所以称其为"思维"而非"知识",是因为知识是有限的、有领域(domain)的,而思维是可以无限的、创造性的、跨领域的。 本文要谈的正是基于其跨领域的特性,将一些OO中最简单的基本名词,从产品设计的角度做一些发散思考。 一、业务产品中的OO 本文只普及OO中的几个最基本的通用术语,并且不再赘述它们的技术定义(有兴趣的可以自行百度),用人话把这几个术语背后的思维逻辑描述一下。 1. 类(Class)和对象(Instance) 在业务产品设计之前,第一步要做的就是把业务逻辑中那些可以认为是一个主体的"东西"抽象出来,这个主体可能是真实的(如医生、医生助理、医院等),也可能是虚拟的(如医疗服务、订单、预约等),我们称这些抽象出来的主体为类。 怎么判断哪些可以抽象成类呢? 拥有自己的属性或自己的行为。比如医生有姓名、职称、擅长等属性,也有看病、开检查单、上班等行为。 还要看这些属性和行为是否在自己的业务逻辑中产生作用。比如手机号虽然也有运营商、归属地等属性,但是这些属性与自己的业务没有关系,那么它就没必要抽象成类。 如果说类是我们对主体定义了一个模型,对象就是基于这个模型产生的具体实例。比如张妙手医生、二级护理服务、小李刚成交的2800元的二级护理服务的订单。 类是在业务逻辑和产品设计中使用的载体,对象则是业务运行中实实在在产生的一条条数据。 在业务的讨论中经常发生名词混淆,口径不统一等问题,多方对同一个名词理解有偏差,甚至根本不是一个东西,导致很多不必要的争论和误解,这些现象产生的原因就是没有准确的抽象定义清楚类。 2. 抽象(Abstraction) 刚刚提到过这个术语,好的"抽象"就是一个建模的过程。把一个富有多种多样属性和行为的主体,抽出对业务有用的部分,不多不少,不偏不倚,还要富有兼容性和扩展性。 抽象是OO的基础,这个事跟说相声差不多。门槛特别低,按刚才说的逻辑很容易就掌握了。 但是要做好也特别难,也是最考验功力的地方,因为后续所有的设计都基于抽象出来的类,尤其是在业务不断优化和拓展中,牵一发动全身,经常会出现一失足成千古恨的情况。 3. 继承(Inheritance) 继承顾名思义,主要用来定义类的父子关系。比如我们的业务产品提供医生就诊服务、麻醉服务,护理服务。 于是我们可以抽象出3个类来对应他们在业务逻辑中的主体位置。但这只是起步,OO的抽象并不仅限于此。 我们可以发现,这3个主体的属性和行为中有很多是重复的。比如都有价格、提供者、有效期等属性,都可以提供预约等。 简单来说,都有医疗服务的共性。于是我们可以抽象出一个父类——医疗服务类。3个主体各自的类都作为子类继承于服务类,以此自动获得父类的特征。 这种父子关系的好处是: 将主体的共性抽象出来后,一旦对业务产品进行升级或者变动时,能够快速梳理相关的其他业务逻辑。比如当我们需要为就诊服务增加会员价格或评价机制时,马上就会想到对其他的服务也可以适用。此时,只需要在医疗服务父类增加相应的逻辑就可以了,减少了很多重复工作量。 同时还能保证子类的特殊性,比如护理服务可能是全天候的,会诊服务会提供诊断结果,麻醉服务可能是一次性的。 4. 多态(Polymorphism) 多态是指不同子类的同一个行为是有具体差别的,是多种状态的。比如医护人员能够提供服务的行为,虽然都叫提供服务,但实际上提供的服务内容和方式都是不一样的。 于是我们在业务逻辑中可以统称为提供医疗服务,实际运营时要针对具体的服务详细定制流程和规则,并提供相应的支持。这样可以在业务逻辑设计的过程中,面对总体设计时不会被零碎的细节干扰。 5. 封装(Encapsulation) 封装很好理解,比如业务中的医疗服务。服务内部的成本、运营、人员配合等对用户来讲是个黑盒子,我们将其封装起来,只需要提供对应的消息传递机制(message sending)就可以了。 消息传递机制由接口(interface)和规则(regulation)构成。 接口:就是消息传递的通道,比如服务的购买入口、咨询入口、预约入口、投诉入口; 规则:就是明确用户通过接口需要提供什么、能够获得什么。提供个人信息并在线支付价款就能获得服务;提供时间和服务凭证就能获得医生的预约;提供医生助理姓名和证据就能投诉并得到反馈。 当用户提出需求后,他并不需要知道我们是怎么满足他的,只需要给用户结果就可以了。将复杂的逻辑留给自己,将简单的动作留给用户,这就是封装最大的意义。 6. 关联(Association) 关联形容的是类和类之间的联系,将不同类关联起来就能够组合出复杂的业务,而且每个类就向零件一样,可以更换,可以复用,为创造提供了无限的可能性。比如: 多点执业可以通过医生和医院的关联实现; 用户购买的诊疗服务通过用户、医生、服务类别的关联实现; 就诊预约可以通过用户、订单、服务助理、医生出诊时间地点等关联实现。 关联的多种形式 当我们在定义业务产品的时候提出一个需求:每个医生需要配备医生助理,帮助医生进行患者管理工作。那么医生和医生助理之间就形成了关联。 最基本的4种关联: "一对一":一个医生配备一个专属助理,只服务这个医生; "一对多":一个医生配备多个专属助理,都只服务这个医生; "多对一":一个医生配备一个助理,一个助理服务多个医生; "多对多":一个医生配备多个助理(服务助理,跟诊助理等),一个助理可以服务多个医生。 不同的关联方式拥有不同的成本,不同的效果,不同的管理方式,不同的业务流程。同样一个最初的需求,定义什么样的解决方案,得到的结果却大不相同。 小案例: 业务中目前已经提供目前麻醉服务、就诊服务等医疗服务。根据当时的业务产品定义,每个服务为一个就诊人提供服务。随着业务的发展产生了如下两个需求: 产科服务结束后,一般会向妈妈提供后续的儿科服务,有时会碰到双胞胎,于是儿科服务的就诊人就需要关联多个; 随着业务发展,公司希望开发家庭服务包,通过打包多个专业科室的医生,为一个家庭的所有成员提供服务。 基于OO的OOA(Object-Oriented Analysis): 儿科服务与产科服务都属于个体对个体的服务,都是用了就诊服务类作为模型。双胞胎概率很小,从设计角度分析,为了一些特例去改变类的属性结构和关联关系是需要慎重的。再从业务角度分析,产生这种现状的原因是当时没有清晰的定义儿科服务是否可以针对群体,如果医生的工作量会随就诊人的增加而增加,而服务收益不变,这是不合理的。所以,多个孩子(即使是双胞胎)应该购买多个儿科服务,就像上学也要交两份学费一样。而不是直接更改服务和就诊人的关联规则和设计; 家庭服务包在业务定义上是群体对群体的服务。这种服务与医生的关联关系是多对多,与就诊人的关联关系也是多对多的。于是,与原来的就诊服务类产生了本质上的不同,接下来内部预约的规则,收益的分配都因此而变的复杂和不同。如果还复用原来的就诊服务这个轮子,已经不再适合。这时就需要抽象出一个新类,来作为接下来设计业务产品的基础。 二、OO是产品经理的内功心法 如果某个从事技术开发的同学恰巧也看到了这篇文章,可能会觉得这些内容如此熟悉,不正是每天开发的内容么? 那就对了,OO其实也是产品经理的内功心法。 从某个角度讲,一个好的产品经理必需的一个能力就是通过OO将业务产品设计和技术产品设计统一,保证方向和实施的无缝对接,协调业务和技术的高效配合。 只有内功心法还不够,还需要大杀器配合才能做到,那就是大名鼎鼎的UML(Unified Modeling Language),以后再介绍吧。