ABAP OO的八个理由.docx
- 文档编号:30207730
- 上传时间:2023-08-07
- 格式:DOCX
- 页数:38
- 大小:153.70KB
ABAP OO的八个理由.docx
《ABAP OO的八个理由.docx》由会员分享,可在线阅读,更多相关《ABAP OO的八个理由.docx(38页珍藏版)》请在冰豆网上搜索。
ABAPOO的八个理由
ABAPOO的八大理由
几年前SAPBASIS4.6为ABAP扩展了OO功能,这是很多传统的ABAP程序员陷入困境。
首先对于ABAP程序员来说它们终于可以通过OO这种开发方法得潜在价值来提高开发效率和所开发程序的可维护性了。
然而一直以来习惯于面向过程编程的ABAPER迅速的转向OO模式也不是那么容易,很多人还是坚持使用传统的面向过程的开发模式,他们觉得不用OO也可以得到他们想要的。
这篇文章的主要目的便是让你明白OO能给你带来什么以及如何转型为OO模式。
很多关于ABAPOO方面的文章要么过于抽象,要么举的例子过于简单对于实际应用无关痛痒。
本篇文章力图通过简单而又切合实际的例子通过面向过程和面向对象两种模式的比较来说明OO的优点。
1.首先ABAPOO具有更高层次的数据封装性,从而增强了程序的可维护性和稳定性。
在面向过程的模式里,一个程序的全局变量区包含着许多不相干的变量,这些变量在一个区域里交织在一起,这样的话这个程序的某个功能对程序状态的改变并不为程序中另外一个功能所知。
为保持整个程序的稳定性需要大量的约束和维护成本。
而对于OO模式,状态是保存在对象上的。
对象可以把内部和外部数据和方法分隔开,以保证功能(OO中的方法)只能改变与它相关的数据,对象的属性是不会被改变的,从而保证了其在应用程序中的稳定性。
2.ABAPOO可以实现一个类的多个实例。
(对象是由数据以及操作数据的方法组成的。
),每一个对象都有自己在类中定义的属性值,并且可以通过本身的方法来改变本身的状态。
这就意味着开发者无需为每个对象建立数据和方法之间的联系也无需手工管理每个对象的生命周期。
在面向过程的方法中没有多实例的概念,数据和功能是相互分离的。
你使用的是无状态的功能,并且每次都需要通过传递参数来初始化它,并且手工将其占有的内存清除。
3.ABAPOBJECT通过继承进一步增强了程序代码的可重用性,这正是面向对象方法的一个重要方面。
通过这个特点你可以重复利用所继承类的部分或者所有方法,只需编写类本身所特有的方法,这样会减少为每个类编写的方法,从而增强了程序的可维护性。
而对于面向过程的编程来说,你将会受制于all-or-nothing的状况,你要么调用所有的部分要么就建立新的。
4.ABAPOO是你可以通过接口(interface)来调用对象的业务逻辑,而不是直接去使用对象,这样就避免了你需要详细了解每一个对象的特定功能。
这样也避免了你在需要修改某项功能的时候无需修改接口(interface)的内容,这也为标准程序的修改提供了新的途径,即BADI。
面向过程的编程并没有这种提供对立的接口去联系某个对象的概念—接口只是通过form或functionmodule的参数实现的。
5.ABAPOO非常容易与事件驱动的模式结合在一块。
不同的应用之间是通过发布与预定松散的耦合在一起的,调用者和被调用者之间并不是被动的绑定在一起的。
这种方式与面向过程的方法相比更进一步的增强了灵活性,他们的绑定是紧密地,程序的执行流程也是预定义好的。
当然,这些好处都需要你今后在开发中是用OO的方法来开发才能得到。
然而重新建模你应用程序的开发模式是令人沮丧和头疼的,我们保证这种情况不会出现,读完这篇文章我们相信你会得到同样的结论。
当然,没有说我们需要重新建模已有的应用,我们只是希望你在今后的开发过程中最后考虑一下OO的模式。
如果你坚持认为面向过程的模式对你来说已经足够了,也请接着往下读。
通过使用对象的方法来代替form和functionmodule你仍然可以增强你的ABAP程序。
1.ABAPOO更加明确所以更易于使用。
例如在使用ABAPOO你的程序的执行流程不再是由运行时隐含的控制。
这样你就可以自己去设计程序所执行的流程了而不必像面向过程那样去了解和服从外部控制机制(即报表和dialogscreen的事件)。
2.ABAPOO具有更加清晰的语法和语义规则,比如一些容易出错的过时的语句在ABAPOO类中已经明确不能再使用。
而在面向过程的程序中这些语法仍然被支持,顶多就是在关键的时候给你报个警告信息。
3.ABAP的一些新技术只能通过ABAPOO来实现。
例如所有新的GUI的概念比如SAPControlFramework和BSP只有通过ABAPOO的方式才能够实现。
而对于面向过程的ABAP你就只能使用传统的screen和listprocessing了。
所以即便你在未来的开发中还不准备完全的转型为OO,你可以使用的OO技术来减少错误的隐患以及增强代码的可维护性。
下面的部分将会阐述如何达到这一目的。
那么面向过程的ABAP和ABAPOO究竟是孰优孰劣?
下面的部分将逐一进行论述。
首先先了解以下ABAPOO的年代史。
1.SAPBasisRelease4.5发布了ABAPOO的一个版本,引入了类接口的概念,并可以通过类来创建对象(实例化类)。
2.SAPBasisRelease4.6发布了ABAPOO的完全版本,引入了OO方式的重要概念继承(inheritance),可以通过多个接口来建立一个复合的接口。
3.SAPWEBAPPLICATIONSERVER6.10/6.20SAPbasis的下一代版本,在类之间引入了friendship的概念。
并引入了对象服务(objectservice)可以把对象存储在数据库中。
4.SAPWEBAPPLICATIONSERVER6.40引入了共享对象(SharedObjects)的概念,即允许在应用服务器的共享内存中存储对象。
这样在这个服务器中的任何一个程序都可以访问它。
几个关键点
⏹ABAPOO是ABAP编程语言的扩展
⏹ABAPOO是向下兼容的
⏹SAP发布ABAPOO是为了进一步增强代码的可重用性
⏹随着ABAPOO的发布,ABAP运行时支持面向过程和面向对象两种模式
对于面向过程的模式,程序的运行通常是从screen的dialogmodule或selectionscreen的start-of-selection事件开始的。
你在这些处理模块中操作全局变量来实现需求的功能。
你可以通过内部的form和外部的functionmodule来实现程序的模块化。
这些过程除了可以操作全局变量外还可以具备内部的本地变量来协助实现内部的一些特定功能。
对于OO编程,唯一的结构单位就是类,这里类的实例对象取代了全局变量。
这些对象封装了应用的状态和行为。
应用的状态是用属性来代表的它取代了面向过程中的全局变量。
应用的行为是通过方法来实现的,他们用来改变应用的属性或者调用其它对象的方法。
⏹ABAPOO支持OO和面向过程的两种模式,这样在传统的ABAP程序(比如报表,模块池,功能池等)中你也可以使用ABAP对象类。
在这些程序里你也就可以使用基于面向对象的新技术了,比如一些用户界面,避免了要想使用这些新技术必须重新编写程序。
⏹目前大部分程序都是面向过程和ABAPOO的混合体如下图所示:
左边是纯粹的ABAPOO模式,所有的代码都封装在类中。
你的应用中并不直接触presentationlayer(SAPGui,BusinessServerPagesetc.),persistentdata(databasetable,systemfile)。
他们是通过类库中的相应服务类来提供的。
比如SAPControlFramework,DesktopOfficeIntegration,andBusinessPages提供了与表现层的接口。
对于SAPWebApplication6.10以上提供了与数据库层接口的服务对象。
虽然纯粹的OO模式技术上是可行的,但是现实中还存在着大量的两种模式的混合体如右面的图所示。
ABAP对象和面向过程的技术同时应用,调用常用的功能模块,调用屏幕或者直接访问数据库等在对象中都存在。
混合的模式及利用了新技术又保护了已投入的成本。
两种模式的选择
正如本文所述,OO的模式是最佳的选择,除非在绝对必要的情况下才使用面向过程的模式。
比如传统的screenprogramming在OO中是不支持的,附录中会进一步阐释如何实现screen与OO的结合。
OO编程优于过程编程的五个原因
下面的部分里我们将着重论述OO编程的主要优点,尽管这里所提到的优点与其他的OO语言(JAVAC++)没有什么太大的区别,我们这里着重在ABAPOO与传统的ABAP程序相比而体现出来的优点。
我们将通过实现一个银行账户管理的简单例子来比较两种模式的差别。
原因一:
数据封装
将数据和程序封装在一个组件中将使程序变得容易修改。
不要把一个应用的所有的数据和功能放在同各超长的程序里,你只需要把你的应用通过组件的稳定的接口把需要的部分包进来即可。
如果一个封装的组件有一个好的接口,而这些接口的设计比较完善的话,那么这些接口的内部结构就会被很好的隐蔽起来,对这个部件的内部的修改便不会影响到应用的其他部件。
让我们来看看面向过程和面向对象的方式是如何实现封装的。
面向过程模式的封装
在面向过程模式中有两种变量:
全局变量:
他在程序的全部分声明,可以在程序的任何部分应用。
局部变量:
他在某个过程(formoffunctionmodule)中声明也只能在其中应用。
全局变量的生命周期取决于整个程序的生命周期,局部变量的生命周期取决于该过程的生命周期。
由于生命周期的限制,局部变量不适合于封装,它是过程中为了达到某个目的而使用的辅助变量。
如果没有使用TABLES和COMMONPARTS语句那么程序中所声明的全局变量只能被该程序内部的各个模块调用。
那么这就决定了在面向过程的模式中数据的封装只对程序本身有效。
下面是一个简单的如何通过功能池实现对一个银行帐号的封装。
Function-poolAccount
DATA:
current_accountTYPEaccounts-amount.
Functiondeposit.
*IMPORTINGREFERENCE(amount)TYPEaccount-amount
Current_amount=current_amount+amount.
EndFunction.
Functionwithdraw.
*IMPORTINGREFERENCE(amount)TYPEaccount-amount
*RAISING
*CK_NEGATIVE_AMOUNT
IFcurrent_amount>amount.
Current_amount=current_amount–amount.
ELSE.
RAISEEXCEPTIONTYPECK_NEGATIVE_AMOUNT.
ENDIF.
ENDFUNCTION.
这个模块池封装了银行账户的余额,并且通过功能模块deposit和withdraw来处理银行账户的余额。
虽然单单是一个银行帐号的话这个模块池工作的非常好,但如果是多个银行帐号并要实现银行帐号之间交互的功能,问题就出现了。
为了实现现实世界中多个银行帐号的管理,你不得不为每一个银行帐号建一个唯一名字的模块池和功能模块,这显然是不可取的。
下面是个把所有银行帐号封装在一个模块池中的例子:
FUNCTION-POOLaccounts.
DATA:
account_tableTYPESORTEDTABLEOFaccounts
WITHUNIQUEKEYid.
ENDFUNCTION.
LOAD-OF-PROGRAM.
SELECT*INTOTABLEaccount_table
FROMaccounts.
FUNCTIONdeposit.
*IMPORTING
*REFERENCE(id)TYPEaccounts-id
*REFERENCE(amount)TYPEaccounts-amount
DATA:
account_waTYPEaccounts.
READTABLEaccount_tabe
INTOaccount_wa
WITHTABLEKEYid=id.
Account_wa-amount=account_wa-amount+amount.
MODIFYaccount_tableFROMaccount_wa.
ENDFUNCTION.
FUNCTIONwithdraw.
*IMPORTING
*REFERENCE(id)TYPEaccounts-id
*REFERENCE(amount)TYPEaccounts-amount
*RAISE
*CX_NEGATIVE_AMOUNT
DATA:
account_waTYPEaccounts.
READTABLEaccount_table
INTOaccount_wa
WITHTABLEKEYid=id.
IFaccount-amount>amount.
Account-amount=account-amount–amount.
MODIFYaccount_tableFROMaccount_wa.
ELSE.
RAISEEXCEPTIONTYPECX_NEGATIVE_AMOUNT.
ENDIF.
ENDFUNCTION.
FUNCTIONtransfer.
*IMPORTING
*REFERENCE(id_from)TYPEaccounts-id
*REFERENCE(id_to)TYPEaccounts-id
*REFERENCE(amount)TYPEaccounts-amount
*RAISE
*CX_NEGATIVE_AMOUNT
CALLFUNCTION‘withdraw’
EXPORTINGid=id
Amount=amount.
CALLFUNCTION‘deposit’
EXPORTINGid=id
Amount=amount.
ENDFUNCTION.
这样多个银行帐号就被封装在内表account_tab中,每个银行帐号是通过关键字ID来区分的。
内表account_tab在模块池被调用时被填充以便模块池中的所有功能模块都可以使用它。
功能模块deposit和withdraw通过id来处理一个银行帐号,而新的功能模块transer来实现两个银行帐号之间的转账。
这样我们通过一定的技巧实现了多个银行帐号的管理但是为了区别每个银行帐号却增加了每个功能模块接口参数的复杂性。
面向对象模式的数据封装
OO模式的编程也有两种数据类型,类实例的属性和类方法中的本地变量。
实例属性的生命周期依赖于对象的生命周期,本地变量的生命周期依赖于类方法的生命周期。
所以面向对象的本地变量与面向过程的本地变量的生命周期是一样的。
它是类方法中的辅助变量与数据封装无关。
ABAP对象的数据封装在类这个级别。
可以定义类属性的可见性,它的级别分别是在类内可见,在子类中可见,或者在外部类中可见。
下面的例子演示了如何在类中封装银行账号:
CLASSaccountDEFINITION.
PUBLICSECTION.
METHODS:
constructorIMPORTINGidTYPEaccount-id,
DepositIMPORTINGamountTYPEaccounts-amount,
WithdrawIMPORTINGamountTYPEaccounts-amount
RAISINGcx_negative_amount,,
TransferIMPORTINGamountTYPEaccounts-amount
TargetREFTOaccount
RAISINGcx_negative_amount.
PRIVATESECTION.
DATAamountTYPEaccounts-amount.
ENDCLASS.
CLASSaccountIMPLEMENTATION.
METHODconstructor.
SELECTSINGLEamountINTO(amount)
FROMaccounts
WHEREid=id.
ENDMETHOD.
METHODdeposit.
Me->amount=me->amount+amount.
ENDMETHOD.
METHODwithdraw.
IFme->amount>amount.
Me->amount=me->amount–amount.
ELSE.
RAISEEXCEPTIONTYPEcx_negative_amount.
ENDIF.
ENDMETHOD.
METHODtransfer.
Me->withdraw(amount).
Target->deposit(amount).
ENDMETHOD.
ENDCLASS.
在account类的定义部分可以定义属性和方法的可见性(privatepublic)。
类的定义把功能和数据结合到了一块。
理论上,前面例子的功能池也做到了这点,只不过在不同的数据模块间进行交互的时候就暴露了其局限性,所以功能池只能说是多个功能模块的容器还不能真正的共享数据。
在ABAPOO的类中PUBLIC部分定义了其与外部交互的接口。
在这个例子中描述类状态的属性是私有的,而对外的公共接口是操作这些属性的方法。
这个例子中的类既实现了前面面向过程的第一个例子的简单功能也实现了第二个例子较为复杂的功能。
这个类的deposit,withdraw方法与第一个例子的两个功能模块的作用相同,由于你可以创建一个类的多个对象,所以这个类只需封装一个银行帐号的数据即可,constructor方法用来为不同的银行账号初始化数据。
由于每个对象都有它自己的数据,所以你不需为类添加额外的参数。
最后,不止于此,由于一个对象可以调用另外一个对象的方法,那么不同银行账号之间的交互便有了更加简洁的方案,这例子中transfer功能的实现要比前面第二个例子的实现简单的多。
原因二,实例化
对象的实例化是面向对象编程的一个重要特点。
在ABAPOO中,通过实例化你可以实现一个类的多个变体,这样程序员便可以调用任何一个对象并让他完成相应的功能(比如一个银行帐号的收款存款和窗户的打开关闭等)。
在面向过程的编程中,当程序内容被编译到内存中便隐含着一个实例化的过程,但是这个实例并不能为程序员显式的调用,因为这需要数据和功能的分离,而面向过程的的程序员并不是直接对对象进行操作,而是要告诉一个功能模块需要修改那些数据(比如需要告诉功能模块那个银行帐号要付款或者那个窗户要打开)。
下面我们将详细描述这两种不同的实例化之间的区别。
面向过程的实例化
大多数程序员可能没有意识到其实在面向过程的程序里也有类似于对象实例化的现象。
程序本身在被调入内存的时候其实就是一个隐含的实例化过程,因为程序本身被调用或者程序中的某个过程被其他程序调用了。
模块池隐含实例化的例子
DATA:
id1(8)TYPEn,
Id2(8)TYPEn,
AmntTYPEpDECIMALS2,
Exc_refTYPEREFTOcx_negative_amount,
TextTYPEstring.
TRY.
Id1=…..
Id2=…..
Amnt=…..
CALLFUNCTION‘TRANSFER’
Id_from=id1
Id_to=id2
Amount=amnt.
CATCHcx_negative_amountINTOexc_ref.
Text=exc_ref->get_text().
MESSAGEtextTYPEi.
ENDTRY.
上面的例子演示了调用银行账户模块池的转账功能模块的例子,两个银行账户是通过账户ID来区分的。
如果银行账户模块池中的功能模块是第一次被调用,那么整个模块池就会被调到调用程序的内部session中,模块池的LOAD-OF-PROGRAM事件就会被触发。
对应的事件部分充当着模块池实例化的结构事件。
如果在同一个内部session中,在同一个程序或另一个程序中银行帐号模块池已经被调用,那么下一调用,模块池已经被装载到这个内部session中。
由于程序或者过程只有一次装载到内存的过程,所以功能模块便可以使用其所在模块池中的全局数据。
这种实例化对于数据封装带来了如下缺陷:
●你无法控制实例化的时刻(在一个较大的程序中功能模块在任何时候,任何地点都有可能被调用),这样当一个程序在调用功能模块的时候它将无法知道是否另一个程序或过程改变了模块池的全局数据。
●你无法在内存中删除模块池的实例,因为模块池的实例的生命周期取决于调用主程序的生命周期。
如果你想在模块池的全局数据中使用数据量较大的变量,那么你在使用后需要手工将其清除。
另外模块池一般是许多功能模块的容器,这样就会给内存带来很大的压力,即便是你只是在较短的时间调用很简单的功能模块。
●每个程序在内存中只能创建一个实例。
正如我们在原因1种所看到的,这就限制了我们充分发挥模块池的数据封装性。
还要注意的如果在模块池中,全局变量不能被整取操作,那么将是功能模块对全局变量的使用变得更加危险。
全局变量的状态取决于功能模块的调用顺序,尤其是在一个大程序里,功能模块在不同的地方都有可能调用,这样就导致了模块池全局变量状态的不稳定。
面向对象的实例化
行如其名,在面向对象的模式中,对象是唯一存在的东西。
对象是类的实例,是通过CREATEOBJECT语句来实现的。
ABAP对象不可能被隐含的实例化,你通过指针变量来操作对象。
当然你通过指针所操作的对象不一定是自己创建的,这种现象主要是通过指针操作的对象很有可能是从其他地方传过来的,比如说工厂方法(factorymethod)或者接口参数。
你可以在一个类中创建多个对象每个对象通过属性都有自己的标识符和内容。
对象的生命周期是由使用者来控制的(其他对象,程序或过程等),只要有指针变量指向他,他就在内存中存在。
如果没有指针变量指向对象,这个对象就会被自动的被ABAP内存收集器从内存中删除。
和程序的实例一样,ABAP对象也存储在主程序的内部SESSION中。
在640之后你就可以在应用服务器的SHAREDMEMORY中创建sharedobject了,这样在这应用服务器上运行的任何程序都可以调用它。
下面的例子演示了从银行账号类中产生的两个银行账号对象。
DATA:
account1TYPEREFTOaccount,
Account2TYPEREFTOaccount,
AmntTYPEpDECIMALS2,
Exc_refTYPEREFTOcx_negative_amount,
TextTYPEstring.
CREATEOBJECT:
account1EXPORTINGid=….,
Account2EXPORTINGid=…..
TRY.
Amnt=….
Account1transfer(EXPORTINGamount=amn
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ABAP OO的八个理由 OO 八个 理由