抽象(计算机科学)

软件工程计算机科学中,抽象是从对象系统的研究中概括具体细节(例如属性)的过程,以将注意力集中在更大重要性的细节上。抽象是计算机科学和软件工程中的一个基本概念,尤其是在面向对象的编程范式中。其中的示例包括:

理由

抽象的本质是保留在给定情况下相关的信息,而忘记了在这种情况下与之无关的信息。

- John V. Guttag

计算主要独立于混凝土世界。该硬件实现了与他人互换的计算模型。该软件是在体系结构中构建的,可以使人类一次专注于一些问题来创建巨大的系统。这些体系结构是由抽象的特定选择组成的。 Greenspun的第十条规则是关于这种建筑如何不可避免和复杂的格言

语言抽像是计算中抽象的一种中心形式:开发了新的人工语言来表达系统的特定方面。建模语言有助于计划。计算机语言可以使用计算机处理。这个抽象过程的一个例子是编程语言机器语言汇编语言高级语言的世代发展。每个阶段都可以用作下一阶段的垫脚石。语言抽象继续以脚本语言特定于域的编程语言继续进行。

在编程语言中,某些功能使程序员创建新的抽象。这些包括子例程模块多态性软件组件。诸如软件设计模式体系结构样式之类的其他抽像对于翻译人员仍然是看不见的,并且仅在系统设计中运行。

一些抽象试图通过完全隐藏其构建的抽象来限制程序员需要注意的概念范围。软件工程师兼作家乔尔·斯波尔斯基(Joel Spolsky)批评了这些努力,声称所有抽像都是漏水的- 他们永远无法完全隐藏下面的细节;但是,这并不否定抽象的有用性。

某些抽象旨在与其他抽象相互互动 - 例如,编程语言可能包含用于调用低级语言的外国功能接口

抽像功能

程式设计语言

不同的编程语言可提供不同类型的抽象,具体取决于该语言的预期应用程序。例如:

  • 面向对象的编程语言中,例如C ++对象PascalJava抽象的概念本身已成为声明性的语句 - 使用语法function(parameters) = 0;(在C ++中)或关键字abstractinterface(在Java中)。在这样的声明之后,程序员有责任实施一类以实例化声明的对象。
  • 功能编程语言通常表现出与功能相关的抽象,例如lambda抽象(将术语变成某些变量的函数)和高阶函数(参数是函数)。
  • LISP编程语言家族的现代成员,例如Clojure计划常见的LISP支持宏观系统,以允许句法抽象。其他编程语言(例如Scala)也具有宏或非常相似的元编程功能(例如, Haskell具有模板Haskell ,而OCAML具有元摄像机)。这些可以允许程序员消除样板代码,抽象繁琐的函数调用序列,实现新的控制流结构并实现特定域的语言(DSL) ,这些语言允许以简洁而优雅的方式表达特定于领域的概念。所有这些,如果正确使用时,都通过使预期的目的更加明确地提高了程序员的效率和代码的清晰度。句法抽象的结果还在于,与“更传统的”相比,在任何现代LISP中,任何LISP方言和实际上几乎任何编程语言原则上都可以在任何现代LISP中实施(但在大多数情况下仍然是无误)的努力。 PythonCJava等编程语言。

规范方法

分析师已经开发了各种方法来正式指定软件系统。一些已知方法包括:

  • 基于抽像模型的方法(VDM,Z);
  • 代数技术(Larch,Clear,OBJ,Act One Act,CASL);
  • 基于过程的技术(Lotos,SDL,Estelle);
  • 基于痕量的技术(特殊,TAM);
  • 基于知识的技术(精炼,要点)。

规范语言

规范语言通常依赖于一种或另一种的抽象,因为规格通常是在项目中较早定义的,而不是最终的实现(在更抽象的层面上)。例如, UML规范语言允许在项目的架构和规范阶段中的瀑布项目中的抽象类别定义。

控制抽象

编程语言将控制抽像作为其使用的主要目的之一。计算机机器了解非常低级别的操作,例如将一些位从内存的一个位置移动到另一个位置,并产生两个位序列的总和。编程语言允许在更高级别上完成此操作。例如,考虑以帕斯卡( Pascal)方式写的这一说法:

a := (1 + 2) * 5

对于人类来说,这似乎是一个相当简单且明显的计算( “一个加两个是三,第五次是十五个” )。但是,进行此评估所需的低级步骤,并将值“ 15”返回,然后将该值分配给变量“ A”,实际上是非常微妙而复杂的。值需要将值转换为二进制表示(通常比人们想像的要复杂得多),并且计算(由编译器或解释器)分解为汇编指令(同样,对程序员的直观程度要少得多:诸如操作,例如向左移动二进制寄存器,或将一个寄存器内容的二进制补充添加到另一个寄存器,这根本不是人类对加法或乘法的抽象算术操作的看法)。最后,将所得的值分配为“ 15”标记为“ A”的变量,以便以后可以使用“ A”,涉及其他“幕后”步骤,以查找变量的标签,以及在物理中的结果位置或虚拟内存,将“ 15”的二进制表示形式存储到该内存位置,等等。

没有控制抽象,程序员每次只想添加或乘以几个数字并将结果分配给变量时,都需要指定所有寄存器/二进制级别的步骤。这种重复的努力有两个严重的负面后果:

  1. 它迫使程序员每次需要类似操作时都不断重复相当普遍的任务
  2. 它迫使程序员为特定的硬件和指令集编程

结构化编程

结构化编程涉及将复杂的程序任务分解为较小的零件,并具有清晰的流量控制和组件之间的接口,并降低了副作用的复杂性潜力。

在一个简单的程序中,这可能旨在确保循环具有单个或明显的出口点,并(在可能的情况下)从功能和过程中获得单个出口点。

在较大的系统中,它可能涉及将复杂的任务分解为许多不同的模块。考虑一个处理船舶和海岸办公室工资单的系统:

  • 最高级别可能具有典型的最终用户操作菜单。
  • 其中可能是独立的可执行文件或库,用于签署和关闭员工或打印支票等任务。
  • 在每个独立组件中,可能有许多不同的源文件,每个文件都包含程序代码来处理问题的一部分,只有选定的接口可用于程序的其他部分。程序上的登录可以为每个数据输入屏幕和数据库接口具有源文件(本身可能是独立的第三方库或静态链接的库例程集)。
  • 数据库或工资核算应用程序还必须启动与船舶和岸边交换数据的过程,并且数据传输任务通常会包含许多其他组件。

这些层产生的效果是将一个组件的实现细节及其各个组件的实施细节及其各个组件的效果。面向对象的编程包含并扩展了此概念。

数据抽象

数据抽象可以在数据类型抽象属性与其实现的具体详细信息之间进行明确的分离。抽象属性是使用数据类型(数据类型的接口)可见的客户代码,同时混凝土实现完全私密,并且确实可以更改,例如随着时间的推移将效率提高纳入。这个想法是,此类更改不应该对客户端代码产生任何影响,因为它们在抽象行为上没有差异。

例如,可以定义一个称为查找表的抽像数据类型,该数据类型将相关联,并且可以通过指定其相应的键来检索值。这样的查找表可以通过各种方式实现:作为哈希表二进制搜索树,甚至简单的线性列表(键:值)对。就客户端代码而言,这种类型的抽象属性在每种情况下都是相同的。

当然,这一切都依赖于首先获得接口的详细信息,因为任何更改都可能对客户端代码产生重大影响。作为查看这一点的一种方法:界面构成了数据类型和客户端代码之间约定行为的合同;合同中未阐明的任何内容都可能会更改,恕不另行通知。

手动数据抽象

尽管大部分数据抽像是通过计算机科学和自动化发生的,但有时此过程是手动完成的,而无需编程干预。可以理解的一种方法是通过对文献进行系统审查的过程进行数据抽象。在这种方法中,进行荟萃分析时,一个或几个摘要将数据抽象,而误差通过双重数据抽象降低,然后是独立检查(称为裁决)

面向对象的编程中的抽象

面向对象的编程理论中,抽象涉及定义代表可以执行工作,报告和更改其状态的“参与者”对象的设施,并与系统中的其他对象进行“通信”。术语封装是指状态详细信息的隐藏,但将数据类型的概念从较早的编程语言扩展到最强烈地将行为与数据相关联,并标准化不同数据类型相互作用的方式是抽象的开始。当抽象进入定义的操作中时,可以取代不同类型的对象,称为多态性。当它朝相反的方向进行时,在类型或类的内部进行构造以简化一组复杂的关系,称为委托继承

各种面向对象的编程语言为抽象提供了类似的设施,所有这些都支持以对象为导向的编程中的多态性策略,其中包括在相同或相似角色中替换一种类型的另一种类型。尽管不那么受支持,但配置,图像或软件包可能会在编译时链接时间加载时间下预先确定许多此类绑定。这只会在运行时至少留下此类绑定。

常见的LISP对象系统自我,例如,较少的类别区别,而更多地使用委托对多态性。单个对象和功能更灵活地抽象,以更好地与LISP的共享功能遗产更好。

C ++例证了另一个极端:它在很大程度上依赖于模板过载和编译时的其他静态绑定,这又有一定的灵活性问题。

尽管这些示例提供了实现相同抽象的替代策略,但它们并没有从根本上改变支持代码中的抽象名词的需求 - 所有编程都依赖于将动词作为函数,名词作为数据结构和过程的能力。

例如,考虑样本Java片段以代表一些常见的农场“动物”,以模拟其饥饿和喂养的简单方面的一种抽像水平。它定义一个Animal既代表动物的状态及其功能:

public class Animal extends LivingThing
{
     private Location loc;
     private double energyReserves;
     public boolean isHungry() {
         return energyReserves < 2.5;
     }
     public void eat(Food food) {
         // Consume food
         energyReserves += food.getCalories();
     }
     public void moveTo(Location location) {
         // Move to new location
         this.loc = location;
     }
}

使用上述定义,可以创建动物类型的对象并将其称为这样的方法:

thePig = new Animal();
theCow = new Animal();
if (thePig.isHungry()) {
    thePig.eat(tableScraps);
}
if (theCow.isHungry()) {
    theCow.eat(grass);
}
theCow.moveTo(theBarn);

在上面的示例中Animal是一种抽象来代替实际动物,LivingThing是进一步的抽象(在这种情况下为概括)Animal.

如果一个人需要一个更具差异化的动物等级结构- 例如,那些提供牛奶的人与那些在生命尽头没有提供肉类的人提供的牛奶- 那是一个抽象的中介水平,可能是乳制品(牛,山羊)吃适合喂好牛奶的食物,以及可以吃食物以提供最佳肉质的食物(猪,牛皮)。

这样的抽象可以消除应用程序编码器指定食物类型的需求,因此它们可以集中精力于喂养时间表。这两个类可以使用继承或单独使用,并且程序员可以在两种类型之间定义不同程度的多态性。这些设施在语言之间往往会发生巨大变化,但是总的来说,每个设施都可以实现其他任何一种。许多操作过载,数据类型的数据类型在编译时可以与任何程度的继承或其他实现多态性相同的效果。课程符号仅仅是编码员的便利性。

面向对象的设计

关于要抽像以及在编码者控制下要保持什么的决定成为面向对象的设计和领域分析的主要关注点 - 实际上确定现实世界中的相关关系是面向对象的分析或遗产分析的关注点。

通常,为了确定适当的抽象,必须对范围(域分析)做出许多小决策,确定必须与哪些其他系统(遗产分析)进行合作,然后执行详细的面向对象的分析,该分析在项目时间和预算中表达约束作为面向对象的设计。在我们的简单示例中,领域是bar,活的猪和母牛及其饮食习惯是遗产的限制,详细的分析是,编码人员必须具有灵活性来喂食动物,因此没有理由进行编码。班级本身的食物类型以及设计是一个单一的简单动物类别,其中猪和牛是具有相同功能的实例。一个区分乳制品的决定将改变详细的分析,但是域和遗产分析将不变 - 因此,它完全由程序员控制,被称为对象面向对象的编程中的抽象,与域或遗产的抽像不同,分析。

考虑因素

在讨论编程语言形式方法抽象解释的正式语义时,抽象是指考虑观察到的程序行为的详细但安全的定义的行为。例如,人们可以仅观察程序执行的最终结果,而不是考虑所有执行的中间步骤。抽象定义为一个具体(更精确)的执行模型。

如果人们可以在混凝土或抽像模型上很好地回答有关该财产的问题,则抽象可能是准确忠实的。例如,如果一个人希望知道仅涉及整数 +, - ,×的数学表达式评估的结果是值得,那么一个人只需要执行所有操作模型(此抽象的熟悉形式是施放的九)。

然而,虽然不一定要精确,但应该是合理的。也就是说,即使抽象可能仅产生不可证明的结果,也应该从他们那里获得合理的答案。例如,班上的学生可以通过最小和最大的年龄来抽象。如果有人问某个人是否属于该班级,则可以将该人的年龄与最小和最大的年龄进行比较。如果他的年龄超出了该范围,则可以安全地回答该人不属于班级的人。如果没有,则只能回答“我不知道”。

编程语言中包含的抽像水平会影响其整体可用性认知维度框架包括形式主义中抽象梯度的概念。该框架使编程语言的设计师可以研究抽象与设计特征之间的权衡,以及抽象的变化如何影响语言可用性。

在处理计算机程序时,抽象可以证明是有用的,因为计算机程序的非平凡属性本质上是不可确定的(请参见Rice的定理)。结果,用于得出有关计算机程序行为的信息的自动方法要幺必须删除(在某些情况下,它们可能失败,崩溃或永远不会产生结果),健全性(它们可能提供虚假信息)或精度(他们可能会回答一些问题)。

抽像是抽象解释的核心概念。模型检查通常是在研究系统的抽象版本上进行的。

抽像水平

计算机科学通常提出抽象的级别(或不太常见的),其中每个级别代表相同信息和过程的不同模型,但细节量不同。每个级别都使用一个涉及仅适用于特定域的唯一对象和组成的表达系统。每个相对抽象的“较高”级别都建立在相对具体的“较低”水平上,该水平往往提供越来越“颗粒状”的表示。例如,大门以电子电路为基础,在门上的二进制,机器语言,二进制语言,机器语言上的编程语言,应用程序和编程语言上的操作系统。每个级别都是通过其下面的级别体现的,但并未确定,使其成为一种具有独立性的描述语言。

数据库系统

由于许多数据库系统的用户对计算机数据结构的熟悉程度不深,因此数据库开发人员通常会通过以下级别隐藏复杂性:

数据库系统的数据抽象级别

物理水平:最低抽象级别描述了系统实际存储数据的方式。物理级别详细描述了复杂的低级数据结构。

逻辑级别:下一个较高的抽象级别描述了数据库存储的数据以及这些数据之间存在的关系。因此,逻辑级别用少数相对简单的结构来描述整个数据库。尽管在逻辑层面上实现简单结构可能涉及复杂的物理水平结构,但逻辑级别的用户并不需要意识到这种复杂性。这被称为物理数据独立性数据库管理员必须决定将哪些信息保存在数据库中,并使用抽象的逻辑级别。

视图级别:最高级别的抽象级别仅描述整个数据库的一部分。即使逻辑级别使用更简单的结构,由于存储在大型数据库中的各种信息,因此仍然存在复杂性。数据库系统的许多用户不需要所有这些信息。相反,他们只需要访问数据库的一部分。存在抽象的视图水平,以简化其与系统的相互作用。该系统可以为同一数据库提供许多视图

分层体系结构

提供不同水平抽象设计设计的能力可以

  • 大大简化设计
  • 使不同的角色参与者能够在各种抽象的水平上有效地工作
  • 支持软件工件的可移植性(理想的理想模型)

系统设计业务流程设计都可以使用。一些设计过程专门生成包含各种抽象的设计。

分层体系结构将应用程序的关注点分为堆叠组(层)。这是一种用于设计计算机软件,硬件和通信的技术,其中系统或网络组件分层隔离,以便可以在一层中进行更改而不会影响其他层。

也可以看看