类(计算机编程)

面向对象的编程中,是用于创建对象的可扩展程序编码 - 板,为状态(成员变量)提供了初始值和行为的实现(成员函数或方法)。

当对象由类的构造函数创建时,将结果对象称为类的实例,并且特定于对象的成员变量称为实例变量,以与整个类共享的类变量对比。

实际上,在某些语言中,类是一个编译时间功能(在运行时不能声明新类),而其他语言类是一流的公民,通常是本身(通常是类型的对象)班级或类似)。在这些语言中,在自身内部创建类的类称为元类

相关概念

实例

面向对象的编程(OOP)中,实例是任何对象的具体出现,通常存在于计算机程序的运行时。正式地,“实例”是“对象”的代名词,因为它们每个都是特定的值(实现),并且这些值可以称为实例对象。 “实例”强调了对象的独特身份。实例的创建称为实例化。

一个对象可能以多种方式变化。该对象的每个实现的变体都是其类的实例。也就是说,它是具有指定而不是变量的给定类的成员。在非编程环境中,您可以将“狗”视为一种类型,而您的特定狗是该类别的实例。

基于类的编程中,对像是由称为构造函数的子例程作为类实例创建的,并被破坏者销毁。对像是类的实例,因为它可以访问类的所有数据类型(原始和非原始)和类的方法等。因此,对象可以称为类实例或类对象。物体实例化称为结构。并非所有类都可以实例化 - 无法实例化抽像类,而可以实例化的类称为具体类。在基于原型的编程中,可以通过复制(克隆)原型实例来完成实例化。

类与类型

在最随意的用法中,人们经常指一个对象的“类”,但是狭义的对象具有类型:接口,即成员变量的类型,成员函数的签名(方法)(方法)和属性满足这些。同时,类具有实现(特别是方法的实现),并且可以使用给定的实现创建给定类型的对象。在类型理论的术语中,类是一个实现‍ - ‌一个具体的数据结构和子例程的收集 - ‌类型是接口。不同的(混凝土)类可以产生相同(摘要)类型的对象(取决于类型系统);例如,类型堆栈可以使用两个类实现: SmallStack (对于小堆栈快速,但尺度较差)和ScalableStack (尺度很好,但对于小堆栈而言高高的头顶)。同样,给定类可能具有几个不同的构造函数。

类类型通常代表名词,例如一个人,地方或事物或名称的名词,而班级代表了这些名词。例如,香蕉类型通常代表香蕉的特性和功能,而AbcbananaXyzbanana类则代表生产香蕉的方式(例如,香蕉供应商或数据结构和功能,可以在视频游戏中代表和绘制香蕉)。然后, Abcbanana类可以生产特殊的香蕉: Abcbanana类的实例将是香蕉类型的对象。通常只给出一种类型的实现,在这种情况下,类名称通常与类型名称相同。

设计和实施

类是由结构和行为成分组成的。包含类作为编程构造的编程语言提供支持,用于各种与类相关的功能,使用这些功能所需的语法差异很大,从一种编程语言到另一种编程语言。

结构

上课的UML符号

类包含数据字段描述(或属性字段数据成员属性)。这些通常是在程序运行时与状态变量关联的字段类型和名称;这些状态变量要幺属于类的类或特定实例。在大多数语言中,按类定义的结构决定了其实例使用的内存的布局。其他实现是可能的:例如, Python中的对象使用关联键值容器。

Eiffel等一些编程语言支持不变性的规范作为类的定义,并通过类型系统执行它们。状态的封装对于能够执行班级的不变性是必要的。

行为

使用方法定义了类或其实例的行为。方法是子例程,具有在对像或类上操作的能力。这些操作可能会改变对象的状态,或者简单地提供访问该对象的方式。存在许多种类的方法,但对它们的支持因语言而异。某些类型的方法是由程序员代码创建和调用的,而其他特殊方法(例如构造函数,驱动器和转换运算符)是由编译器生成的代码创建和调用的。语言还可以允许程序员定义并调用这些特殊方法。

类接口

每个类通过提供结构和行为来实现(或实现)接口。结构由数据和状态组成,行为由指定方法的实现代码组成。接口的定义与该接口的实现之间存在区别。但是,这一行在许多编程语言中都模糊了,因为类声明既定义和实现界面。但是,某些语言提供了将接口和实现分开的功能。例如,抽像类可以在不提供实现的情况下定义接口。

支持类继承的语言还允许类从其派生的类中继承界面。

例如,如果“ A类”从“ B类”继承,并且“ B类”实现接口“接口B”,则“类A类”也继承了“接口B”提供的功能(常数和方法声明)。

在支持访问指定符的语言中,类的接口被认为是类的公共成员集,包括方法和属性(通过隐式getter和setter方法);任何私人成员或内部数据结构均无意取决于外部代码,因此不是接口的一部分。

面向对象的编程方法学表明,类的任何接口的操作均应彼此独立。它产生了一个分层设计,接口客户端使用接口中声明的方法。一个接口没有要求客户以任何特定顺序调用一个接口的操作。该方法的好处是,客户码可以假设接口的操作可在客户端访问对象时使用。

类接口示例

电视机前面的按钮是您与塑料外壳另一侧的电线之间的接口。您按“电源”按钮以打开电视。在此示例中,您的特定电视是实例,每个方法都由一个按钮表示,所有按钮共同组成接口(其他与您的型号相同的电视集将具有相同的接口)。在最常见的形式中,接口是一组相关方法的规范,而没有任何相关方法的实现。

电视机还具有无数的属性,例如大小以及是否支持颜色,共同构成了其结构。一个类代表电视的完整描述,包括其属性(结构)和按钮(接口)。

获得制造的电视总数可能是电视课的静态方法。该方法显然与类相关联,但在类的每个实例的领域之外。在所有电视对象集合中找到特定实例的静态方法是另一个示例。

会员可访问性

以下是一组通用的访问说明符

  • 私人(或阶级私人)限制了对类本身的访问。只有同一类的一部分的方法才能访问私人成员。
  • 受保护(或受班级保护)允许班级本身及其所有子类访问成员。
  • 公众意味着任何代码都可以通过其名称访问成员。

尽管许多面向对象的语言都支持上述访问说明符,但它们的语义可能会有所不同。

面向对象的设计将访问说明符与仔细的公共方法实现的仔细设计来强制执行类别不变性 - 对象状态的构造。访问说明符的常见用法是将类的内部数据与界面分开:内部结构是私人的,而公共访问者方法可用于检查或更改此类私人数据。

访问说明符不一定控制可见性,因为即使是私人成员也可以看到客户外部代码。在某些语言中,可以在运行时引用一个无法访问但可见的成员(例如,由从成员函数返回的指针),但是尝试通过参考客户端代码的成员名称来使用它是类型检查器阻止。

各种面向对象的编程语言可以在各种程度上执行成员的可访问性和可见性,并根据语言的类型系统和编译策略,在编译时运行时执行。例如, Java语言不允许访问类的私人数据编译的客户端代码。在C ++语言中,私有方法可见,但在接口中不可访问;但是,可以通过明确声明代表该类界面的完全抽像类来使它们变得不可见。

某些语言具有其他可访问性方案:

  • 实例与类可访问性Ruby分别支持实例私人实例保护的访问指定符,分别代替阶级私人和阶级保护。它们的不同之处在于,他们根据实例本身而不是实例类限制访问权限。
  • 朋友:C ++支持一种机制,其中明确声明为班级函数的功能可以访问指定为私人或受保护的成员。
  • 基于路径:Java支持限制对Java软件包中对成员的访问,这是文件的逻辑路径。但是,在扩展Java框架以在与框架类同一包中实现类以访问受保护成员的类别的类时,这是一种常见的做法。源文件可能存在于完全不同的位置,并且可以部署到不同的.jar文件,但就JVM而言,仍处于相同的逻辑路径中。

阶层之间的关系

除了独立课程的设计外,编程语言还可以根据类之间的关系支持更高级的类设计。通常提供的类间关系设计功能是组成分层

组成

类可以由其他类组成,从而在封闭类及其嵌入式类之间建立了组成关系。类之间的组成关系也通常称为has-a关系。例如,可以由“汽车”类组成并包含“发动机”类。因此,汽车发动机。构图的一个方面是遏制,这是由具有它们的实例的组件实例围栏。如果封闭对象包含按值按值的组件实例,则组件及其封闭对象具有相似的寿命。如果组件通过参考包含,则可能没有类似的寿命。例如,在Objective-C 2.0中:

@interface Car : NSObject
@property NSString *name;
@property Engine *engine
@property NSArray *tires;
@end

汽车具有NSString字符串对象),引擎NSArray (数组对象)的实例。

分层

类可以从一个或多个现有类得出超类)和派生类(子类子类)。派生类与派生类别类别的关系通常称为IS-A关系。例如,可以从“控制”类派生“按钮”类。因此,按钮是一个控件。父母类的结构和行为成员是由子类继承的。衍生的类还可以定义其他结构成员(数据字段)和行为成员(方法),除了它们继承的那些构件(方法),因此是其超类的专业​​知识。此外,如果语言允许,派生的类可以覆盖继承的方法。

并非所有语言都支持多种继承。例如,Java允许类实现多个接口,但仅从一个类继承。如果允许多个继承,则层次结构是有向的无环图(或简短的DAG),否则是。层次结构将类作为节点和继承关系作为链接。与不同级别的类别相比,同一级别的类更可能与类关联。该层次结构的水平称为抽像水平

示例(iPhone SDK的简化Objective-C 2.0代码):

@interface UIResponder : NSObject //...
@interface UIView : UIResponder //...
@interface UIScrollView : UIView //...
@interface UITableView : UIScrollView //...

在此示例中,UITAILVIEW是一个UISCrollViewUiviewUiresponderNSOBJECT。

子类的定义

从概念上讲,超类是其子类的超集。例如,一个通用的类层次结构将涉及GraphicObject作为矩形椭圆形的超类,而正方形将是矩形的子类。这些都是集合理论中的子集关系,即,所有正方形都是矩形,但并非所有矩形都是正方形。

一个常见的概念错误是将关系一部分与子类纠正。例如,汽车和卡车都是车辆,将它们建模为车辆类的子类是适当的。但是,将汽车的组件部分建模为子类关系是错误的。例如,汽车是由发动机和车身组成的,但是将发动机或车身作为汽车子类建模是不合适的。

面向对象的建模中,这类关系通常被建模为对象属性。在此示例中,汽车类将具有称为零件的属性。零件将被输入包含一个对象集合,例如车身引擎轮胎等。对象建模语言(例如UML)包括模拟“一部分”和其他关系的各个方面的功能 - 数据,例如对象的基数,输入和输出值的约束等。开发人员工具可以利用此信息来生成对象的基本数据定义以外的其他代码,例如对GET和设置方法进行错误检查。

建模和实施对像类系统的一个重要问题是类是否可以拥有一个或多个超类。在具有实际集合的现实世界中,很少会发现与另一组相交的集合。但是,尽管某些口味和关闭的系统为多个父母在运行时提供了一种能力,但它引入了复杂性,以对象为导向的社区中的许多人认为与首先使用对像类的目标相反。在处理多个超级阶级时,了解哪个课程将负责处理消息可能会变得复杂。如果不经意地使用,此功能可以引入一些相同的系统复杂性,并且歧义类旨在避免。

大多数现代面向对象的语言,例如SmallTalk和Java,都需要在运行时单一继承。对于这些语言,多个继承可能对建模可能有用,但对于实现而言无用。

但是,语义Web应用程序对象确实有多个超类。互联网的波动性需要这种灵活性,并且诸如Web本体语言(OWL)之类的技术标准旨在支持它。

一个类似的问题是,是否可以在运行时修改类层次结构。诸如口味,关闭和SmallTalk之类的语言都作为其元对象协议的一部分支持此功能。由于类本身是一流的对象,因此可以通过向其发送适当的消息来使它们动态更改结构。其他专注于强大键入(例如Java和C ++)的语言不允许在运行时修改类层次结构。语义Web对象具有运行时间更改类的功能。理性类似于允许多个超级类的理由,以至于互联网是如此动态和灵活,以至于需要对层次结构进行动态变化才能管理这种波动性。

班级概念和继承的正交性

尽管通常认为基于班级的语言支持继承,但继承并不是类概念的内在方面。某些语言通常称为“基于对象的语言”,但不支持继承。基于对象的语言的示例包括Visual Basic的早期版本。

在面向对象的分析中

面向对象的分析UML中,两个类之间的关联代表了类或其相应实例之间的协作。联想有方向;例如,两个类之间的双向关联表明两个类都知道它们的关系。协会可以根据其名称或目的标记。

关联角色结束了关联,并描述了相应类的作用。例如,“订阅者”角色描述了班级实例“人”的实例参与“订阅“与班级”的关联。此外,“杂志”在同一协会中具有“订阅杂志”角色。关联角色多重性描述了多少实例与关联的另一类的每个实例相对应。常见的多重性是“ 0..1”,“ 1..1”,“ 1 ..*”和“ 0 ..*”,其中“*”指定任何数量的实例。

班级分类学

类别有许多类别,其中一些重叠。

抽象和混凝土

在支持继承的语言中,抽像类抽象基类ABC )是无法直接实例化的类。相比之下,混凝土类可以直接实例化的类。抽像类的实例化只能通过混凝土类间接发生。

抽像类要幺明确地标记为这样的标签,要幺简单地指定抽象方法(或虚拟方法)。摘要类可以提供某些方法的实现,还可以通过签名指定虚拟方法,这些签名将由抽像类的直接或间接后代实现。在可以实例化摘自抽像类的类之前,其父类的所有抽象方法必须由派生链中的某些类实现。

大多数面向对象的编程语言允许程序员指定哪些类被视为抽象,并且不允许这些类实例化。例如,在JavaC#PHP中,使用了关键字摘要。在C ++中,一个抽像类是该语言中至少由适当语法给出的至少一个抽象方法的类(C ++条例中的纯虚拟函数)。

仅由虚拟方法组成的类称为C ++中的纯抽象基类(或纯ABC ),并且该语言用户也称为接口。其他语言,尤其是Java和C#,通过该语言中的关键字来支持称为接口的抽像类的变体。在这些语言中,不允许多个继承,但是类可以实现多个接口。这样的类只能包含抽象的公开访问方法。

本地和内部

在某些语言中,可以在全球范围以外的其他范围内声明类。此类类别有多种类型。

内部类是在另一类中定义的类。内部类别及其包含类之间的关系也可以视为另一种类型的类关联。内部类通常既不与封闭类的实例相关联,也不与其封闭类实例化。根据语言,可能会或可能无法从封闭类外部参考类。相关概念是内部类型,也称为内部数据类型嵌套类型,这是内部类概念的概括。 C ++是支持内部类和内部类型的语言的一个示例(通过Typedef声明)。

另一种类型是本地类,它是在过程或函数中定义的类。这将对类名称的引用限制为在声明类的范围内。根据语言的语义规则,与非本地类别相比,本地类可能还有其他限制。一种常见的限制是禁止本地类方法访问封闭功能的本地变量。例如,在C ++中,本地类可能是指在其封闭功能中声明的静态变量,但可能无法访问该函数的自动变量

元类

Metaclasses是类是类的类。元类描述了类集合的共同结构,可以实现设计模式或描述特定类型的类。元类通常用于描述框架

在某些语言中,例如PythonRubySmalltalk ,类也是对象。因此,每个类都是内置在语言中的独特元类的实例。通用的LISP对象系统(CLOS)提供了实现这些类和元类的元对象协议(MOP)。

不可分析(或密封)

不可分类的类或密封的类允许程序员设计类和层次结构的类别,在层次结构中的某个级别,禁止进一步的派生(也可以将独立类别指定为不可分类的类别,以防止任何层次结构的形成) 。将其与抽象类相比,这意味着,鼓励和需要推导才能使用。不可分类的类是隐式具体的

通过将课程声明为sealed在C#或AS中final在Java或PHP中。例如,爪哇String班级被指定为最终

不可分类的类可能允许编译器(以编译语言)执行不可用于次级分类的优化。

打开课

开放式课程是可以更改的课程。通常,客户无法更改可执行程序。开发人员通常可以更改某些类,但通常无法更改标准或内置的类别。在Ruby中,所有课程都是开放的。在Python中,可以在运行时创建类,然后可以修改所有类。 Objective-C类别允许程序员将方法添加到现有类中,而无需重新编译该类,甚至可以访问其源代码。

混合物

某些语言对Mixins有特殊的支持,尽管在任何具有多种继承的语言中,Mixin只是一个不代表IS-A-A型关系的类。混合素通常用于在多个类中添加相同的方法。例如,当不共享共同父母的类FileReaderWebPages Craper中包含类unicodeconversionmixin时,可能会提供一种称为unicode_to_ascii的方法。

部分的

在支持该功能的语言中,部分类是一个类,其定义可以在单个源代码文件中或跨多个文件中分为多个部分。这些零件在编译时合并,使编译器输出与非派对类相同。

引入部分类别的主要动机是促进代码生成器(例如视觉设计人员)的实现。否则,开发代码生成器是一个挑战或妥协,这些代码生成器可以在开发人员写的代码中交错时可以管理生成的代码。使用部分类,代码生成器可以在文件中处理单独的文件或粗粒的部分类别,因此可以通过大量解析,提高编译器效率并消除损坏开发人员代码的潜在风险,从而通过复杂地插入生成的代码来缓解。在简单的部分类别的实现中,编译器可以执行一个“统一”部分类别的所有部分的预编译阶段。然后,汇编可以照常进行。

部分阶级功能的其他好处和影响包括:

  • 以唯一的方式启用类的界面和实现代码。
  • 简化编辑器中大型课程的导航。
  • 以类似于面向方面的编程,但不使用任何额外工具的方式,实现关注点的分离
  • 使多个开发人员可以同时处理单个类,而无需在以后将单个代码合并到一个文件中。

在相当长的时间里,部分类别以班级扩展名的名义存在于Smalltalk中。随着.NET框架2的到来, Microsoft引入了部分类别,并在C# 2.0和Visual Basic 2005中支持。 Winrt还支持部分课程。

无害的

无害的类允许程序员将在没有类实例的情况下在运行时访问的每个类字段和方法组合在一起。确实,禁止对这种类别的实例化。

例如,在C#中,标记为“静态”的类不能实例化,只能具有静态成员(字段,方法,其他),可能没有实例构造函数,并且已密封

未命名

未命名的类匿名类是一个不绑定到定义上名称或标识符的类。这类似于命名与未命名函数

好处

将软件组织成对像类的好处分为三类:

  • 快速发展
  • 易于维护
  • 重复使用代码和设计

对像类促进快速开发,因为它们减少了代码和用户之间的语义差距。系统分析师可以使用基本相同的词汇,谈论帐户,客户,账单等与开发人员和用户进行交谈。对像类通常会促进快速开发,因为大多数面向对象的环境都配有强大的调试和测试工具。可以在运行时间检查课程实例,以验证系统是否按预期执行。同样,大多数面向对象的环境没有获得核心内存的转储,而是解释了调试功能,以便开发人员可以准确分析程序中的位置,并且可以看到哪些方法被调用到哪些参数以及参数。

对像类可通过封装来易于维护。当开发人员需要更改对象的行为时,他们可以将更改定位到该对象及其组件部分。这减少了维护增强的不必要副作用的潜力。

软件重复使用也是使用对像类的主要好处。类通过继承和接口促进重复使用。当需要新的行为时,通常可以通过创建一个新类,并让该类继承其超级阶级的默认行为和数据,然后相应地量身定制行为或数据的某些方面。当另一个对像想调用(而不是创建一种新的一种对像类)时,通过接口(也称为方法)重复使用。这种重复使用的方法删除了许多常见错误,当一个程序从另一种程序中重新使用代码时,可以进入软件。

运行时表示

作为数据类型,类通常被视为编译时构建体。语言或库还可以支持代表有关类的运行时信息的原型工厂元对象,甚至代表元数据,该元数据提供了对反射设施的访问以及在运行时操纵数据结构格式的能力。许多语言将有关类的运行时类型信息与类区分开,因为在运行时不需要信息。某些动态语言在运行时和编译时构建体之间没有严格的区分,因此可能不会区分元主体和类。

例如,如果人类是代表班级人的元主体,那么可以使用人类元主体的设施来创建班级人的实例。

也可以看看