接口 练习
接口 练习
#practice #interface #oo-design
Related Concepts
📌核心模式
| 关键词 | 答案 |
|---|---|
| 接口定义 | INTERFACES ~ 操作符访问,或使用 ALIASES 创建别名 |
| 多接口 | 一个类可以实现多个接口,弥补单继承的限制 |
| 接口多态 | 通过接口引用调用方法,实际执行类实现版本 |
题目1 - [记忆] 接口的定义和特点
请说明ABAP中接口(Interface)的定义方式和主要特点。
📌查看答案
接口定义语法:
1 | INTERFACE lif Printable. |
主要特点:
- 纯抽象:接口只包含声明(方法、属性、事件),不包含任何实现代码
- 无实例化:不能直接创建接口的实例(没有
CREATE OBJECT),只能创建实现该接口的类的对象 - 公有可见:接口中的所有成员默认是公有的,没有可见性分区(无PUBLIC/PROTECTED/PRIVATE)
- 命名约定:局部接口以
LIF_开头,全局接口以IF_开头 - 实现语法:类使用
INTERFACES lif_name来实现接口 - 设计作用:定义行为契约,实现类必须提供所有接口方法的实现
题目2 - [记忆] 接口与类的区别
请列举接口(Interface)和类(Class)之间的五个主要区别。
📌查看答案
| 特性 | 接口(Interface) | 类(Class) |
|---|---|---|
| 实现代码 | 仅包含声明,无实现代码 | 包含完整的实现 |
| 实例化 | 不能创建实例 | 可以创建实例(除抽象类外) |
| 可见性 | 所有成员自动为公有 | 可定义PUBLIC/PROTECTED/PRIVATE |
| 多重性 | 一个类可以实现多个接口 | 一个类只能继承一个父类 |
| 成员类型 | 可包含方法、属性、事件、常量 | 可包含所有类型的成员,包括构造函数 |
| 继承 | 接口可以继承其他接口(复合接口) | 类单继承一个父类 |
| 设计用途 | 定义行为契约(”能做什么”) | 定义对象的结构和行为(”是什么”) |
题目3 - [记忆] 波浪号(~)操作符
请说明ABAP中波浪号(~)操作符在接口上下文中的用途和使用方式。
📌查看答案
波浪号()操作符用于限定接口方法的归属,格式为 `interface_namemethod_name`。
用途场景:
类实现中:在
IMPLEMENTATION部分实现接口方法时1
2
3
4
5
6
7
8
9
10
11CLASS lcl_report DEFINITION.
PUBLIC SECTION.
INTERFACES lif_printable.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD lif_printable~print.
" 实现接口的print方法
WRITE: / 'Printing report'.
ENDMETHOD.
ENDCLASS.外部调用时:通过对象引用调用接口方法时(如果不使用别名)
1
2DATA(lo_report) = NEW lcl_report( ).
lo_report->lif_printable~print( ).
注意:通过接口引用调用时不需要波浪号:
1 | DATA: lo_printable TYPE REF TO lif_printable. |
题目4 - [记忆] 别名(ALIASES)
请说明ABAP中别名(ALIASES)的作用和语法。
📌查看答案
别名的作用:为接口中的方法创建一个更简短、更直观的替代名称,避免使用冗长的
interface~method 语法。语法:
1 | ALIASES alias_name FOR interface_name~method_name. |
示例:
1 | CLASS lcl_report DEFINITION. |
使用别名后:
1 | DATA(lo_report) = NEW lcl_report( ). |
优点:
- 简化代码,提高可读性
- 隐藏接口名称的实现细节
- 当类实现多个接口时,别名可以消除同名方法的歧义
题目5 - [记忆] 接口引用与多态
请说明如何通过接口引用实现多态。
📌查看答案
接口引用实现多态的方式:
声明接口引用变量:
1
DATA: lo_serializable TYPE REF TO lif_serializable.
将实现类的对象赋给接口引用(类似向上转换):
1
2lo_serializable = NEW lcl_order( ). " lcl_order 实现了 lif_serializable
lo_serializable = NEW lcl_product( ). " lcl_product 也实现了 lif_serializable通过接口引用调用方法:
1
lo_serializable->to_json( ).
实际执行的是具体类(lcl_order 或 lcl_product)的实现版本
优势:
- 调用者只需要知道接口定义,不需要知道具体的实现类
- 可以在运行时替换不同的实现,符合”依赖倒置原则”
- 可以创建接口引用的内表,统一管理不同类型的对象
1
2
3
4
5DATA: lt_objects TYPE TABLE OF REF TO lif_serializable.
" 可以包含 lcl_order、lcl_product 等各种实现类的对象
LOOP AT lt_objects INTO DATA(lo_obj).
lo_obj->to_json( ). " 多态调用
ENDLOOP.
题目6 - [应用] 实现多个接口
请编写一个类
lcl_document,同时实现两个接口lif_printable(含方法lif_saveable(含方法save)。要求使用别名简化方法调用。
📌查看答案
1 | " 接口定义 |
使用方式:
1 | " 通过别名调用 |
题目7 - [应用] 接口多态应用场景
系统中有三种日志记录器:文件日志(
lcl_file_logger)、数据库日志(lcl_db_logger)和邮件日志(lcl_email_logger)。请设计一个接口使它们可以互换使用,并编写调用代码在运行时选择不同的日志记录器。
📌查看答案
接口设计:
1 | INTERFACE lif_logger. |
实现类(示例一个,其余类似):
1 | CLASS lcl_file_logger DEFINITION. |
运行时选择(多态调用):
1 | DATA: lo_logger TYPE REF TO lif_logger. |
设计优势:
- 新增日志记录器时只需实现接口,无需修改调用代码
- 运行时可灵活切换实现
- 符合开闭原则和依赖倒置原则
题目8 - [分析] 接口vs继承的设计选择
项目中需要设计一个支付系统。需求如下:
- 有多种支付方式:信用卡、支付宝、微信支付、银行转账
- 某些支付方式支持退款(如信用卡、支付宝),某些不支持(如某些银行转账)
- 某些支付方式支持分期付款(如信用卡),某些不支持
请分析:应该使用继承还是接口来设计?请给出你的设计方案和理由。
📌查看答案
分析:接口优于继承
理由:
- 支付方式之间没有”is-a”层次关系:信用卡、支付宝等都是独立的支付手段,不存在谁是谁的子类的层次关系
- 行为组合而非层次继承:退款和分期是独立的能力(行为),不是类层次结构
- 灵活性:同一个支付方式可能有多个独立的能力组合
推荐设计方案(基于接口):
1 | " 基础支付接口(所有支付方式必须实现) |
实现示例:
1 | " 信用卡:支付 + 退款 + 分期 |
调用方式:
1 | " 所有支付方式统一处理 |
设计原则总结:
- “是什么”(身份)用继承
- “能做什么”(能力/行为)用接口
- 支付方式的核心是”行为能力”的组合,因此接口是最合适的设计选择
📌模式总结
| 关键词 | 答案 |
|---|---|
| 接口定义 | INTERFACE … ENDINTERFACE,纯声明无实现,成员默认公有 |
| 接口 vs 类 | 接口无实现、无实例、可多实现;类有实现、可实例化、单继承 |
| ~操作符 | interface~method 格式,在实现和通过对象引用调用时使用 |
| ALIASES | 为 interface~method 创建简短别名,提高可读性 |
| 接口多态 | 接口引用指向不同实现类对象,调用同一方法执行不同行为 |
| 多接口实现 | INTERFACES if1, if2, …,一个类可实现多个接口 |
| 设计选择 | “是什么”用继承,”能做什么”用接口 |