OO面向对象设计理念在编程领域都是通用的(如Java,C++开发)
面向对象的三大特征
1.封装:重复的代码打包起来一起用
2.抽象:原子性组合
3.多态:多样性,同样的方法不同的实现
ABAP的面向对象主要由几种元素组成。
1.作用范围(全局类SE24,本地类LCL(Local Class))
2.属性(静态属性,实例属性)及其作用范围(Public Protected Private)
3.方法(静态方法,实例方法)及其作用范围(Public Protected Private)
4.接口 完成一些事情的高度抽象
5.友元 (暂时用的较少)
6.事件 某些指定的操作可以作为事件,触发事件时要做什么(ASOP)
7.类型 (暂时用的较少)
8.别名 (暂时用的较少)
访问属性:public公有的/private私有的/protected受保护的 method
关于abstract 和interface的区别:
I. abstract是为了子类和继承类而做
II. interface 是为了外部调用(飞机和鸟都能飞行,但是他不是同类,这个例子蛮好的,让人容易理解)
- base class:负责公共部分逻辑的代码
- sub class:负责各自特殊逻辑。公共逻辑在base class完成,不同部分通过sub class redefine来实施
OO面向对象编程--定义、实现和使用类
- Define, implement and use simple class(定义、实现和使用简单类):指的是在编程中创建一个基本的类,实现其功能,并在程序中使用它。
类是对象的模板。反过来说,也可以说对象的类型就是它的类。类是对对象的抽象描述,可以将其视为构建对象的一组指令。对象的属性由类的成分定义,这些成分描述了对象的状态和行为。
本地类和全局类
在ABAP对象中,类可以被声明为全局的或局部的。在ABAP工作台的类构建器(事务SE24)中定义全局类和接口。它们被集中存储在R/3存储库的类库中的类池中。R/3系统中的所有ABAP程序都可以访问全局类。局部类是在ABAP程序内部定义的。局部类和接口只能在定义它们的程序中使用。在ABAP程序中使用一个类时,系统首先会搜索具有指定名称的局部类。如果没有找到,它会寻找全局类。除了可见性问题之外,使用全局类和使用局部类没有区别。然而,在设计局部类和全局类的方式上存在显著差异。如果定义的局部类只在单个程序中使用,通常只需要定义外部可见的组件,以使其适应该程序。另一方面,全局类必须能够在任何地方使用。这意味着在定义全局类的接口时,必须遵守某些限制,因为系统必须能够保证任何使用全局类对象的程序都能识别每个接口参数的数据类型。
定义局部类
局部类由ABAP源代码组成,被ABAP语句CLASS ... ENDCLASS包围。一个完整的类定义包括声明部分和(如果需要的话)实现部分。类<class>的声明部分是一个语句块:
CLASS <class> DEFINITION.
...
ENDCLASS.
它包含了类的所有组件(属性、方法、事件)的声明。当定义局部类时,声明部分属于全局程序数据。因此,应该将其放在程序的开头。
类的结构
以下陈述定义了类的结构:
类包含组件
每个组件被分配到一个可见性部分
类实现方法
以下部分更详细地描述了类的结构。
类组件
类组件构成了类的内容。所有组件都在类的声明部分声明。组件定义了类中对象的属性。当定义类时,每个组件都被分配到三个可见性部分之一,这些部分定义了类的外部接口。类的所有组件在类内部都是可见的。所有组件都在同一个命名空间中。这意味着类的所有组件必须在类内部具有唯一的名字。
类中有两种类型的组件——那些为每个对象单独存在的组件,以及那些不论实例数量如何,整个类只存在一次的组件。特定于实例的组件被称为实例组件。不特定于实例的组件称为静态组件。在ABAP对象中,类可以定义以下组件。由于可以在类中声明的所有组件也可以在接口中声明,以下描述同样适用于接口。
属性
属性是类内部的数据字段,可以具有任何ABAP数据类型。对象的状态由其属性的内容决定。属性的一种类型是引用变量。引用变量允许你创建和引用对象。引用变量可以在类中定义,使你能够在类内部访问对象。
实例属性
实例属性的内容定义了对象的特定实例状态。使用DATA语句来声明它们。
静态属性
静态属性的内容定义了对类的所有实例都有效的类状态。静态属性对每个类只存在一次。使用CLASS-DATA语句来声明它们。它们在整个类的运行时都是可访问的。类中的所有对象都可以访问其静态属性。如果在一个对象中更改了静态属性,这个更改在类中的所有其他对象中都是可见的。
方法
方法是类内部的程序,定义了对象的行为。它们可以访问类的所有属性。这使得它们能够改变对象的数据内容。它们还具有参数接口,用户在调用它们时可以向它们提供值,并从它们那里接收值。类的私有属性只能通过同一类中的方法来改变。
方法的定义和参数接口与函数模块类似。你在类的声明部分定义一个方法<meth>,并在实现部分使用以下处理块来实现它:
METHOD <meth> .
...
ENDMETHOD.
可以像在其他ABAP程序(子程序和函数模块)中一样,在方法中声明局部数据类型和对象。使用CALL METHOD语句来调用方法。
实例方法
使用METHODS语句声明实例方法。它们可以访问类的所有属性,并能触发类的所有事件。
静态方法
使用CLASS-METHODS语句声明静态方法。它们只能访问静态属性并触发静态事件。
特殊方法
除了使用CALL METHOD调用的正常方法外,还有两种特殊方法,分别称为CONSTRUCTOR和CLASS_CONSTRUCTOR,它们在创建一个对象(CONSTRUCTOR)或首次访问类的组件(CLASS_CONSTRUCTOR)时自动被调用。
可见性部分
可以将类的声明部分划分为最多三个可见性区域:
CLASS <class> DEFINITION.
PUBLIC SECTION.
...
PROTECTED SECTION.
...
PRIVATE SECTION.
...
ENDCLASS.
这些区域定义了类组件的外部可见性,即类与其用户之间的接口。类的每个组件都必须被分配到一个可见性部分。
公共部分
在公共部分声明的所有组件对类的所有用户以及继承自它的类的方法是可访问的。类的公共组件构成了类与其用户之间的接口。
受保护部分
在受保护部分声明的所有组件对类及其子类的所有方法都是可访问的。受保护组件在类与其子类之间形成了一种特殊的接口。
私有部分
在私有部分声明的组件仅在同一个类的方法中可见。私有组件不是类外部接口的一部分。
封装
三个可见性区域是面向对象编程中的一个重要特性——封装的基础。当定义一个类时,应该非常小心地设计公共组件,并尽可能地声明最少的公共组件。一旦发布了一个类,全局类的公共组件就不能再被改变。
例如,公共属性是外部可见的,并且构成了对象与其用户之间的接口的一部分。如果想完全封装一个对象的状态,就不能声明任何公共属性。除了定义属性的可见性之外,还可以使用READ-ONLY修饰符来防止它被更改。
如果在类的声明部分声明了方法,还需要为它编写一个实现部分。这由另一个语句块组成:
CLASS <class> IMPLEMENTATION.
...
ENDCLASS.
类的实现部分包含了类所有方法的实现。局部类的实现部分是一个处理块。因此,不属于处理块的后续编码是不可使用的。
在实现方法时,不需要指定任何接口参数,因为这些已经在方法声明中定义了。方法的接口参数在方法实现中表现得像局部变量。可以使用DATA语句在方法内部定义额外的局部变量。
与函数模块一样,可以使用RAISE <exception>和MESSAGE RAISING语句来处理错误情况。
当实现一个静态方法时,请记住它只能操作类的静态属性。实例方法可以操作静态和实例属性。
以下ABAP代码定义了一个名为`lcl_airplane`的类,用于表示飞机。下面是对代码的逐行注释说明:
***INCLUDE ZBC404_HF_LCL_AIRPLANE1 .
******************************************
* Definition part
* 定义部分
******************************************
CLASS lcl_airplane DEFINITION.和使用类
* 定义一个名为lcl_airplane的类。
*--------------------------------
* Public section
* 公共部分
*--------------------------------
PUBLIC SECTION.
* 公共部分声明,这些成员可以被其他类访问。
TYPES: t_name(25) TYPE c.
* 定义一个类型t_name,用于存储长度为25个字符的字符串。
METHODS:
constructor,
set_attributes IMPORTING p_name TYPE t_name
p_planetype TYPE saplane-planetype,
display_attributes,
display_n_o_airplanes.
* 定义方法:构造方法(constructor),用于创建类的实例时初始化属性。
* set_attributes方法,用于设置飞机的名称和类型。
* display_attributes方法,用于显示飞机的属性。
* display_n_o_airplanes方法,用于显示创建的飞机实例总数。
*--------------------------------
* Private section
* 私有部分
*--------------------------------
PRIVATE SECTION.
* 私有部分声明,这些成员只能被类的其他方法访问。
* Private attributes
DATA: name(25) TYPE c,
planetype TYPE saplane-planetype.
* 声明私有属性name和planetype,分别用于存储飞机名称和类型。
* Private static attribute
CLASS-DATA n_o_airplanes TYPE i.
* 声明一个类数据成员n_o_airplanes,用于跟踪创建的飞机实例总数。
ENDCLASS.
******************************************
* Implementation part
* 实现部分
******************************************
CLASS lcl_airplane IMPLEMENTATION.
* 实现部分,定义类的方法的具体行为。
METHOD constructor.
* 构造方法的实现,用于初始化飞机的名称和类型。
n_o_airplanes = n_o_airplanes + 1.
* 每次创建新实例时,增加飞机实例的总数。
ENDMETHOD.
METHOD set_attributes.
* set_attributes方法的实现,用于设置飞机的名称和类型。
name = p_name.
planetype = p_planetype.
ENDMETHOD.
METHOD display_attributes.
* display_attributes方法的实现,用于显示飞机的名称和类型。
WRITE:/ 'Name:', name, 'Planetype:', planetype.
ENDMETHOD.
METHOD display_n_o_airplanes.
* display_n_o_airplanes方法的实现,用于显示创建的飞机实例总数。
WRITE: / 'No. planes:', n_o_airplanes.
ENDMETHOD.
ENDCLASS."
* 实现部分结束。
这个类定义了一个飞机对象,包括它的属性(名称和类型)和一个类数据成员(飞机实例总数)。类中的方法允许创建飞机实例、设置其属性、显示其属性以及显示所有创建的飞机实例总数。构造方法在创建新实例时自动调用,而set_attributes方法提供了一种在创建后设置属性的方式。
以下ABAP代码是一个报表程序(REPORT),用于创建和操作lcl_airplane类的对象。以下是对代码的逐行注释说明:
REPORT ZBC404_HF_MAINTAIN_AIRPLANES.
* 报告的名称是ZBC404_HF_MAINTAIN_AIRPLANES。
INCLUDE zbc404_hf_lcl_airplane1.
* 包含名为zbc404_hf_lcl_airplane1的本地对象文件,
* 这个文件包含了lcl_airplane类的实现。
* Create reference to class lcl_airplane
* 创建对lcl_airplane类对象的引用。
DATA: airplane1 TYPE REF TO lcl_airplane,
airplane2 TYPE REF TO lcl_airplane.
START-OF-SELECTION.
* 报告的开始部分。
* Create instance
* 创建lcl_airplane类的实例。
CREATE OBJECT airplane1.
* 调用静态方法display_n_o_airplanes来显示当前创建的飞机实例总数。
CALL METHOD: airplane1->display_n_o_airplanes.
* 创建第二个飞机实例。
CREATE OBJECT airplane2.
* Setting attributes using a method with parameters
* 使用带参数的方法set_attributes来设置飞机的属性。
CALL METHOD airplane1->set_attributes
EXPORTING p_name = 'Kurt'
p_planetype = 'MD80'.
END-OF-SELECTION.
* 报告的结束部分。
* Using methods
* 使用飞机对象的方法。
CALL METHOD: airplane1->display_n_o_airplanes, " 显示飞机实例总数
airplane1->display_attributes. " 显示飞机的名称和类型
这段代码首先包含了lcl_airplane类的实现,然后创建了两个飞机对象的引用。接着,它创建了第一个飞机实例并显示了当前的飞机实例总数。然后,它创建了第二个飞机实例,并使用set_attributes方法(这个在原始的lcl_airplane类定义中没有提供,是在zbc404_hf_lcl_airplane1中定义的)来设置第一个飞机实例的名称和类型。最后,它再次显示了飞机实例总数和第一个飞机实例的属性。
程序执行结果:
ABAP OO面向对象的理由
几年前SAP BASIS 4.6为ABAP扩展了OO功能,这是很多传统的ABAP程序员陷入困境。首先对于ABAP程序员来说它们终于可以通过OO这种开发方法得潜在价值来提高开发效率和所开发程序的可维护性了。然而一直以来习惯于面向过程编程的ABAPER迅速的转向OO模式也不是那么容易,很多人还是坚持使用传统的面向过程的开发模式,他们觉得不用OO也可以得到他们想要的。这篇文章的主要目的便是让你明白OO能给你带来什么以及如何转型为OO模式。
很多关于ABAP OO方面的文章要么过于抽象,要么举的例子过于简单对于实际应用无关痛痒。本篇文章力图通过简单而又切合实际的例子通过面向过程和面向对象两种模式的比较来说明OO的优点。
1. 首先ABAP OO具有更高层次的数据封装性,从而增强了程序的可维护性和稳定性。在面向过程的模式里,一个程序的全局变量区包含着许多不相干的变量,这些变量在一个区域里交织在一起,这样的话这个程序的某个功鞥对程序状态的改变并不为程序中另外一个功能所知。为保持整个程序的稳定性需要大量的约束和维护成本。而对于OO模式,状态是保存在对象上的。对象可以把内部和外部数据和方法分隔开,以保证功能(OO中的方法)只能改变与它相关的数据,对象的属性是不会被改变的,从而保证了其在应用程序中的稳定性。
2. ABAP OO可以实现一个类的多个实例。(对象是由数据以及操作数据的方法组成的。),每一个对象都有自己在类中定义的属性值,并且可以通过本身的方法来改变本身的状态。这就意味着开发者无需为每个对象建立数据和方法之间的联系也无需手工管理每个对象的生命周期。在面向过程的方法中没有多实例的概念,数据和功能是相互分离的。你使用的是无状态的功能,并且每次都需要通过传递参数来初始化它,并且手工将其占有的内存清除。
3. ABAP OBJECT通过继承进一步增强了程序代码的可重用性,这正是面向对象方法的一个重要方面。通过这个特点你可以重复利用所继承类的部分或者所有方法,只需编写类本身所特有的方法,这样会减少为每个类编写的方法,从而增强了程序的可维护性。而对于面向过程的编程来说,你将会受制于all-or-nothing的状况,你要么调用所有的部分要么就建立新的。
4. ABAP OO是你可以通过接口(interface)来调用对象的业务逻辑,而不是直接去使用对象,这样就避免了你需要详细了解每一个对象的特定功能。这样也避免了你在需要修改某项功能的时候无需修改接口(interface)的内容,这也为标准程序的修改提供了新的途径,即BADI。面向过程的编程并没有这种提供对立的接口去联系某个对象的概念—接口只是通过form或function module的参数实现的。
5. ABAP OO非常容易与事件驱动的模式结合在一块。不同的应用之间是通过发布与预定松散的耦合在一起的,调用者和被调用者之间并不是被动的绑定在一起的。这种方式与面向过程的方法相比更进一步的增强了灵活性,他们的绑定是紧密地,程序的执行流程也是预定义好的。
当然,这些好处都需要你今后再开发中是用OO的方法来开发才能得到。然而重新建模你应用程序的开发模式是令人沮丧和头疼的,我们保证这种情况不会出现,读完这篇文章我们相信你会得到同样的结论。当然,没有说我们需要重新建模已有的应用,我们只是希望你在今后的开发过程中最后考虑一下OO的模式。
如果你坚持认为面向过程的模式对你来说已经足够了,也请接着往下读。通过使用对象的方法来代替form和function module你仍然可以增强你的ABAP程序。
1. ABAP OO更加明确所以更易于使用。例如在使用ABAP OO你的程序的执行流程不再是由运行时隐含的控制。这样你就可以自己去设计程序所执行的流程了而不必像面向过程那样去了解和服从外部控制机制(即报表和dialog screen的事件)。
2. ABAP OO具有更加清晰的语法和语义规则,比如一些容易出错的过时的语句在ABAP OO类中已经明确不能再使用。而在面向过程的程序中这些语法仍然被支持,顶多就是在关键的时候给你报个警告信息。
3. ABAP的一些新技术只能通过ABAP OO来实现。例如所有新的GUI的概念比如SAP Control Framework和BSP只有通过ABAP OO的方式才能够实现。而对于面向过程的ABAP你就只能使用传统的screen和list processing了。
所以即便你在未来的开发中还不准备完全的转型为OO,你可以使用的OO技术来减少错误的隐患以及增强代码的可维护性。下面的部分将会阐述如何达到这一目的。
那么面向过程的ABAP和ABAP OO究竟是孰优孰劣?下面的部分将逐一进行论述。首先先了解以下ABAP OO的年代史。
1. SAP Basis Release 4.5发布了ABAP OO的一个版本,引入了类接口的概念,并可以通过类来创建对象(实例化类)。
2. SAP Basis Release 4.6发布了ABAP OO的完全版本,引入了OO方式的重要概念继承(inheritance),可以通过多个接口来建立一个复合的接口。
3. SAP WEB APPLICATION SERVER 6.10/6.20 SAP basis的下一代版本,在类之间引入了friendship的概念。并引入了对象服务(object service)可以把对象存储在数据库中。
4. SAP WEB APPLICATION SERVER 6.40引入了共享对象(Shared Objects)的概念,即允许在应用服务器的共享内存中存储对象。这样在这个服务器中的任何一个程序都可以访问它。
几个关键点
n ABAP OO是ABAP编程语言的扩展
n ABAP OO 是向下兼容的
n SAP发布ABAP OO是为了进一步增强代码的可重用性
n 随着ABAP OO的发布,ABAP运行时支持面向过程和面向对象两种模式
对于面向过程的模式,程序的运行通常是从screen的dialog module或selection screen的start-of-selection事件开始的。你在这些处理模块中操作全局变量来实现需求的功能。你可以通过内部的form和外部的function module来实现程序的模块化。这些过程除了可以操作全局变量外还可以具备内部的本地变量来协助实现内部的一些特定功能。
对于OO编程,唯一的结构单位就是类,这里类的实例对象取代了全局变量。这些对象封装了应用的状态和行为。应用的状态是用属性来代表的它取代了面向过程中的全局变量。应用的行为是通过方法来实现的,他们用来改变应用的属性或者调用其它对象的方法。
n ABAP OO支持OO和面向过程的两种模式,这样在传统的ABAP程序(比如报表,模块池,功能池等)中你也可以使用ABAP对象类。在这些程序里你也就可以使用基于面向对象的新技术了,比如一些用户界面,避免了要想使用这些新技术必须重新编写程序。
n 目前大部分程序都是面向过程和ABAP OO 的混合体如下图所示:
左边是纯粹的ABAP OO模式,所有的代码都封装在类中。你的应用中并不直接触presentation layer(SAP Gui , Business Server Pages etc.),persistent data(database table,system file)。他们是通过类库中的相应服务类来提供的。比如SAP Control Framework ,Desktop Office Integration, and Business Pages提供了与表现层的接口。对于SAP Web Application 6.10以上提供了与数据库层接口的服务对象。
虽然纯粹的OO模式技术上是可行的,但是现实中还存在着大量的两种模式的混合体如右面的图所示。ABAP 对象和面向过程的技术同时应用,调用常用的功能模块,调用屏幕或者直接访问数据库等在对象中都存在。混合的模式及利用了新技术又保护了已投入的成本。
两种模式的选择
正如本文所述,OO的模式是最佳的选择,除非在绝对必要的情况下才使用面向过程的模式。比如传统的screen programming在OO中是不支持的,附录中会进一步阐释如何实现screen与OO的结合。
OO面向对象编程--定义、实现和使用类
第一个类程序
如果你有过Java或者别的基础会比较好理解一点。类的静态方法是在类上的,实例只是一段带有实例数据指向类的数据。执行静态方法和改变静态属性的时候会找到类的内存地址再使用静态方法,调用对应的属性。静态属性在一次载入的时候是通用的(即一个Gui窗口加载一个类,在此GUI窗口同一个类的静态属性共享)。写起来好像有些复杂,不妨用一个例子来说明。
*定义类
CLASS ZCL_PERSON DEFINITION
*定义公开的构造器(单例模式可不公开)
CREATE PUBLIC .
*Public的属性或者方法
PUBLIC SECTION.
DATA MV_NAME TYPE CHAR10.
DATA MV_ID TYPE CHAR15.
*Public的构造器
METHODS CONSTRUCTOR
IMPORTING
VALUE(ID) TYPE CHAR15 OPTIONAL .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
*类方法的具体实现
CLASS ZCL_PERSON IMPLEMENTATION.
METHOD CONSTRUCTOR.
MV_ID = ID.
ENDMETHOD.
ENDCLASS.
*事件执行
START-OF-SELECTION.
DATA ZCL_PERSON TYPE REF TO ZCL_PERSON.
ZCL_PERSON = NEW #( ID = '1').
WRITE ZCL_PERSON->MV_ID.
这是一个有ID和Name的Person类,通过构造器可以创建一个具有ID和空姓名的实例对象。本例是创建了一个ID为1但没有名字的实例对象,write时显示为1。那这个对象有什么实际作用呢,相当于一个物料挂着一个物料号及其物料描述,这就把Person的概念转化过来了。