软件设计模式(JAVA) 06_适配器模式_实验指导书
- 格式:doc
- 大小:102.50 KB
- 文档页数:6
java设计模式-适配器模式java适配器模式1、概述 什么是适配器模式? 适配器模式是⼀种结构型设计模式。
适配器模式就是:把⼀个类的接⼝变换成客户端所期待的另⼀种接⼝,从⽽使原本因接⼝不匹配⽽⽆法在⼀起⼯作的两个类能够在⼀起⼯作。
⽤电器来打个⽐喻:有⼀个电器的插头是三脚的,⽽现有的插座是两孔的,要使插头插上插座,我们需要⼀个插头转换器,这个转换器即是适配器。
适配器模式涉及3个⾓⾊:源(Adaptee):需要被适配的对象或类型,相当于插头。
适配器(Adapter):连接⽬标和源的中间对象,相当于插头转换器。
⽬标(Target):期待得到的⽬标,相当于插座。
2、适配器模式UML图 图⽚来⾃⽹络 通过上⾯UML图可以知道,客户端期待的接⼝或者对象通过适配器的转换得到了满⾜,Adapter通过内部包装Adaptee对象把源对象转换成客户端期待的对象。
3、适配器模式分类 适配器模式包括3种形式:1. 类适配器模式:类适配器使⽤的是继承的⽅式,⼀般来说⽆法对其⼦类进⾏适配2. 对象适配器模式:对象适配器使⽤的是组合的⽅式,⼦孙类都可以被适配。
另外,对象适配器对于增加⼀些新⾏为⾮常⽅便,⽽且新增加的⾏为同时适⽤于所有的源。
3. 接⼝适配器模式(⼜称缺省适配器模式):接⼝适配器模式(缺省适配模式)基本思想是,为⼀个接⼝提供缺省实现,这样⼦类可以从这个缺省实现进⾏扩展,⽽不必从原有接⼝进⾏扩展。
可以说对象适配器模式是另外6种结构型设计模式的起源(图⽚源⾃⽹络)。
4、三种适配器模式详解 适配器模式的三种实现⽅式及代码实例详解,⽤电器来打个⽐喻:有⼀个电器的插头是两脚的,⽽现有的插座是三孔的,要使插头插上插座,我们需要⼀个插头转换器,这个转换器即是适配器。
图⽚源⾃⽹络4.1、类适配器模式 类适配器使⽤的是继承的⽅式,⼀般来说⽆法对其⼦类进⾏适配,请看代码实例 1.⾸先我们有⼀个要被适配的类/*** 源(相当于两脚插头,也就是被适配的类)* @author ningbeibei*/public class Adaptee {public void adapteeMethod() {System.out.println("两脚插头,被适配的类....");}} 2.定义⼀个⽬标接⼝/*** ⽬标(客户所期待的类,可以是⼀个接⼝抽象类具体的类)* 相当于三孔插板* @author ningbeibei*/public interface Target {void targetMethod(); 3.定义适配器类 定义适配器类通过继承 Adaptee 和实现 Target 接⼝关联起来,/*** 类适配器模式(相当于转换器)* 通过Adapter类把Adaptee类与Target接⼝衔接起来* @author ningbeibei*/public class Adapter extends Adaptee implements Target {@Overridepublic void targetMethod() {//操作处理adapteeMethod();//操作处理}} 4.测试代码/*** 适配器模式测试类* @author ningbeibei*/public class test {public static void main(String[] args) {//类适配器模式Adapter adapter = new Adapter();adapter.targetMethod();}} 5.运⾏结果4.2、对象适配器模式 对象适配器使⽤的是组合的⽅式,它把源类作为属性放⼊适配器类中,请看代码实例 1.定义被适配的类/*** 源(相当于两脚插头,也就是被适配的类)* @author ningbeibei*/public class Adaptee {public void adapteeMethod() {System.out.println("两脚插头,被适配的类....");}} 2.定义⽬标接⼝/*** ⽬标(客户所期待的类,可以是⼀个接⼝抽象类具体的类)* 相当于三孔插板* @author ningbeibei*/public interface Target {void targetMethod();} 3.定义适配器类(类似于转换器) 注意:通过持有Adaptee属性建⽴与Target接⼝联系/*** 对象适配器模式(相当于转换器)* 通过持有Adaptee属性建⽴与Target接⼝联系* @author ningbeibei*/public class Adapter implements Target {//添加属性private Adaptee adaptee;public Adapter (Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void targetMethod() {//操作处理adaptee.adapteeMethod();//操作处理} 4.测试类/*** 适配器模式测试类* @author ningbeibei*/public class test {public static void main(String[] args) {//对象适配器模式Adapter adapter = new Adapter(new Adaptee());adapter.targetMethod();}} 5.结果输出4.3、接⼝适配器模式(缺省适配模式) 接⼝适配器模式⼜称缺省模式,这种模式通过抽象类对接⼝进⾏实现,在抽象类种对接⼝进⾏默认实现。
java设计模式—适配器模式关于JAVA适配器的解释 将⼀个类的接⼝转换成客户希望的另外⼀个接⼝。
Adapter模式使得原本由于接⼝不⽽不能⼀起⼯作的那些类可以⼀起⼯作。
——Gang of Four基本概念 客户:需要调⽤我们的代码的对象。
Adapter模式的宗旨:保留现有类所提供的服务,向客户提供接⼝,以满⾜客户的期望。
主要内容 (1)类适配器: 当客户在接⼝中定义了他期望的⾏为时,我们就可以应⽤适配器模式,提供⼀个实现该接⼝的类,并且扩展已有的类,通过创建⼦类来实现适配。
下⾯是类适配器的UML图: (2)对象适配器: 对象适配器”通过组合除了满⾜“⽤户期待接⼝”还降低了代码间的不良耦合。
在⼯作中推荐使⽤“对象适配”。
下⾯是对象适配器的UML 图: (3)缺省适配器模式: 缺省适配器模式是⼀种特殊的适配器模式,但这个适配器是由⼀个抽象类实现的,并且在抽象类中要实现⽬标接⼝中所规定的所有⽅法,但很多⽅法的实现都是“平庸”的实现,也就是说,这些⽅法都是空⽅法。
⽽具体的⼦类都要继承此抽象类。
适⽤情况 使⽤的前提: 1.接⼝中规定了所有要实现的⽅法 2.但⼀个要继承此接⼝的具体类,只⽤到了其中的⼏个⽅法,⽽其它的⽅法都是没有⽤的。
实现⽅法 1.⽤⼀个抽象类继承已有的接⼝,并实现接⼝中所规定的所有⽅法,这些⽅法的实现可以都是“平庸”实现----空⽅法;但此类中的⽅法是具体的⽅法,⽽不是抽象⽅法,否则的话,在具体的⼦类中仍要实现所有的⽅法,这就失去了适配器本来的作⽤。
2.原本要继承接⼝的⼦类,只继承1中的抽象类即可,并在其内部实现时,只对其感兴趣的⽅法进⾏实现。
注意事项 1.充当适配器⾓⾊的类就是:继承已有接⼝的抽象类 2.为什么要⽤抽象类: 此类是不要被实例化的。
⽽只充当适配器的⾓⾊,也就为其⼦类提供了⼀个共同的接⼝,但其⼦类⼜可以将精⼒只集中在其感兴趣的地⽅。
适配器模式解析 你想使⽤⼀个已经存在的适配器模式,⽽他的接⼝不符合你的需求。
浙江工商大学计算机与信息工程学院学期上机实验报告课程名称:设计模式姓名:学号:指导教师:班级:日期:【一】实验目的:(1)掌握结构型设计模式的意图、使用方法和优缺点,包括适配器模式、装饰模式、外观模式。
【二】实验内容:(1) 假设某系统中使用了一个第三方的日志记录工具,该日志记录工具支持数据库日志记录DatabaseLog和文本文件记录FileLog两种方式,如图1所示,它提供给的API接口是Write()方法,使用方法如下:Log.Write("Logging Message!");图1由于某种原因不能继续使用该日志记录工具了,需要采用另外一个日志记录工具,它同样也支持数据库日志记录DatabaseLog和文本文件记录FileLog两种方式,如图2所示,只不过它提供的API接口是WriteLog()方法,使用方法如下:Log.WriteLog("Logging Message!");图2现要求设计一个解决方案,使得在不修改原有程序的基础上,能实现日志工具的替换。
(2)Java I/O包的类关系图如图3所示,其中FilterInputSteam为装饰类。
要求编写一个装饰者,把输入流内的所有小写字符转换为大写字符。
图3【三】完成报告(预备知识、步骤、程序框图、程序、思考等):第一题UML类图:Java代码:ClientDemo.java/*** @author ******/public class ClientDemo {public static void main (String args[]){ ILog newLog = new Adapter();newLog.write();}}ILog.java/*** @author ******/public interface ILog {public void write();}DatabaseLog.java/*** @author ******/public class DatabaseLog implements ILog { public void write(){System.out.println("数据库日志记录");}}FileLog.java/*** @author ******/public class FileLog implements ILog {public void write(){System.out.println("文本文件记录");}}Adapter.java/*** @author ******/public class Adapter extends LogAdaptee implements ILog { public void write(){super.writeLog();System.out.println("适配器转换。
Java设计模式--适配器模式详解⽬录定义结构⽰例扩展总结定义适配器模式⽤于解决接⼝间的兼容问题。
当我们需要使⽤某个类提供的接⼝,但是这个接⼝与现在的系统需求不符,由于该接⼝是由第三⽅提供的,或者是已经在⽣产上跑了很久的存量类,我们不想通过改变这个类来满⾜现在系统的需求,那么这时候就可以考虑通过将⽬标类封装成⼀个满⾜系统需求的新类,因此适配器(Adapter)也称为包装器(Wrapper)。
结构适配器模式包含如下⾓⾊:Target:⽬标抽象类,客户类期望的接⼝。
Adapter:适配器类,适配器模式的核⼼,实现⽬标抽象类和适配者类的转换。
Adaptee:适配者类,与⽬前需求不兼容的类,即需要被包装的类。
Client:客户类,调⽤在⽬标抽象类中定义的业务⽅法。
适配器模式有类适配器和对象适配器两种实现:1、在类适配器模式中,适配器类实现了⽬标抽象类接⼝并继承了适配者类,在⽬标抽象类的实现⽅法中调⽤所继承的适配者类的⽅法。
2、在对象适配器模式中,适配器类实现了⽬标抽象类并定义了⼀个适配者类的对象实例,在⽬标抽象类的实现⽅法中调⽤适配者类实例的⽅法。
⽰例假如我们⼿上有⼀只⼿机,它只有⼀个type-c接⼝,同时⼿上有⼀条3.5mm的⽿机,想听⾳乐的时候就会发现⼆者不兼容,相较于更换⼿机或者⽿机,⼀般我们会选择⼀条type-c和3.5mm的转接线来实现最⾼性价⽐。
此时将3.5mm⽿机⽐作客户端(客户类),它期望得到⾳乐信号的⽬标是3.5mm接⼝(⽬标抽象类),type-c接⼝则是⼿机提供的⾳乐信号传输接⼝(适配者类),与⽿机接头不兼容,⽽转接线(适配器类)可以将type-c接⼝包装为⽿机可以插⼊的3.5mm接⼝,能够解决⼿机与⽿机的不兼容问题,实现⽤⽿机接收⼿机播放的⾳乐信号。
1、定义⽬标抽象类,即客户类期望的接⼝。
public interface ThreePointFiveConn {//⼀个提供⾳乐的3.5mm接⼝public void playMusic(String connector);}2、定义适配者类,即需要被包装的类。
java设计模式之适配器模式
结构型模式之适配器模式
将⼀个类的接⼝转换成客户希望的另外⼀个接⼝,使得原本由于接⼝不兼容⽽不能⼀起⼯作的那些类能⼀起⼯作。
适配器模式分为:类适配器模式和对象适配器模式。
类适配器模式通过继承和实现⽅式来实现,对象适配器模式通过聚合和组合关系来实现,前者类之间的耦合度⽐后者⾼,且要求程序员了解现有组件库中的相关组件的内部结构,所以应⽤相对较少些。
适配器模式的结构:
适配器模式(Adapter)包含以下主要⾓⾊:
⽬标接⼝(Target):当前系统业务所期待的接⼝,它可以是抽象类或接⼝。
适配者类(Adaptee):它是被访问和适配的现存组件库中的组件接⼝。
适配器类(Adapter):它是⼀个转换器,通过继承或引⽤适配者的对象,把适配者接⼝转换成⽬标接⼝,让客户按⽬标接⼝的格式访问适配者。
类适配器模式:
实现⽅式:定义⼀个适配器类来实现当前系统的业务接⼝,同时⼜继承现有组件库中已经存在的组件。
类适配器模式违背了合成服⽤原则。
类适配器是客户类有⼀个接⼝规范的情况下可⽤,反之不可⽤。
对象适配器模式:
实现⽅式:对象适配器模式可采⽤将现有组件库中已经实现的组件引⼊适配器类中,该类同时实现当前系统的业务接⼝。
注意:还有⼀个适配器模式是接⼝适配器模式,当不希望实现⼀个接⼝中所有的⽅法是,可以创建⼀个抽象类Adapter,实现所有⽅法。
⽽此时我们只需要继承该抽象类即可。
应⽤场景:
以前开发的系统存在满⾜新系统功能需求的类,但其接⼝同新系统的接⼝不⼀致。
使⽤第三⽅提供的组件时,但组件接⼝定义和⾃⼰要求的接⼝定义不同。
Java设计模式-适配器模式概念: 将⼀个类的接⼝,转换成客户期望的另⼀个接⼝。
适配器模式让原来接⼝不兼容的类可以在⼀起⼯作。
解决的问题: 提供类似于中间⼈的作⽤:把原本不兼容、不能⼀起⼯作的接⼝组合在⼀起,使得它们能够在⼀起正常的⼯作。
模式结构: 有两种适配器模式:对象适配器和类适配器。
因为类适配器需要使⽤到多重继承,⽽Java不⽀持多重继承,所以本⽂就只对对象适配器进⾏解释。
模式中的⾓⾊: Target(⽬标接⼝):客户请求的接⼝。
Adapter必须要实现这个接⼝(也可以是抽象类)。
Adapter(适配器类):实现Target接⼝,组合⽤户所需要的类。
Adaptee(被适配者):请求最终的执⾏者。
UML图: 这种适配器模式充满着良好的OO设计原则:使⽤对象组合,以修改的接⼝包装被适配者:这种做法还有额外的优点,那就是,被适配者的任何⼦类,都可以搭配着适配器使⽤。
情景导⼊: 某个玩具⼚商的⽤户需要⼀种能学鸭⼦叫的玩具鸡,怎么办? ⼚商想这还不容易:我只要⽣产⼀个玩具鸡的外形,将它的发⾳功能委托⽣产玩具鸭的⽣产线负责。
也就是说:外表看上去是玩具鸡,但发⾳这个功能有玩具鸭实现。
1package com.tony.Adapter;23/**4 * Target接⼝,接收⽤户请求。
5 * 玩具鸡6*/7public interface Chicken {89void speak();10 }1package com.tony.Adapter;23/**4 * 玩具鸭接⼝5 * ⾯向接⼝编程是⼀种好习惯6*/7public interface Duck {89void speak();10 }1package com.tony.Adapter;23/**4 * Adapter5 * 玩具鸡适配器,将发⾳请求委托给玩具鸭。
6*/7public class ChickenAdapter implements Chicken { 89private Duck duck;1011public ChickenAdapter(){12 duck = new ToyDuck();13 }14 @Override15public void speak() {16 duck.speak();17 }1819 }1package com.tony.Adapter;23/**4 * Adaptee5 * 被适配者,请求的真正执⾏者。
通常,客户类(clients of class)通过类的接口访问它提供的服务。
有时,现有的类(existing class)可以提供客户类的功能需要,但是它所提供的接口不一定是客户类所期望的。
这是由于现有的接口太详细或者缺乏详细或接口的名称与客户类所查找的不同等诸多不同原因导致的。
在这种情况下,现有的接口需要转化(convert)为客户类期望的接口,这样保证了对现有类的重用。
如果不进行这样的转化,客户类就不能利用现有类所提供的功能。
适配器模式(Adapter Pattern)可以完成这样的转化。
适配器模式建议定义一个包装类,包装有不兼容接口的对象。
这个包装类指的就是适配器(Adapter),它包装的对象就是适配者(Adaptee)。
适配器提供客户类需要的接口,适配器接口的实现是把客户类的请求转化为对适配者的相应接口的调用。
换句话说:当客户类调用适配器的方法时,在适配器类的内部调用适配者类的方法,这个过程对客户类是透明的,客户类并不直接访问适配者类。
因此,适配器可以使由于借口不兼容而不能交互的类可以一起工作(work together)。
在上面讨论的接口:(1)不是指在JAVA编程语言中接口的概念,虽然类的接口可以通过JAVA借扩来定义。
(2)不是指由窗体和GUI控件所组成的GUI应用程序的用户接口。
(3)而是指类所报漏的,被其他类调用的编程接口,类适配器(Class Adapter)VS对象适配器(Object Adapter)适配器总体上可以分为两类??类适配器(Class Adapter)VS对象适配器(Object Adapter)类适配器:类适配器是通过继承类适配者类(Adaptee Class)实现的,另外类适配器实现客户类所需要的接口。
当客户对象调用适配器类方法的时候,适配器内部调用它所继承的适配者的方法。
对象适配器:对象适配器包含一个适配器者的引用(reference),与类适配器相同,对象适配器也实现了客户类需要的接口。
实验(上机)六适配器模式实验(上机)目的1、练习使用结构型设计模式;2、练习使用适配器模式的设计思路;3、练习使用适配器模式实现“教务学生成绩排序查找”案例的实现。
实验(上机)课时2学时实验(上机)环境JDK1.8\Eclipse Mars预备知识1、结构型模式;2、适配器模式概述;3、适配器模式的结构与实现;4、适配器模式的应用实例;5、缺省适配器模式;6、双向适配器模式;7、适配器模式的优缺点与适用环境。
实验(上机)内容在为某学校开发教务管理系统时,开发人员发现需要对学生成绩进行排序和查找,该系统的设计人员已经开发了一个成绩操作接口ScoreOperation,在该接口中声明了排序方法Sort(int[]) 和查找方法Search(int[], int),为了提高排序和查找的效率,开发人员决定重用现有算法库中的快速排序算法类QuickSortClass和二分查找算法类BinarySearchClass,其中QuickSortClass的QuickSort(int[])方法实现了快速排序,BinarySearchClass的BinarySearch (int[], int)方法实现了二分查找。
由于某些原因,开发人员已经找不到该算法库的源代码,无法直接通过复制和粘贴操作来重用其中的代码;而且部分开发人员已经针对ScoreOperation接口(自己开发的接口)编程,如果再要求对该接口进行修改或要求大家直接使用QuickSortClass类和BinarySearchClass类将导致大量代码需要修改。
现使用适配器模式设计一个系统,在不修改已有代码的前提下将类QuickSortClass 和类BinarySearchClass的相关方法适配到ScoreOperation接口中。
新建解决方案,新建一个控制台应用程序,编写适配器模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
实验(上机)六适配器模式实验(上机)目的1、练习使用结构型设计模式;2、练习使用适配器模式的设计思路;3、练习使用适配器模式实现“教务学生成绩排序查找”案例的实现。
实验(上机)课时2学时实验(上机)环境JDK1.8\Eclipse Mars预备知识1、结构型模式;2、适配器模式概述;3、适配器模式的结构与实现;4、适配器模式的应用实例;5、缺省适配器模式;6、双向适配器模式;7、适配器模式的优缺点与适用环境。
实验(上机)内容在为某学校开发教务管理系统时,开发人员发现需要对学生成绩进行排序和查找,该系统的设计人员已经开发了一个成绩操作接口ScoreOperation,在该接口中声明了排序方法Sort(int[]) 和查找方法Search(int[], int),为了提高排序和查找的效率,开发人员决定重用现有算法库中的快速排序算法类QuickSortClass和二分查找算法类BinarySearchClass,其中QuickSortClass的QuickSort(int[])方法实现了快速排序,BinarySearchClass的BinarySearch (int[], int)方法实现了二分查找。
由于某些原因,开发人员已经找不到该算法库的源代码,无法直接通过复制和粘贴操作来重用其中的代码;而且部分开发人员已经针对ScoreOperation接口(自己开发的接口)编程,如果再要求对该接口进行修改或要求大家直接使用QuickSortClass类和BinarySearchClass类将导致大量代码需要修改。
现使用适配器模式设计一个系统,在不修改已有代码的前提下将类QuickSortClass 和类BinarySearchClass的相关方法适配到ScoreOperation接口中。
新建解决方案,新建一个控制台应用程序,编写适配器模式类实现代码,实现以上需求的案例,要求编写为控制台应用程序,并能调试运行。
实验(上机)步骤1、本实例类图:OperationAdapter--sortObjsearchObj: QuickSortClass: BinarySearchClass+ + +OperationAdapter ()Sort (int[] array)Search (int[] array, int key) ...: int[]: intScoreOperation+ +Sort (int[] array)Search (int[] array, int key)...: int[]: intQuickSortClass++++QuickSort (int[] array)Sort (int[] array, int p, int r)Partition (int[] a, int p, int r)Swap (int[] a, int i, int j)...: int[]: void: int: voidBinarySearchClass+BinarySearch (int[] array, int key)...: intClient2、创建Adapter的工程,并根据模式的角色创建对应的包。
3、新建ScoreOperation:抽象成绩操作类,充当目标接口p ackage Target;public interface ScoreOperation {// 成绩操作类:用户希望的接口方法// 成绩排序int[] Sort(int[] array);// 成绩查找int Search(int[] array, int key);}4、新建QuickSortClass:快速排序类,充当适配者p ackage Adaptee;public class QuickSortClass {// 快速排序类// 实现对数组的快速排序public int[] QuickSort(int[] array) {Sort(array, 0, array.length - 1);return array;}public void Sort(int[] array, int p, int r) { int q = 0;if (p < r) {q = Partition(array, p, r);Sort(array, p, q - 1);Sort(array, q + 1, r);}}public int Partition(int[] a, int p, int r) { int x = a[r];int j = p - 1;for (int i = p; i <= r - 1; i++) {if (a[i] <= x) {j++;Swap(a, j, i);}}Swap(a, j + 1, r);return j + 1;}public void Swap(int[] a, int i, int j) {int t = a[i];a[i] = a[j];a[j] = t;}}5、新建BinarySearchClass:二分查找类,充当适配者package Adaptee;public class BinarySearchClass {// 二分查找,如果找到,返回1,找不到返回-1// <param name="array">查找的数组</param>// <param name="key">查找的关键字</param>public int BinarySearch(int[] array, int key) { int low = 0;int high = array.length - 1;while (low <= high) {int mid = (low + high) / 2;int midVal = array[mid];if (midVal < key) {low = mid + 1;} else if (midVal > key) {high = mid - 1;} else {return 1; // 找到元素返回1}}return -1; // 未找到元素返回-1}}6、新建OperationAdapter:操作适配器,充当适配器package Adapter;import Adaptee.BinarySearchClass;import Adaptee.QuickSortClass;import Target.ScoreOperation;public class OperationAdapter implements ScoreOperation {// 维护与适配者之间的关联关系private QuickSortClass sortObj; // 定义适配者QuickSortClass对象private BinarySearchClass searchObj; // 定义适配者BinarySearchClass 对象// 构造函数,初始化适配者对象public OperationAdapter() {sortObj = new QuickSortClass();searchObj = new BinarySearchClass();}// 排序方法实现public int[] Sort(int[] array) {return sortObj.QuickSort(array); // 调用适配者类QuickSortClass 的排序方法}// 实现成绩查找方法public int Search(int[] array, int key) {return searchObj.BinarySearch(array, key); // 调用适配者类BinarySearchClass的查找方法}}7、Client:客户端测试类package Test;import Adapter.OperationAdapter;import Target.ScoreOperation;public class Client {public static void main(String[] args) {// 针对抽象目标接口编程ScoreOperation operation = new OperationAdapter();// 定义成绩数组int[] scores = { 84, 76, 50, 69, 90, 91, 88, 96 };int[] result;int score;System.out.println("成绩排序结果:");result = operation.Sort(scores);// 遍历输出成绩for (int i : result) {System.out.println(i + ",");}System.out.println();System.out.println("查找成绩90:");score = operation.Search(result, 90);if (score != -1) {System.out.println("找到成绩90。
");} else {System.out.println("没有找到成绩90。
");}System.out.println("查找成绩92:");score = operation.Search(result, 92);if (score != -1) {System.out.println("找到成绩92。
");} else {System.out.println("没有找到成绩92。
");}}}8、结果及分析结束考核内容1、上交各自建立的电子版解决方案及项目。
2、用EA建模工具编制以上类图。
3、上交实验报告。