2013年上半年(5月份)软考软件设计师级别考试中,与设计模式相关的试题共19分,其中上午填空题4分,下午试题15分。试题及分析如下:
上午试题:
● (44)设计模式能使一个对象的状态发生改变时通知所有依赖它的监听者。(45)设计模式限制类的实例对象只能有一个。适配器(Adapter)设计模式可以用于(46)。用于为一个对象添加更多功能而不使用子类的是(47)设计模式。
(44) A. 责任链(Chain of Responsibility) B. 命令(Command)
C. 抽象工厂(Abstract Factory) D. 观察者(Observer)
(45) A.原型(Prototype) B. 工厂方法(Factory Method)
C. 单例(Singleton) D.生成器(Builder)
(46) A. 将已有类的接口转换成和目标接口兼容
B. 改进系统性能
C. 将客户端代码数据转换成目标接口期望的合适的格式
D. 使所有接口不兼容类可以一起工作
(47) A.桥接(Bridge) B. 适配器(Adapter)
C. 组合(Composite) D.装饰器(Decorator)
答案:
(44) D,本题考查“观察者(Observer)模式”的定义(意图)。更多关于观察者模式的介绍,参见“对象间的联动——观察者模式”。
(45) C,本题考查“单例(Singleton)模式”的定义(意图)。更多关于单例模式的介绍,参见“确保对象的唯一性——单例模式”。
(46) A,本题考查“适配器(Adapter)模式”的定义(意图)。选项B明显是错误的,适配器模式不能改进系统性能,相反,对系统性能还有一点点负面影响,毕竟适配器对象的创建是需要占用内存的,而且会导致系统中存在方法的多次调用,要通过适配器来间接调用适配者的方法,此外,适配器模式的引入还会增加系统中类的数量。选项C错在适配的方向上,适配器是将适配者(已有类)与目标接口适配,提供给客户端使用,而不是将客户端中的代码或数据进行转换。选项D错在太武断,不应该使用“所有”,这样的设计将导致适配器的职责过重,是面向对象设计要避免的,适配器用于适配那些客户端所需要的适配者类,而并不是所有类。更多关于适配器模式的介绍,参见“不兼容结构的协调——适配器模式”。
(47) D,本题考查“装饰(Decorator)模式”的定义(意图)。更多关于装饰模式的介绍,参见“扩展系统功能——装饰模式”。
下午试题:
试题五为C++版,试题六为Java版,考查内容完全相同,下面以Java版为例:
试题六(共15分)
阅读下列说明和Java代码,将应填入(n)处的字句写在答题纸的对应栏内。
【说明】
现要求实现一个能够自动生成求职简历的程序。简历的基本内容包括求职者的姓名、性别、年龄及工作经历等。希望每份简历中的工作经历有所不同,并尽量减少程序中的重复代码。
现采用原型(Prototype)模式来实现上述要求,得到如下图所示的类图。
类图
【Java代码】
class WorkExperience (1)Cloneable { //工作经历
private String workDate;
private String company;
public Object Clone() {
(2) ;
obj.workDate = this.workDate;
obj.company = this.company;
return obj;
}
}
class Resume (3)Cloneable { //简历
private String name;
private String sex;
private String age;
private WorkExperience work;
public Resume(String name) {
this.name = name;
work = new WorkExperience();
}
private Resume(WorkExperience work) {
this.work = (4);
}
public void SetPersonalInfo(String sex, String age) { /*代码略*/ }
public void SetWorkExperience(String workDate, String company) { /*代码略*/ }
public Object Clone() {
Resume obj = (5);
//其余代码省略
return obj;
}
}
class WorkResume {
public static void main(String[] args) {
Resume a = new Resume("张三");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998~2000","XXX公司");
Resume b = (6) ;
b.SetWorkExperience("2001~2006","YYY公司");
}
}
试题分析:这道题考查原型模式,不过Sunny觉得这道题出得不太好,,具体来说,存在以下几个问题【个人意见,欢迎讨论!】:
(1) 题意模糊。何谓“自动生成求职简历”?应该是基于已有的简历快速创建新的简历。“希望每份简历中的工作经历有所不同”,这句话也有点问题,同一个人,工作经历怎么会不同?我个人觉得不同的简历区别应该在于“应聘职位”等,或者根据不同类型的职位突出不同的特长和能力,而姓名、性别、年龄、学历、工作经历等信息应该是相同的。
(2) 结构混乱,与现实不符。WorkExperience这个类代码不完整,无法为成员变量workDate和company赋值,也没有提示“其余代码省略”。Resume类也存在问题,一份简历难道只能有一条工作经历?类图中Resume与WorkExperience之间的多重性是1:1..*,也就是说一份简历可以对应一条或者多条工作经历,但是在Resume类中并没有用集合类型(如数组或者List等)来存储多条WorkExperience,而是只有一个WorkExperience对象,这样的设计难免与实际情况不相符合。
(3) 代码存在问题。Resume类中的SetWorkExperience()方法参数应该改为WorkExperience类型更为合理,否则方法名和实际功能不一致,而且按照题中代码,我不知道如何去实现省略的SetWorkExperience()方法,因为workDate和company根本访问不了。在WorkExperience类中,至少应该增加两对Getter与Setter方法,否则WorkExperience类中的数据成员无法操作。
(4) 类图与代码不一致。类图中简历Resume与工作经历WorkExperience是单向一对多的关系,而代码中是单向一对一,个人建议还是应该用一对多实现,更符合现实情况,我认为类图没有问题,代码需要修改。
(5) 目的不明确,缺少必要的注释,造成答案不唯一。第(6)空让人很费解,你说写个new Resume("张三")吧,也没有任何问题,这跟原型模式就没有任何关系了,但也不能算错,因为没有说客户端一定要调用克隆方法,不过命题人的意图应该是使用Clone()方法来实现对象创建,我觉得加个注释啥的会更好一点。
作为软考试题,个人觉得这道题目出得不太好,问题多多,,事实上简历的快速生成是可以作为原型模式的一个应用实例的,不过这道题所提供的代码有点小问题。以上评论难免有一些个人主观的看法,欢迎大家与我交流讨论。更多关于原型模式的介绍,参见“对象的克隆——原型模式”。
参考答案如下:
(1) implements
(2) WorkExperience obj = new WorkExperience()
(3) implements
(4) (WorkExperience)work.Clone()
(5) new Resume(this.work)
(6) (Resume)a.Clone()【实际上,此处填个new Resume("张三")也未尝不可,不过克隆出的新对象的性别和年龄均为null值】
本题在《设计模式实训教程》(刘伟,清华大学出版社,2012年1月)一书中有对应的原题(参见:P58,“第3章 创建型模式实训” 3.2 实训实例 之 3.2.5 原型模式实例之快速创建工作周报),。
【作者:刘伟 http://blog.csdn.net/lovelion】