【设计模式习题】答案和习题
如果需要增加一种新的画笔或增加一种新的颜色,只需对应增加一个具体类即可,且客户端可以针对高层类Pen和Color编程,在运行时再注入具体的子类对象,系统具有良好的可扩展性,满足开闭原则。在电脑主机(Mainframe)中,只需要按下主机的开机按钮(on()),即可调用其他硬件设备和软件的启动方法,如内存(Memory)的自检(check()),CPU的运行(run()),硬盘(HardDisk)的
一. 简答题(共1题,5分)
1. (简答题, 5分)
在某绘图软件中提供了多种大小不同的画笔(Pen),并且可以给画笔制定不同的颜色,模设计人员针对画笔的结构设计了如图1-1所示的初始类图。
通过仔细分析,设计人员发现该类图存在非常严重的问题,即如果需要增加一种新的大小或颜色的笔,就需要增加很多子类,例如增加一种绿色的笔,则对应每一中大小的笔都需要增加一支绿色笔,系统中类的个数急剧增加。
试根据依赖倒转原则和合成复用原则对该设计方案进行重构,使得增加新的大小或颜色的笔都较为方便,请绘制重构之后的结构图(类图)。
本练习可以通过依赖倒转原则和合成复用原则进行重构,重构方案如下所示:
在本重构方案中,将笔的大小和颜色设计为两个继承结构,两者可以独立变化,根据依赖倒转原则,建立一个抽象的关联关系,将颜色对象注入到画笔中;再根据合成复用原则,画笔在保持原有方法的同时还可以调用颜色类的方法,保持原有性质不变。如果需要增加一种新的画笔或增加一种新的颜色,只需对应增加一个具体类即可,且客户端可以针对高层类Pen和Color编程,在运行时再注入具体的子类对象,系统具有良好的可扩展性,满足开闭原则。(注:本重构方法即为桥接模式,在第4章将对该模式进行进一步讲解并提供实例代码来实现该模式。
也可以进一步将Size进行和Color一样的处理,单独封装,继承产生子类,最后组合到Pen当中。
1. (简答题, 5分)
使用简单工厂模式模拟女娲(Nvwa)造人(Person),如果传入参数“M”,则返回一个Man对象,如果传入参数“W”,则返回一个Woman对象,用Java语言实现该场景。现需要增加一个新的Robot类,如果传入参数“R”,则返回一个Robot对象,对代码进行修改并注意“女娲”的变化。
结果可以以word文档格式的附件上传。
简单工厂模式。参考类图如下所示:
分析:在本实例中,Nvwa类充当工厂类,其中定义了工厂方法makePerson(),Person类充当抽象产品类,Man、Woman和Robot充当具体产品类。工厂方法makePerson()的代码如下所示:
public static Person makePerson(char arg)
{
Person person = null;
switch(arg)
{
case ‘M’:
person = new Man(); break;
case ‘W’:
person = new Woman(); break;
case ‘R’:
person = new Robot(); break;
}
return person;
}
如果需要增加一个新的具体产品,则必须修改makePerson()方法中的判断语句,需增加一个新的case语句,违背了开闭原则。
2. (简答题, 5分)
现需要设计一个程序来读取多种不同类型的图片格式,针对每一种图片格式都设计一个图片读取器(ImageReader),如GIF图片读取器(GifReader)用于读取GIF格式的图片、JPG图片读取器(JpgReader)用于读取JPG格式的图片。图片读取器对象通过图片读取器工厂ImageReaderFactory来创建,ImageReaderFactory是一个抽象类,用于定义创建图片读取器的工厂方法,其子类GifReaderFactory和JpgReaderFactory用于创建具体的图片读取器对象。使用工厂方法模式实现该程序的设计。
正确答案:
工厂方法模式。参考类图如下所示:
分析:在本实例中,ImageReaderFactory充当抽象工厂,GifReaderFactory和JpgReaderFactory充当具体工厂,ImageReader充当抽象产品,GifReader和JpgReader充当具体产品。
1. (简答题, 5分)
计算机包含内存(RAM)、CPU等硬件设备,根据下面的“产品等级结构-产品族”示意图,使用抽象工厂模式实现计算机设备创建过程并绘制相应的类图。
正确答案:
抽象工厂模式。参考类图如下所示:
分析:在本实例中,AbstractFactory充当抽象工厂,PcFactory和MacFactory充当具体工厂,CPU和RAM充当抽象产品,PcCPU、MacCPU、PcRAM和MacRAM充当具体产品。CPU、PcCPU和MacCPU构成一个产品等级结构,RAM、PcRAM和MacRAM构成一个产品等级结构,PcCPU和PcRAM构成一个产品族,MacCPU和MacRAM构成一个产品族。
2. (简答题, 5分)使用单例模式的思想实现多例模式,确保系统中某个类的对象只能存在有限个,如两个或三个,设计并编写代码实现一个多例类。
正确答案:
单例模式。参考类图如下所示:
分析:多例模式(Multiton Pattern)是单例模式的一种扩展形式,多例类可以有多个实例,而且必须自行创建和管理实例,并向外界提供自己的实例,可以通过静态集合对象来存储这些实例。多例类Multiton的代码如下所示:
import java.util.*;
public class Multiton
{
//定义一个数组用于存储四个实例
private static Multiton[] array = {new Multiton(), new Multiton(), new Multiton(), new Multiton()};
//私有构造函数
private Multiton()
{
}
//静态工厂方法,随机返回数组中的一个实例
public static Multiton getInstance()
{
return array[random()];
}
//随机生成一个整数作为数组下标
public static int random()
{
Date d = new Date();
Random random = new Random();
int value = Math.abs(random.nextInt());
value = value % 4;
return value;
}
public static void main(String args[])
{
Multiton m1,m2,m3,m4;
m1 = Multiton.getInstance();
m2 = Multiton.getInstance();
m3 = Multiton.getInstance();
m4 = Multiton.getInstance();
System.out.println(m1m2);
System.out.println(m1m3);
System.out.println(m1==m4);
}
}
1. (简答题, 5分)实现一个双向适配器实例,使得猫(Cat)可以学狗(Dog)叫,狗可以学猫抓老鼠。绘制相应类图并使用代码编程模拟。
正确答案:
适配器模式。参考类图如下所示:
分析:在本实例中,Adapter充当适配器,Cat和Dog既充当抽象目标,又充当抽象适配者。如果客户端针对Cat编程,则Cat充当抽象目标,Dog充当抽象适配者,ConcreteDog充当具体适配者;如果客户端针对Dog编程,则Dog充当抽象目标,Cat充当抽象适配者,ConcreteCat充当具体适配者。本实例使用对象适配器,Adapter类的代码如下所示:
class Adapter implements Cat, Dog
{
private Cat cat;
private Dog dog;
public void setCat(Cat cat)
{
this.cat = cat;
}
public void setDog(Dog dog)
{
this.dog = dog;
}
public void cry() //猫学狗叫
{
dog.wang();
}
public void catchMouse()
{
cat.catchMouse();
}
public void wang()
{
dog.wang();
}
public void action() //狗学猫抓老鼠
{
cat.catchMouse();
}
}
2. (简答题, 5分)
海尔(Haier)、TCL、海信(Hisense)都是家电制造商,它们都生产电视机(Television)、空调(Air Conditioner)、冰箱(Refrigeratory)。现需要设计一个系统,描述这些家电制造商以及它们所制造的电器,要求绘制类图并用代码模拟实现。
正确答案:
桥接模式。参考类图如下所示:
分析:在本实例中,Manufacturer充当抽象类角色,Haier、TCL和Hisense充当扩充抽象类角色,Appliance充当抽象实现类,Television、AirConditioner和Refrigeratory充当具体实现类。本实例部分代码如下所示:
abstract class Manufacturer
{
protected Appliance appliance;
public abstract void produce();
public void setAppliance(Appliance appliance)
{
this.appliance = appliance;
}
}
class Haier extends Manufacturer
{
public void produce()
{
System.out.println(“生产海尔电器!”);
appliance.assemble(); //调用实现类的业务方法
}
}
1. (简答题, 5分)
某教育机构组织结构如下图所示:
在该教育机构的OA系统中可以给各级办公室下发公文,现采用组合模式设计该机构的组织结构,绘制相应的类图并编程模拟实现,在客户端代码中模拟下发公文。
正确答案:
组合模式。参考类图如下所示:
分析:本实例使用了安全组合模式,Unit充当抽象构件角色,Office充当叶子构件角色,Institution充当容器构件角色。本实例代码如下所示:
abstract class Unit
{
public abstract void handleArchives();
}
class Office extends Unit
{
private String name;
public Office(String name)
{
this.name = name;
}
public void handleArchives()
{
System.out.println(this.name + “处理公文!”);
}
}
class Institution extends Unit
{
private ArrayList list = new ArrayList();
private String name;
public Institution(String name)
{
this.name = name;
}
public void add(Unit unit)
{
list.add(unit);
}
public void handleArchives()
{
System.out.println(this.name + “接收并下发公文:”);
for(Object obj : list)
{
((Unit)obj).handleArchives();
}
}
}
在客户类中创建树形结构,代码如下所示:
class Client
{
public static void main(String args[])
{
Institution bjHeadquarters,hnSubSchool,csTeachingPost,xtTeachingPost;
Unit tOffice1,tOffice2,tOffice3,tOffice4,aOffice1,aOffice2,aOffice3,aOffice4;
bjHeadquarters = new Institution(“北京总部”);
hnSubSchool = new Institution(“湖南分校”);
csTeachingPost = new Institution(“长沙教学点”);
xtTeachingPost = new Institution(“湘潭教学点”);
tOffice1 = new Office(“北京教务办公室”);
tOffice2 = new Office(“湖南教务办公室”);
tOffice3 = new Office(“长沙教务办公室”);
tOffice4 = new Office(“湘潭教务办公室”);
aOffice1 = new Office(“北京行政办公室”);
aOffice2 = new Office(“湖南行政办公室”);
aOffice3 = new Office(“长沙行政办公室”);
aOffice4 = new Office(“湘潭行政办公室”);
csTeachingPost.add(tOffice3);
csTeachingPost.add(aOffice3);
xtTeachingPost.add(tOffice4);
xtTeachingPost.add(aOffice4);
hnSubSchool.add(csTeachingPost);
hnSubSchool.add(xtTeachingPost);
hnSubSchool.add(tOffice2);
hnSubSchool.add(aOffice2);
bjHeadquarters.add(hnSubSchool);
bjHeadquarters.add(tOffice1);
bjHeadquarters.add(aOffice1);
bjHeadquarters.handleArchives();
}
}
2. (简答题, 5分)
某系统中的文本显示组件类(TextView)和图片显示组件类(PictureView)都继承了组件类(Component),分别用于显示文本内容和图片内容,现需要构造带有滚动条、或者带有黑色边框、或者既有滚动条又有黑色边框的文本显示组件和图片显示组件,为了减少类的个数可使用装饰模式进行设计,绘制类图并编程模拟实现。
正确答案:
装饰模式。参考类图如下所示:
分析:本实例使用了透明装饰模式,Component充当抽象组件角色,TextView和PictureView充当具体组件角色,Decorator充当抽象装饰角色,ScrollBarDecorator和BlackBorderDecorator充当具体装饰角色。其中,Decorator类和ScrollBarDecorator类示例代码如下:
class Decorator extends Component
{
private Component component;
public Decorator(Component component)
{
this.component = component;
}
public void display()
{
component.display();
}
}
class ScrollBarDecorator extends Decorator
{
public ScrollBarDecorator(Component component)
{
super(component);
}
public void display()
{
System.out.println(“增加滚动条”);
super.display();
}
}
3. (简答题, 5分)
在电脑主机(Mainframe)中,只需要按下主机的开机按钮(on()),即可调用其他硬件设备和软件的启动方法,如内存(Memory)的自检(check()),CPU的运行(run()),硬盘(HardDisk)的读取(read()),操作系统(OS)的载入(load())等,如果某一过程发生错误则电脑启动失败。使用外观模式模拟该过程,绘制类图并编程模拟实现。
正确答案:
外观模式。参考类图如下所示:
分析:在本实例中,Mainframe充当外观角色,Memory、CPU、HardDisk和OS充当子系统角色,其中,Mainframe类代码如下所示:
class Mainframe{
private Memory memory;
private CPU cpu;
private HardDisk harddisk;
private OS os;
public Mainframe()
{
memory = new Memory();
cpu = new CPU();
harddisk = new HardDisk();
os = new OS();
}
public void on()
{
try
{
memory.check();
cpu.run();
harddisk.read();
os.load();
}
catch(Exception e)
{
System.out.println(“启动失败!”);
}
}
}
4. (简答题, 5分)
应用软件所提供的桌面快捷方式是快速启动应用程序的代理,桌面快捷方式一般使用一张小图片来表示(Picture),通过调用快捷方式的run()方法将调用应用软件(Application)的run()方法。使用代理模式模拟该过程,绘制类图并编程模拟实现。
正确答案:
代理模式。参考类图如下所示:
分析:在本实例中,Subject充当抽象主题角色,Application充当真实主题角色,Picture充当代理主题角色,其中,Picture类代码如下所示:
class Picture implements Subject
{
private Application obj;
public Picture()
{
obj = new Application();
}
public void run()
{
obj.run();
}
}
1. (简答题)为了用户使用方便,某系统提供了一系列功能键,用户可以自定义功能键的功能,如功能键FunctionButton可以用于退出系统(SystemExitClass),也可以用于打开帮助界面(DisplayHelpClass)。用户可以通过修改配置文件来改变功能键的用途,现使用命令模式来设计该系统,使得功能键类与功能类之间解耦,相同的功能键可以对应不同的功能。要求绘制类图并编写代码。
正确答案:
命令模式。参考类图如下所示:
分析:在本实例中,FunctionButton充当请求调用者,SystemExitClass和DisplayHelpClass充当请求接收者,而ExitCommand和HelpCommand的充当具体命令类。其中,FunctionButton类、Command类、ExitCommand类和SystemExitClass类代码如下所示:
class FunctionButton{
private Command command;
public void setCommand(Command command){
this.command = command;
}
public void click(){
command.execute();
}
}
abstract class Command{
public abstract void execute();
}
class ExitCommand extends Command{
private SystemExitClass seObj;
public ExitCommand(){
seObj = new SystemExitClass();
}
public void execute(){
seObj.exit();
}
}
class SystemExitClass{
public void exit(){
System.out.println(“退出系统!”);
}
}
1. (简答题, 5分)某教务管理系统中一个班级(Class)包含多个学生(Student),使用Java内置迭代器实现对学生信息的遍历,要求按学生年龄由大到小的次序输出学生信息。画出类图,用Java语言模拟实现该过程。
正确答案:
迭代器模式。参考类图如下所示:
分析:在本实例中,Class类充当聚合类,在其中定义了一个ArrayList类型的集合用于存储Student对象,为了实现按学生年龄由大到小的次序输出学生信息,自定义一个比较器类MyComparator实现了Comparator接口并实现在接口中声明的compare()方法。在Class类的displayStudents()方法中创建一个比较器对象用于排序,再创建一个迭代器对象用于遍历集合。本实例代码如下所示:
import java.util.*;
class Class
{
private ArrayList students = new ArrayList();
public void addStudent(Student student)
{
students.add(student);
}
public void displayStudents()
{
Comparator comp = new MyComparator();
Collections.sort(students,comp);
Iterator i = students.iterator();
while(i.hasNext())
{
Student student = (Student)i.next();
System.out.println(“姓名:” + student.getSName() + “,年龄:” + student.getSAge());
}
}
}
class MyComparator implements Comparator
{
public int compare(Object obj1,Object obj2)
{
Student s1=(Student)obj1;
Student s2=(Student)obj2;
if(s1.getSAge()<s2.getSAge())
{
return 1;
}
else
{
return 0;
}
}
}
class Student
{
private String sName;
private int sAge;
private String sSex;
public Student(String sName,int sAge,String sSex)
{
this.sName = sName;
this.sAge = sAge;
this.sSex = sSex;
}
public void setSName(String sName) {
this.sName = sName;
}
public void setSAge(int sAge) {
this.sAge = sAge;
}
public void setSSex(String sSex) {
this.sSex = sSex;
}
public String getSName() {
return (this.sName);
}
public int getSAge() {
return (this.sAge);
}
public String getSSex() {
return (this.sSex);
}
}
class MainClass
{
public static void main(String args[])
{
Class obj = new Class();
Student student1,student2,student3,student4;
student1 = new Student(“杨过”,20,“男”);
student2 = new Student(“令狐冲”,22,“男”);
student3 = new Student(“小龙女”,18,“女”);
student4 = new Student(“王语嫣”,19,“女”);
obj.addStudent(student1);
obj.addStudent(student2);
obj.addStudent(student3);
obj.addStudent(student4);
obj.displayStudents();
}
}
//输出结果如下:
//姓名:令狐冲,年龄:22
//姓名:杨过,年龄:20
//姓名:王语嫣,年龄:19
//姓名:小龙女,年龄:18
2. (简答题, 5分)某在线游戏支持多人联机对战,每个玩家都可以加入某一战队组成联盟,当战队中某一成员受到敌人攻击时将给所有盟友发送通知,盟友收到通知后将作出响应。使用观察者模式设计,画出类图并实现该过程。
正确答案:
观察者模式
分析:在本实例中,Observer充当抽象观察者角色,Player充当具体观察者角色,Ally充当观察目标角色。其中,Ally类和Player类代码如下所示:
class Player implements Observer
{
private String name;
public Player(String name)
{
this.name = name;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void help()
{
System.out.println(“坚持住,” + this.name + “来救你!”);
}
public void beAttacked(Ally ally)
{
System.out.println(this.name + “被攻击!”);
ally.notifyObserver(name);
}
}
class Ally
{
private ArrayList players = new ArrayList();
public void join(Observer obs)
{
players.add(obs);
}
public void quit(Observer obs)
{
players.remove(obs);
}
public void notifyObserver(String name)
{
System.out.println(“紧急通知,盟友” + name + “遭受敌人攻击!”);
for(Object obs : players)
{
if (!((Observer)obs).getName().equalsIgnoreCase(name))
{
((Observer)obs).help();
}
}
}
}
3. (简答题, 5分)某纸牌游戏软件中,人物角色具有入门级(Primary)、熟练级(Secondary)、高手级(Professional)和骨灰级(Final)四种等级,角色的等级与其积分相对应,游戏胜利将增加积分,失败则扣除积分。入门级具有最基本的游戏功能play() ,熟练级增加了游戏胜利积分加倍功能doubleScore(),高手级在熟练级基础上再增加换牌功能changeCards(),骨灰级在高手级基础上再增加偷看他人的牌功能peekCards()。现使用状态模式来设计该系统,绘制类图并编程实现。
正确答案:
状态模式。参考类图如下所示:
分析:在本实例中,PlayerRole充当环境类角色,RoleState充当抽象状态类,PrimaryState、SecondaryState、ProfessionalState和FinalState充当具体状态类。本实例部分代码如下所示:
class PlayerRole //环境类
{
private String nickname;
private RoleState state;
public PlayerRole(String nickname)
{
this.nickname = nickname;
}
public String getNickname()
{
return this.nickname;
}
public void setState(RoleState state)
{
this.state = state;
}
public void play(int score, String result)
{
state.play(score,result);
}
public void doubleScore(int score, String result)
{
state.doubleScore(score,result);
}
public void changeCards()
{
state.changeCards();
}
public void peekCards()
{
state.peekCards();
}
}
abstract class RoleState //抽象状态类
{
protected PlayerRole role;
protected int point; //积分
protected String grade; //等级
public abstract void play(int score, String result);
public abstract void doubleScore(int score, String result);
public abstract void changeCards();
public abstract void peekCards();
public abstract void check();
}
class PrimaryState extends RoleState //具体状态类
{
public PrimaryState(PlayerRole role)
{
this.point = 0;
this.grade = “入门级”;
this.role = role;
}
public PrimaryState(RoleState state)
{
this.point = state.point;
this.grade = “入门级”;
this.role = state.role;
}
public void play(int score, String result)
{
if(result.equalsIgnoreCase(“win”)) //获胜
{
this.point += score;
System.out.println(“玩家” + this.role.getNickname() + “获胜,增加积分” + score + “,
当前积分为” + this.point + “。”);
}
else if(result.equalsIgnoreCase(“lose”)) //失利
{
this.point -= score;
System.out.println(“玩家” + this.role.getNickname() + “失利,减少积分” + score + “,
当前积分为” + this.point + “。”);
}
this.check();
}
public void doubleScore(int score, String result)
{
System.out.println(“暂不支持该功能!”);
}
public void changeCards()
{
System.out.println(“暂不支持该功能!”);
}
public void peekCards()
{
System.out.println(“暂不支持该功能!”);
}
public void check() //模拟
{
if(this.point >= 10000)
{
this.role.setState(new FinalState(this));
}
else if(this.point >= 5000)
{
this.role.setState(new ProfessionalState(this));
}
else if(this.point >= 1000)
{
this.role.setState(new SecondaryState(this));
}
}
}
//其他具体状态类代码省略
1. (简答题, 5分)某系统需要对重要数据(如用户密码)进行加密,并提供了几种加密方案(如凯撒加密、DES加密等),对该加密模块进行设计,使得用户可以动态选择加密方式。要求绘制类图并编程模拟实现。
正确答案:
策略模式。参考类图如下所示:
分析:在本实例中,DataOperation充当环境类角色,Cipher充当抽象策略角色,CaesarCipher和ModCipher充当具体策略角色。其中,DataOperation类的代码如下所示:
class DataOperation{
private Cipher cipher;
public void setCipher(Cipher cipher){
this.cipher = cipher;
}
public String doEncrypt(int key, String plainText){
return cipher.doEncrypt(key,plainText);
}
}
2. (简答题, 5分)某银行软件的利息计算流程如下:系统根据账户查询用户信息;根据用户信息判断用户类型;不同类型的用户使用不同的利息计算方式计算利息(如活期账户CurrentAccount和定期账户SavingAccount具有不同的利息计算方式);显示利息。现使用模板方法模式来设计该系统,绘制类图并编程实现。
正确答案:
模板方法模式。参考类图如下所示:
分析:在本实例中,Account充当抽象父类角色,CurrentAccount和SavingAccount充当具体子类角色,其中Account和CurrentAccount的模拟代码如下所示:
abstract class Account{
public abstract void getUserType();
public abstract void calculateInterest();
public void findUser(){
System.out.println(“查询用户信息!”);
}
public void display(){
System.out.println(“显示利息!”);
}
public void handle(){
findUser();
getUserType();
calculateInterest();
display();
}
}
class CurrentAccount extends Account{
public void getUserType(){
System.out.println(“活期账户!”);
}
public void calculateInterest(){
System.out.println(“按活期利率计算利息!”);
}
}
填空题
已知某类库开发商提供了一套类库,类库中定义了Application类和Document类,它们之间的关系如图1所示,其中,Application类表示应用程序自身,而Document类则表示应用程序打开的文档。Application类负责打开一个已有的以外部形式存储的文档,如一个文件,一旦从该文件中读出信息后,它就由一个Document对象表示。
图1 类图
当开发一个具体的应用程序时,开发者需要分别创建自己的Application和Document子类,例如图1中的类MyApplication和类MyDocument,并分别实现Application和Document类中的某些方法。
已知Application类中的openDocument方法采用了模板方法设计模式,该方法定义了打开文档的每一个步骤,如下所示:
(1) 首先检查文档是否能够被打开,若不能打开,则给出出错信息并返回;
(2) 创建文档对象;
(3) 通过文档对象打开文档;
(4) 通过文档对象读取文档信息;
(5) 将文档对象加入到Application的文档对象集合中。
【Java代码】
abstract class Document {
public void save() { /* 存储文档数据,此处代码省略 */}
public void open(String docName) { /*打开文档,此处代码省略 /}
public void close() { / 关闭文档,此处代码省略 */}
public abstract void read(String docName);
}
abstract class Application {
private Vector< (1) > docs; /文档对象集合/
public boolean canOpenDocument(String docName){
/判断是否可以打开指定文档,返回真值时表示可以打开,
返回假值表示不可打开,此处代码省略/
}
public void addDocument(Document aDocument){
/将文档对象添加到文档对象集合中/
docs.add( (2) );
}
public abstract Document doCreateDocument(); /创建一个文档对象/
public void openDocument(String docName) { /打开文档/
if( (3) ) {
System.out.println(“文档无法打开!”);
return;
}
(4) adoc = (5) ;
(6) ;
(7) ;
(8) ;
}
}
正确答案:
第1空
Document
第2空
aDocument
第3空
!canOpenDocument(docName)
第4空
Document
第5空
doCreateDocument()
第6空
adoc.open(docName)
第7空
adoc.read(docName)
第8空
addDocument(adoc)
1(填空题)
某软件公司现欲开发一款飞机模拟系统,该系统主要模拟不同种类飞机的飞行特征与起飞特征。需要模拟的飞机种类及其特征如表1所示。
飞机种类
起飞特征
飞行特征
直升机(Helicopter)
垂直起飞(VerticalTakeOff)
亚音速飞行(SubSonicFly)
客机(AirPlane)
长距离起飞(LongDistanceTakeOff)
亚音速飞行(SubSonicFly)
歼击机(Fighter)
长距离起飞(LongDistanceTakeOff)
超音速飞行(SuperSonicFly)
鹞式战斗机(Harrier)
垂直起飞(VerticalTakeOff)
超音速飞行(SuperSonicFly)
为支持将来模拟更多种类的飞机,采用策略设计模式(Strategy)设计的类图如图1所示。
图1 类图
图1中,AirCraft为抽象类,描述了抽象的飞机,而类Helicopter、AirPlane、Fighter和Harrier分别描述具体的飞机种类,方法fly()和takeOff()分别表示不同飞机都具有飞行特征和起飞特征;类FlyBehavior与TakeOffBehavior为抽象类,分别用于表示抽象的飞行行为与起飞行为;类SubSonicFly与SuperSonicFly分别描述亚音速飞行和超音速飞行的行为;类VerticalTakeOff与LongDistanceTakeOff分别描述垂直起飞与长距离起飞的行为。
【Java代码】
interface FlyBehavior{
public void fly();
}
class SubSonicFly implements FlyBehavior{
public void fly() {System.out.println(“亚音速飞行!”);}
}
class SuperSonicFly implements FlyBehavior{
public void fly() {System.out.println(“超音速飞行!”);}
}
interface TakeOffBehavior{
public void takeOff();
}
class VerticalTakeOff implements TakeOffBehavior{
public void takeOff() {System.out.println(“垂直起飞!”);}
}
class LongDistanceTakeOff implements TakeOffBehavior{
public void takeOff() {System.out.println(“长距离起飞!”);}
}
abstract class AirCraft{
protected (1) ;
protected (2) ;
public void fly() { (3) ; }
public void takeOff() { (4) ; }
}
class Helicopter (5) AirCraft {
public Helicopter() {
flyBehavior = new (6) ;
takeOffBehavior = new (7) ;
}
}
//其他代码省略
正确答案:
第1空
FlyBehavior flyBehavior
第2空
TakeOffBehavior takeOffBehavior
第3空
flyBehavior.fly()
第4空
takeOffBehavior.takeOff()
第5空
extends
第6空
SubSonicFly()
第7空
VerticalTakeOff()
必答 (填空题)
传输门是传输系统中的重要装置。传输门具有Open(打开)、Closed(关闭)、Opening(正在打开)、StayOpen(保持打开)、Closing(正在关闭)五种状态。触发状态的转换事件有click、complete和timeout三种。事件与其相应的状态转换如图1所示。
图1 传输门响应事件与其状态转换图
下面的【Java代码1】与【Java代码2】分别用两种不同的设计思路对传输门进行状态模拟,请填补代码中的空缺。
【Java代码1】
public class Door {
public static final int CLOSED = 1;
public static final int OPENING = 2;
public static final int OPEN = 3;
public static final int CLOSING = 4;
public static final int STAYOPEN = 5;
private int state = CLOSED;
//定义状态变量,用不同的整数表示不同状态
private void setState(int state) {
this.state = state; //设置传输门当前状态
}
public void getState() {
//此处代码省略,本方法输出状态字符串,
//例如,当前状态为CLOSED时,输出字符串为“CLOSED”
}
public void click() {
if( (1) ) setState(OPENING);
else if( (2) ) setState(CLOSING);
else if( (3) ) setState(STAYOPEN);
}
//发生timeout事件时进行状态转换
public void timeout() {
if(state == OPEN) setState(CLOSING);
}
public void complete() { //发生complete事件时进行状态转换
if(state == OPENING) setState(OPEN);
else if(state == CLOSING) setState(CLOSED);
}
public static void main(String[] args) {
Door aDoor = new Door();
aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.complete();
aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.click();
aDoor.getState(); return;
}
}
【Java代码2】
public class Door {
public final DoorState CLOSED = new DoorClosed(this);
public final DoorState OPENING = new DoorOpening(this);
public final DoorState OPEN = new DoorOpen(this);
public final DoorState CLOSING = new DoorClosing(this);
public final DoorState STAYOPEN = new DoorStayOpen(this);
private DoorState state = CLOSED;
//设置传输门当前状态
public void setState(DoorState state) {
this.state = state;
}
public void getState() {//根据当前状态输出对应的状态字符串
System.out.println(state.getClass().getName());
}
public void click() { (4) ;} //发生click事件时进行状态转换
public void timeout() { (5) ;} //发生timeout事件时进行状态转换
public void complete() { (6) ;} //发生complete事件时进行状态转换
public static void main(String[] args){
Door aDoor = new Door();
aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.complete();
aDoor.getState(); aDoor.timeout(); aDoor.getState(); return;
}
}
public abstract class DoorState { //定义所有状态类的基类
protected Door door;
public DoorState(Door door) {
this.door = door;
}
public abstract void click() {}
public abstract void complete() {}
public abstract void timeout() {}
}
class DoorClosed extends DoorState { //定义一个基本的Closed状态
public DoorClosed (Door door) {super(door); }
public void click() { (7) ;}
//该类定义的其余代码省略
}
//其余代码省略
正确答案:
第1空
state == CLOSED || state ==CLOSING
第2空
state == OPENING || state == STAYOPEN
第3空
state == OPEN
第4空
state.click()
第5空
state.timeout()
第6空
state.complete()
第7空
door.setState(door.OPENING)
(填空题)
某软件公司欲基于迭代器模式开发一套用于遍历数组元素的类库,其基本结构如图1所示:
图1 类图
在图1中,Collection类是抽象聚合类,ConcreteCollection类是具体聚合类,Iterator类是抽象迭代器类,ConcreteIterator类是具体迭代器类。在聚合类中提供了创建迭代器的工厂方法createIterator()和数组的Setter和Getter方法,在迭代器中提供了用于遍历数组元素的相关方法,如first()、last()、next()等。
【Java代码】
interface Collection{
public void setArray(Object objs[]); //设置数组
public Object[] getArray(); //获取数组
public Iterator createIterator(); //创建迭代器
}
interface Iterator{
public void first(); //索引指向第一个元素
public void last(); //索引指向最后一个元素
public void next(); //索引指向下一个元素
public void previous(); //索引指向上一个元素
public boolean isLast(); //判断是否最后一个元素
public boolean isFirst(); //判断是否第一个元素
public Object getItem(); //获取当前索引所指向的元素
}
class ConcreteCollection implements Collection{
private Object[] objs;
public void setArray(Object objs[])
{ this.objs = objs; }
public Object[] getArray()
{ return this.objs; }
public Iterator createIterator()
{ return (1) ; }
}
class ConcreteIterator implements Iterator{
private Object[] objs;
private int index=0; //索引变量,初值为0
public ConcreteIterator(ConcreteCollection collection)
{ this.objs = (2) ; }
public void first()
{ index = 0; }
public void last()
{ (3) ; }
public void next(){
if(index<objs.length){
(4) ;
}
}
public void previous(){
if(index>=0){
(5) ;
}
}
public boolean isLast(){ (6) ; }
public boolean isFirst(){ (7) ; }
public Object getItem()
{ return objs[index]; }
}
class Test{
public static void main(String args[]){
Collection collection;
collection = new ConcreteCollection();
Object[] objs={“北京”,“上海”,“广州”,“深圳”,“长沙”};
collection.setArray(objs);
Iterator i = (8) ;
i.last(); //逆向遍历所有元素
while( (9) ){
System.out.println(i.getItem().toString());
(10) ;
}
}
}
正确答案:
第1空
new ConcreteIterator(this)
第2空
collection.getArray()
第3空
objs.length-1
第4空
index++
第5空
index–
第6空
return indexobjs.length
第7空
return index-1
第8空
collection.createIterator()
第9空
!i.isFirst()
第10空
i.previous()
必答[填空题]
已知某企业欲开发一家用电器遥控系统,即用户使用一个遥控器即可控制某些家用电器的开与关。遥控器如图1所示。该遥控器共有4个按钮,编号分别是0至3,按钮0和2能够遥控打开电器1和电器2,按钮1和3则能遥控关闭电器1和电器2。由于遥控系统需要支持形式多样的电器,因此,该系统的设计要求具有较高的扩展性。现假设需要控制客厅电视和卧室电灯,对该遥控系统进行设计所得类图如图2所示。
图1 遥控器
图2 设计类图
图2中,类RemoteController的方法onPressButton(int button)表示当遥控器按钮按下时调用的方法,参数为按键的编号;Command接口中on和off方法分别用于控制电器的开与关;Light中turnLight(int degree)方法用于调整电灯灯光的强弱,参数degree值为0时表示关灯,值为100时表示开灯并且将灯光亮度调整到最大;TV中setChannel(int channel)方法表示设置电视播放的频道,参数channel值为0时表示关闭电视,为1时表示开机并将频道切换为第1频道。
【Java代码】
class Light{ //电灯类
public void turnLight(int degree) {//调整灯光亮度,0表示关灯,100表示亮度最大}
}
class TV{ //电视机类
public void setChannel(int channel) {//0表示关机,1表示开机并切换到1频道}
}
interface Command{ //抽象命令类
void on();
void off();
}
class RemoteController{ //遥控器类
//遥控器有4个按钮,按照编号分别对应4个Command对象
protected Command[] commands = new Command[4];
//按钮被按下时执行命令对象中的命令
public void onPressButton(int button) {
if(button%2==0) commands[button].on();
else commands[button].off();
}
public void setCommand(int button, Command command){
(1) = command; //设置每个按钮对应的命令对象
}
}
class LightCommand implements Command{ //电灯命令类
protected Light light; //指向要控制的电灯对象
public void on() {light.turnLight(100);}
public void off() {light. (2) ;}
public LightCommand(Light light) {this.light = light; }
}
class TVCommand implements Command{ //电视机命令类
protected TV tv;
public void on() {tv. (3) ;}
public void off() {tv.setChannel(0);}
public TVCommand(TV tv) {this.tv = tv;}
}
public class Test
{
public static void main(String args[])
{
//创建电灯和电视对象
Light light = new Light();
TV tv = new TV();
LightCommand lightCommand = new LightCommand(light);
TVCommand tvCommand = new TVCommand(tv);
RemoteController remoteController = new RemoteController();
//设置按钮和命令对象
remoteController.setCommand(0, (4) );
…//此处省略设置按钮1、按钮2和按钮3的命令对象代码
}
本题中,应用命令模式能够有效地让类 (5) 和类 (6) 、类 (7) 之间的耦合性降至最小。
本题已答 : 36
正确答案:
第1空
commands[button]
第2空
turnLight(0)
第3空
setChannel(1)
第4空
lightCommand;tvCommand
第5空
RemoteController
第6空
Light;TV
第7空
TV; Light
某信息咨询公司推出收费的在线商业信息查询模块,需要对查询用户进行身份验证并记录查询日志,以便根据查询次数收取查询费用,现使用代理模式设计该系统,所得类图如图1所示:
图1
在图1中,AccessValidator类用于验证用户身份,它提供方法validate()来实现身份验证;Logger类用于记录用户查询日志,它提供方法log()来保存日志;RealSearcher类实现查询功能,它提供方法doSearch()来查询信息。ProxySearcher作为查询代理,维持对RealSearcher对象、AccessValidator对象和Logger对象的引用。
【Java代码】
class AccessValidator
{
public boolean validate(String userId)
{
//身份验证实现代码省略
}
}
class Logger
{
public void log(String userId)
{
//日志记录实现代码省略
}
}
interface Searcher
{
public String doSearch(String userId,String keyword);
}
class RealSearcher implements Searcher
{
public String doSearch(String userId,String keyword)
{
//信息查询实现代码省略
}
}
class ProxySearcher (1)
{
private RealSearcher searcher = new RealSearcher();
private AccessValidator validator;
private Logger logger;
public String doSearch(String userId,String keyword)
{
//如果身份验证成功,则执行查询
if( (2) )
{
String result = searcher.doSearch(userId,keyword);
(3) ; //记录查询日志
(4) ; //返回查询结果
}
else
{
return null;
}
}
public boolean validate(String userId)
{
validator = new AccessValidator();
(5) ;
}
public void log(String userId)
{
logger = new Logger();
(6) ;
}
}
class Test
{
public static void main(String args[])
{
(7) ; //针对抽象编程,客户端无须分辨真实主题类和代理类
searcher = new ProxySearcher();
String result = searcher.doSearch(“Sunny”,“Money”);
//此处省略后续处理代码
}
}
本题已答 : 30
正确答案:
第1空
implements Searcher
第2空
validate(userId)
第3空
log(userId)
第4空
return result
第5空
return validator.validate(userId)
第6空
logger.log(userId)
第7空
Searcher searcher
某公司的组织结构图如图1所示,现采用组合设计模式来设计,得到如图2所示的类图。
图1 组织结构图
图2 类图
其中Company 为抽象类,定义了在组织结构图上添加(Add)和删除(Delete)分公司/办事处或者部门的方法接口。类ConcreteCompany表示具体的分公司或者办事处,分公司或办事处下可以设置不同的部门。类HRDepartment和FinanceDepartment 分别表示人力资源部和财务部。
【Java代码】
import java.util.*;
(1) Company {
protected String name;
public Company(String name) { (2) = name; }
public abstract void Add(Company c); // 增加子公司、办事处或部门
public abstract void Delete(Company c); // 删除子公司、办事处或部门
}
class ConcreteCompany extends Company {
private List< (3) > children = new ArrayList< (4) >(); // 存储子公司、办事处或部门
public ConcreteCompany(String name) { super(name); }
public void Add(Company c) { (5) .add©; }
public void Delete(Company c) { (6) .remove©; }
}
class HRDepartment extends Company {
public HRDepartment(String name) { super(name); }
// 其他代码省略
}
class FinanceDepartment extends Company {
public FinanceDepartment(String name) { super(name); }
// 其他代码省略
}
public class Test {
public static void main(String[] args) {
ConcreteCompany root = new ConcreteCompany(“北京总公司”);
root.Add(new HRDepartment(“总公司人力资源部”));
root.Add(new FinanceDepartment(“总公司财务部”));
ConcreteCompany comp = new ConcreteCompany(“上海分公司”);
comp.Add(new HRDepartment(“上海分公司人力资源部”));
comp.Add(new FinanceDepartment(“上海分公司财务部”));
(7) ;
ConcreteCompany comp1 = new ConcreteCompany(“南京办事处”);
comp1.Add(new HRDepartment(“南京办事处人力资源部”));
comp1.Add(new FinanceDepartment(“南京办事处财务部”));
(8) ; // 其他代码省略
}
}
本题已答 : 36
正确答案:
第1空
abstract class
第2空
this.name
第3空
Company
第4空
Company
第5空
children
第6空
children
第7空
root.Add(comp)
第8空
comp.Add(comp1)
必答 (填空题)
现欲实现一个图像浏览系统,要求该系统能够显示BMP、JPEG和GIF三种格式的文件,并且能够在Windows和Linux两种操作系统上运行。系统首先将BMP、JPEG和GIF三种格式的文件解析为像素矩阵,然后将像素矩阵显示在屏幕上。系统需具有较好的扩展性以支持新的文件格式和操作系统。为满足上述需求并减少所需生成的子类数目,采用桥接设计模式进行设计所得类图如图1所示。
图1 类图
采用该设计模式的原因在于:系统解析BMP、JPEG与GIF文件的代码仅与文件格式相关,而在屏幕上显示像素矩阵的代码则仅与操作系统相关。
【Java代码】
class Matrix { //各种格式的文件最终都被转化为像素矩阵
//此处代码省略
}
interface ImageImp {
public void doPaint(Matrix m); //显示像素矩阵m
}
class WinImp implements ImageImp {
public void doPaint(Matrix m) {/调用Windows系统的绘制函数绘制像素矩阵/}
}
class LinuxImp implements ImageImp {
public void doPaint(Matrix m) {/调用Linux系统的绘制函数绘制像素矩阵/}
}
abstract class Image {
public void setImp(ImageImp imp) {
(1) = imp; }
public abstract void parseFile(String fileName);
protected (2) imp;
}
class BMP extends Image {
public void parseFile(String fileName) {
//此处解析BMP文件并获得一个像素矩阵对象m
(3) ;//显示像素矩阵m
}
}
class GIF extends Image {
//此处代码省略
}
class JPEG extends Image {
//此处代码省略
}
public class Main{
public static void main(String[] args){
//在Windows操作系统上查看demo.bmp图像文件
Image image1 = (4) ;
ImageImp imageImp1 = (5) ;
(6) ;
image1.parseFile(“demo.bmp”);
}
}
现假设该系统需要支持10种格式的图像文件和5种操作系统,不考虑类Matrix和类Main,若采用桥接模式则至少需要设计 (7) 个类。
正确答案:
第1空
this.imp
第2空
ImageImp
第3空
imp.doPaint(m)
第4空
new BMP()
第5空
new WinImp()
第6空
image1.setImp(imageImp1)
第7空
17
必答[填空题]
某公司欲开发一款儿童玩具汽车,为了更好地吸引小朋友的注意力,该玩具汽车在移动过程中伴随着灯光闪烁和声音提示,在该公司以往的产品中已经实现了控制灯光闪烁和声音提示的程序,为了重用先前的代码并且使得汽车控制软件具有更好的灵活性和扩展性,使用适配器模式设计该系统,所得类图如图1所示。
图1 类图
在图1中,CarController类是汽车控制器,它包括三个方法用于控制汽车的行为,其中move()用于控制汽车的移动,sound()用于控制汽车的声音,light()用于控制汽车灯光的闪烁, sound()和light()是抽象方法。Sound类是抽象声音类,其方法phonate()用于实现声音提示,在其子类ConcreteSound中实现了该方法;Lamp类是灯光类,其方法twinkle()用于实现灯光闪烁,在其子类ConcreteLamp中实现了该方法。CarAdapter充当适配器,它通过分别调用Sound类的phonate()方法和Lamp类的twinkle()方法实现声音播放和灯光闪烁。
【Java代码】
abstract class Sound {//抽象声音类
public abstract void phonate();
}
class ConcreteSound extends Sound {//具体声音类
public void phonate() {
System.out.println(“声音播放!”);
}
}
abstract class Lamp { //抽象灯光类
public abstract void twinkle();
}
class ConcreteLamp extends Lamp{ //具体灯光类
public void twinkle(){
System.out.println(“灯光闪烁!”);
}
}
(1) CarController {//汽车控制器
public void move() {
System.out.println(“汽车移动!”); }
public abstract void sound();
public abstract void light();
}
class CarAdapter (2) { //汽车适配器
private Sound sound;
private Lamp lamp;
public CarAdapter(Sound sound,Lamp lamp){
(3) ;
(4) ;
}
public void sound() {
(5) ; //声音播放
}
public void light() {
(6) ; //灯光闪烁
}
}
class Client{
public static void main(String args[]){
Sound sound;
Lamp lamp;
CarController car;
sound = new ConcreteSound();
lamp = new ConcreteLamp();
car = (7) ;
car.move();
car.sound();
car.light();
}
}
在本实例中,使用了 (8) (填写类适配器或对象适配器)模式。
本题已答 : 36
正确答案:
第1空
abstract class
第2空
extends CarController
第3空
this.sound = sound
第4空
this.lamp = lamp
第5空
sound.phonate()
第6空
lamp.twinkle()
第7空
new CarAdapter(sound, lamp)
第8空
对象适配器
必答 (简答题)
在某图形库API中提供了多种矢量图模板,用户可以基于这些矢量图创建不同的显示图形,图形库设计人员设计的初始类图如下所示:
在该图形库中,每个图形类(如Circle、Triangle等)的init()方法用于初始化所创建的图形, setColor()方法用于给图形设置边框颜色,fill()方法用于给图形设置填充颜色,setSize()方法用于设置图形的大小,display()方法用于显示图形。
客户类(Client)在使用该图形库时发现存在如下问题:
(1) 由于在创建窗口时每次只需要使用图形库中的一种图形,因此在更换图形时需要修改客户类源代码;
(2) 在图形库中增加并使用新的图形时需要修改客户类源代码;
(3) 客户类在每次使用图形对象之前需要先创建图形对象,有些图形的创建过程较为复杂,导致客户类代码冗长且难以维护。
现需要根据面向对象设计原则对该系统进行重构,要求如下:
(1) 隔离图形的创建和使用,将图形的创建过程封装在专门的类中,客户类在使用图形时无须直接创建图形对象,甚至不需要关心具体图形类类名;
(2) 客户类能够方便地更换图形或使用新增图形,无须针对具体图形类编程,符合开闭原则。
正确答案:
本练习可以通过单一职责原则和依赖倒转原则进行重构,具体过程可分为如下两步:
(1) 由于图形对象的创建过程较为复杂,因此可以将创建过程封装在专门的类中(这种专门用于创建对象的类称为工厂类),将对象的创建和使用分离,符合单一职责原则;
(2) 引入抽象的图形类Shape,并对应提供一个抽象的创建类,将具体图形类作为 Shape的子类,而具体的图形创建类作为抽象创建类的子类,根据依赖倒转原则,客户端针对抽象图形类和抽象图形创建类编程,而将具体的图形创建类类名存储在配置文件中。
重构之后的类图如下所示:
通过上述重构,在抽象类Creator中声明了创建图形对象的方法create(),在其子类中实现了该方法,用于创建具体的图形对象。客户端针对抽象Creator编程,而将其具体子类类名存储在配置文件config.xml中,如果需要更换图形,只需在配置文件中更改Creator的具体子类类名即可;如果需要增加图形,则对应增加一个新的Creator子类用于创建新增图形对象,再修改配置文件,在配置文件中存储新的图形创建类类名。更换和增加图形都无须修改源代码,完全符合开闭原则。(注:本重构方法即为工厂方法模式,在第3章将对该模式进行进一步讲解并提供实例代码来实现该模式。)
简答题]
根据如下描述绘制类图。
某商场会员管理系统包含一个会员类(Member),会员的基本信息包括会员编号(memberNo)、会员姓名(memberName)等,会员可分为金卡会员(GoldMember)和银卡会员(SilverMember)两种,不同类型的会员在购物时可以享受不同的折扣(discount);每个会员可以拥有一个或多个订单(Order),每一个订单又可以包含至少一条商品销售信息(ProductItem),商品销售信息包括订单编号(orderNo)、商品编号(productNo)、商品数量(productNum)、商品单价(productPrice),折扣(productDiscount);每一条商品销售信息对应一类商品(Product),商品信息包括商品编号(productNo)、商品名称(productName)、商品单价(productPrice)、商品库存量(productStuck)等。
本题已答 : 27
正确答案:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)