该如何抽象
学面向对象最常听到的词就是“抽象”。抽象是什么,我觉得有两种定义:一、从具体的东西抽离非关键因素得到的简化实体。二、参数化。
我们认识事物,都是从实体开始的,然后抽离部分细节,得到抽象简化后,归类对比等等得到认知。
比如你认识一个陌生人,首先要了解他得社会身份地位,然后把他对比你其他认识的人,得出你要对他采取哪种态度。这种也叫标签化,脸谱化。作为文学家,经常要克服这种认知惯性,把细节填充进脸谱化的人物中,让人物与众不同。不过程序员不需要做到这一步。
参数化从技术角度去论述这个问题。所谓参数化是指一个行为可以通过函数去描述。函数有自变量和输出值,而将行为的模式定义为参数化的函数,就把静态关系转化为动态关系。这相当于由点及面,由个体问题提升为群体问题,提升了原问题的外延,让解决方案适用更广泛的范畴。
因此,学习抽象应该分两面,一方面要精简信息,第二方面要动态化关系。
步骤如下:
一、具体问题具体分析。
二、具体问题提取关键属性和关键关系。
三、关系的动态化。
细则:
步骤一,将问题域(工作域)的相关信息进行整理,包括活动对象,约束条件,通信格式,通信渠道等等。
步骤二、筛选信息,得出问题所需的关键信息。
步骤三、通过参数化手段把紧密联系的静态关系改为对象无关的动态关系。
有一些人,喜欢通过抽象去认识问题,我认为是错误的,应该从具体逐渐形成抽象,抽象是产物。
没有从具体问题出发的抽象,只是凭空想象。
因此首先要强调的步骤就是信息的搜集阶段,也叫调查阶段,需求分析阶段。有怎样的实体,才有怎样的抽象。
筛选信息是关键一步,为何现实问题很复杂,难以理解,这在于无用信息过多,影响了人们的判断。提取信息后,一方面可以减少无用信息,一方面可以将表面差异很大的事物归结为同一类,用类比的方法去解决问题。
参数化的手法有很多,未必就是特指用函数去实现,而是指类似函数性质的方式。比如代数式就是用符号代替具体的数量,而形成一个适用范围很广的公式。在编程语言中,变量,函数,以及对象,接口等高级特性都是参数化的手段。如果说第一步的目的是为了解决当前问题,这一步就是为了解决类似当前问题的所有问题而设。当然,我们编程未必需要做到这一步,但是如果此类问题有很多,那么为了提高复用性,就需要我们多花点时间去做好这一步的抽象。
面向对象编程,人们往往喜欢什么都归结为对象,再编程。但是对象和抽象是两码事,对象是工具,而抽象才是方法。对象这个工具用不用,在乎我们具体问题是怎样的。因此不能把对象当做编程的第一步。有时候我们只需要一个变量,一个数组就解决问题,为何要去用对象这种庞大的武器呢。这里面需要强调的就是一个优先次序的问题,解决问题是从低级方案慢慢转化到高级方案,只有低级方案不适用的时候,才需要提升到高级方案,而不是说我一定要用最高级最灵活的方案才是去解决问题。务实不等于不学无术。
我在网上搜了一个经典的数学题来做案例:
-----------------------------------
我老婆去市场买菜,她8块钱买了一只鸡。
回来的路上,她同事想买她的鸡。于是我老婆9块钱把鸡卖给了同事。
后来她想中午我想吃鸡,又找到同事,10块钱买了回来。
到了家门口,邻居来了客人,少了菜,一定要我老婆把鸡卖给他,我老婆又11块钱卖给了邻居。
问:我老婆赚了多少钱?
-----------------------------------
第一步就是收集信息,“老婆,市场,鸡,路上,同事,钱,中午,家门口,邻居”,问题是老婆赚了多少钱。
第一步的结果会出现很多我们不知道它有没有用的信息,这需要第二步去筛选。其中老婆,市场,同事,邻居是关键的对象,鸡和钱是个通信信息,路上,中午,家门口是无用信息。关系是:老婆和市场,同事和老婆,老婆和同事,邻居和老婆的购买关系。
到这一步,只要按照顺序写程序的步骤就能得到具体问题的答案。
第三步,老婆,市场,同事,邻居其实是一类东西,除了名字不同(标识不同或对象不同),他们都是懂得买和卖两种操作的类型。因此可以把四个关系参数化为一个关系:交易(买家,卖家)。然后只需要定义4个对象便可:
交易类型 老婆,市场,同事,邻居。
交易(老婆,市场);
交易(同事,老婆);
交易(老婆,同事);
交易(邻居,老婆);
输出(老婆);
第一步,老婆-8;
第二步,老婆+9;
第三步,老婆-10;
第四步,老婆+11;
结果是赚了2块。
如果还想输出其他对象赚了多少,也可以援用老婆的输出函数。这就是我们用了对象方法去参数化的好处。如果没有这一步,就需要分别写4个交易的具体代码。