理解编程的抽象

iiliiliiil 3月前 62

所谓的抽象,就是将一系列事物共有的特质提取出来,舍去独有的部分的过程
也可以说是指从具体问题中,提取出具有共性的模式,再使用通用的解决方法加以处理

通俗的理解抽象

大家从小到大学到的大部分知识,无一例外的都是将具体事物抽象出来的结果
例如大家学到的 1+1 = 2 这个概念,如何理解和抽象的关系?
一只鸡加一只鸡等于两只鸡,一只狗加一只狗是不是就是两只狗?
这个过程中,共有的特质就是一个实体加另一个实体,就等于了两个实体,独有特质就是一个是猫,一个是狗。

于是加法了概念就这样诞生,在你理解了这个概念后,就可以灵活应用,比如你就会知道一个人加一个人就等于两个人

而学习知识最好的办法就是将抽象的知识类比为具体的事物来学习,比如老师教你们1+1 = 2 这个概念,是不是就是举的例子,一只鸡加一只鸡等于两只鸡这样?

而我们在应用学习到的知识时候,也是这个道理,先将具体问题抽象为我们所储备的知识,再用知识去解决问题

一个车可以座5人,10个车能做几人?我们要解决这个问题,得先把问题转为我们学过的数学乘法,在计算出结果

抽象和编程的关系

看到这里你应该对抽象有了一定的理解,那么抽象和编程有什么关系?

一个软件的架构写的如何,包括拓展性,可维护性,可移植性等等方面几乎直接由程序开发者对事物的抽象能力来决定,他们能根据项目的需要总结出什么地方需要用什么算法,什么数据结构,什么设计模式等等。
大家所熟知的各类设计模式,数据结构,算法,就是对各种情况抽象出来的结果。
所以各位应该知道为什么学习这些知识如此重要。

Thinkphp框架的源码就是不错的典范,不少朋友说是仿的 laravel,这里抛开框架整体思想不说,仅讨论代码的规范和各种设计思想,十分是值得大家学习

为什么要抽象

代码可复用性强,表现为同样的功能,绝不写第二次,没有重复功能的代码 参考 Thinkphp源码里 think\File类的 getInfo方法,写好后其他地方只管调用即可

代码可移植性强,写好的功能可以把代码提出来拿到其他项目里用,没有耦合(关于耦合的概念看文档的‘耦合于解耦’篇)参考Thinkphp源码里的vender\topthink\think-image能,使用composer安装,即便拿到其他框架一样可以工作,不会依赖 Thinkphp源码里的任何类库或者函数

抽象的原则及如何抽象

抽象需要对计算过程进行观察,看哪些计算模式是固定的,哪些是经常变化的,将共有的部分提取出来封装。

通常在抽象时需要遵循一个‘三次原则

通俗的说,在编写项目过程中,当一个重复的功能出现在三次以上的地方时候,就应该考虑将共有的部分封装为函数或者类,在需要用到的地方调用,不同的部分用传参数的方式传进去

第一次用到某个功能时,你写一个特定的解决方法;
第二次又用到的时候,你拷贝上一次的代码;
第三次出现的时候,你才着手"抽象化",写出通用的解决方法。

理由

(1)省事。如果一种功能只有一到两个地方会用到,就不需要在"抽象化"上面耗费时间了。
(2)容易发现模式。"抽象化"需要找到问题的模式,问题出现的场合越多,就越容易看出模式,从而可以更准确地"抽象化"。
(3)防止过度冗余。如果一种功能同时有多个实现,管理起来非常麻烦,修改的时候需要修改多处。在实际工作中,重复实现最多可以容忍出现一次,再多就无法接受了。

当然,面对一些能预知的通用功能,应该先封装好,在进行封装,后面在调用,比如类似排序函数之类的功能等

通常封装的形式粒度由小到大

  1. 函数和类中的方法 : 参考thinkphp源码里 helper.php 里的函数
  2.  : 包括工具类,机制类等等,参考thinkphp源码里 thinkphp 命名空间下的各个类文件,session,url等等
  3. 类库 : 功能比较庞大,一个类封装不了的时候,可以考虑封装为多个类,各司其职,相互调用完成复杂逻辑,类似phpexcel,phpMail这样大型工具库,可以参考thinkphp源码里think\cache 文件夹下的各个类

经验丰富的程序设计者,总是可以判断出做出什么程度的抽象是合适的

面向对象中的抽象类 abstract

如何理解面向对象中的抽象类(abstract)
参考thinkphp源码里think\cache 文件夹下的各个类文件
你会发现有个Driver.php里面有个abstract class Driver,有好几个方法如has,get,set等都是没有方法体,前面用了 abstract修饰。再看driver文件夹下有很多文件,这些类文件继承了Driver,他们每个类都重写了Driver类里的用abstract修饰过的方法,在这里就是对缓存这个动作做了抽象的结果。

共有特质:不论用何种方式缓存,File,Memcache,Redis等等,必然都有个set,get,clear等一些方法
独有特质:每个方式写入数据的方式都不一样,参考每个继承了Driver的类,如File,Memcache等,他们重写的方法体里就是具体的逻辑

另外,这个设计思想是典型的适配器模式
同样的示例参考 namespace think\db\connector 下各个类文件和 namespace think\db下的Connection类之间的关系


最新回复 (0)
返回
发新帖