不懂为什么代理模式本质上与对象适配器模式是一致的
目录
解析:软件设计模式的核心目标是解决软件开发中的复用性、可维护性和可扩展性等问题,其两大主题可归纳为系统复用与系统扩展。
Farmer类包含一个Inquirer类型的成员变量inquirer
Farmer通过该成员变量调用Inquirer的方法,表明两者存在依赖关系
而泛化是继承关系;实现是类对接口的关系;包含关系是用在用例图的,更是错
解析:开闭原则就是对扩展开放,对修改关闭;易扩展就需要面向抽象设计;而OCP 的目的是让系统在需求变更时具备弹性,通过预先设计抽象层应对未来变化。
对于DIP和OCP的关系,前者要求高层模块不依赖低层模块,后者通过依赖抽象,系统更易于扩展。两者都是依赖抽象的,不矛盾,而是相辅相成
6.对违反里氏代换原则的两个类,以下可以采用的候选解决方案中错误的是( )
解析: 里氏代换原则就是子类对象必须能够替换其父类对象而不影响程序正确性。在这个定义的前提下我们来分析选项:
A就是提取公共方法,符合要求;B:委派关系就是组合,单独的方法是可以通过委派替代的;C:hsa-a就是拥有,is-a就是对应LSP,这里和B是一样的情况
解析: 创建型关注创建逻辑;结构型关注组合方式;行为型关注交互;而这里面只有C是结构型,为其他对象提供代理,控制对原对象的访问(如远程代理、缓存代理)
9.不同品牌的手机应该由不同的公司制造,如三星公司生产三星手机,苹果公司生产苹果手机。该场景蕴含了 ( )设计模式。
解析:这就是和前面说到的手机是抽象产品,公司是抽象工厂,互相对应,这就是工厂方法
10. 以下对于使用Object类的clone()方法复制的对象描述错误的是 ( ) 。
解析:Object.clone()方法用于创建对象副本,默认实现为浅拷贝(Shallow Copy),即:
11, 在 ( ) 时可使用单例模式(Singleton)。
解析:单例模式确保一个类仅有一个实例,并提供全局访问点,而选项里面只有D是符合的;A适合适配器模式,B适合装饰器模式,C适合工厂模式
12. 关于模式适用性, 不适合使用适配器模式(Adapter)的是( ) 。
解析:A在前面就提到了, 典型场景;C和D本质是一个意思,都是将父类接口封装以适配所有子类。
13. 以下关于桥接模式(Bridge)叙述错误的是( ) 。
解析:桥接模式的核心是:将抽象部分与实现部分分离,使它们可以独立变化。通过组合(关联关系)替代继承,降低系统耦合度。所以AB就是对了,D是另外一个说法,一个意思;而C是在描述装饰器模式。
14. 以下用来描述包含桥接模式(Bridge)的意图是 ( ) 。
解析:和前面一样,A是装饰器模式,C是抽象工厂模式,D是建造者模式
15. Java IO库的设计使用了装饰模式(Decorator), 局部类图如图1所示, 其中类( )是充当具体构件角色(ConcreteComponent)。
16. 以下关于代理模式(Proxy)的叙述错误的是 ( ) 。
解析:代理模式为某一个对象提供一个代理,并由代理对象控制对原对象的引用,主要用于在不改变原对象的情况下,为其增加额外功能。C是定义错误,远程代理是用在地址空间不同的时候,对于使用权限应该考虑保护代理
18.关于模式适用性, 以下 ( ) 不适合使用命令模式(Command)。
19. 以下选项可用来描述观察者(Observer)的是 ( )。
解析:着重讲一下D:目标可以不关联任何观察者(如一个未被订阅的新闻主题)
一、单项选择题
1.软件设计模式的两大主题是 ( )。
A.系统的维护与开发 B.对象组合与类的继承
C、系统架构与系统开发 D.系统复用与系统扩展
解析:软件设计模式的核心目标是解决软件开发中的复用性、可维护性和可扩展性等问题,其两大主题可归纳为系统复用与系统扩展。
答案:D
2.以下对于接口和抽象类的描述正确的是 ( )。
A.抽象类中只能有抽象方法,不能有方法体的普通方法。
B.接口不能被实例化,但抽象类可以定义构造函数,并可以使用构造函数实例化对象。
C.接口是一种极度抽象的类型,它比抽象类更加“抽象”。
D.如果子类没有实现父类的抽象方法,则可以将子类定义为普通类。
解析:老生常谈,秒了。
答案:C
3.根据下面的代码,下面那些叙述正确的是 ( ·)。
public class Farmer {
private Inquirer inquirer;
public void plant() {
inquirer. research();
}
}
A. Farmer类和Inquirer 类之间存在关联关系(Association)
B. Farmer类和Inquirer类之间存在泛化关系(Generalization)
C. Farmer类和Inquirer类之间存在实现关系(Realization)
D. Farmer类和Inquirer类之间存在包含关系(Inclusion)
解析:从代码来看:
Farmer
类包含一个Inquirer
类型的成员变量inquirer
Farmer
通过该成员变量调用Inquirer
的方法,表明两者存在依赖关系
而泛化是继承关系;实现是类对接口的关系;包含关系是用在用例图的,更是错
答案:A
4.下列有关开放/闭合原则表述不正确的是 ( )。
A.开放/闭合原则要求代码“对修改关闭,对扩展开放”,
B.开放/闭合原则要求代码多使用面向抽象设计。
C. 开放/闭合原则是“为变化而设计”(Design -for Changes)的一种规范。
D.依赖倒转原则与开放/闭合原则是相互矛盾的。
解析:开闭原则就是对扩展开放,对修改关闭;易扩展就需要面向抽象设计;而OCP 的目的是让系统在需求变更时具备弹性,通过预先设计抽象层应对未来变化。
对于DIP和OCP的关系,前者要求高层模块不依赖低层模块,后者通过依赖抽象,系统更易于扩展。两者都是依赖抽象的,不矛盾,而是相辅相成
答案:D
5、在接口隔离原则中,下列说法错误的是 ( )。
A、一个类对另外一个接口的依赖性应当是建立在最小的接口上。
B. 客户端不应该依赖那些它不需要的接口,因此一个接口必须只定义一个方法。
C、没有关系的接口合并在一起,会造成接口污染。
D、一个接口代表一个角色,不应当将不同的角色都交给一个接口。
解析:这个一眼肯定就是B
答案:B
6.对违反里氏代换原则的两个类,以下可以采用的候选解决方案中错误的是( )
A、创建一个新的抽象类C,作为两个具体类的父类,将A和B共同的行为移动到C中,从那解决A和B行为不完全一致的问题。
B.将B到A 的继承关系改成委派关系。
C. 区分二者之间是“is-a”还是“has-a”。如果是“is-a”, 则可以使用继承关系: 如果是“has-a”则应该改为委派关系。
D.以上方案都不对。
解析: 里氏代换原则就是子类对象必须能够替换其父类对象而不影响程序正确性。在这个定义的前提下我们来分析选项:
A就是提取公共方法,符合要求;B:委派关系就是组合,单独的方法是可以通过委派替代的;C:hsa-a就是拥有,is-a就是对应LSP,这里和B是一样的情况
答案:D
7.以下不属于创建型设计模式的是( )。
A. 建造者模式(Builder Pattern) B. 工厂方法模式(Factory Metho d Pattern)
C. 代理模式(Proxy Pattern) D. 原型模式(Prototype. Pattern)
解析: 创建型关注创建逻辑;结构型关注组合方式;行为型关注交互;而这里面只有C是结构型,为其他对象提供代理,控制对原对象的访问(如远程代理、缓存代理)
答案:C
8.以下代码使用了 ( )模式。
public abstract class Operation {
private double numberA . = 0;
private double numberB = 0;
public abstract double result();
public class OperationAdd extends Operation {
@Override
public double result() {
return numberA⁻+ numberB;
}}
public class OperationSub extends Operation{
@Override
public double result() {
return numberA - numberB;
}}
public class OperationFactory (
public static Operation create(String sign) {
switch(sign) (
coles e *+*:
return new OperationAdd();
case "-":
return new OperationSub();
default:
return null;
}}
A. 简单工厂(Simple Factory) B.·工厂方法(Factory Method)
C. 抽象工厂(Abstract Factory) D.未使用
解析: 从代码来看,里面就三个部分:抽象类、具体子类、静态工厂类,所以就是简单工程;而简单工厂和工厂方法的区别在于:前者的工厂类型是具体类,后者是抽象类或者接口;同时新增产品的时候前者是需要修改工厂类的,这是违反开闭原则的。
也就是说,你看到工厂是抽象的时候再选B
答案:A
9.不同品牌的手机应该由不同的公司制造,如三星公司生产三星手机,苹果公司生产苹果手机。该场景蕴含了 ( )设计模式。
A. 简单工厂(Simple Factory) B. 工厂方法(Factory Method)
C. 抽象工厂(Abstract Factory) D. 建造者(Builder)
解析:这就是和前面说到的手机是抽象产品,公司是抽象工厂,互相对应,这就是工厂方法
答案:B
10. 以下对于使用Object类的clone()方法复制的对象描述错误的是 ( ) 。
A. 对于对象obj, 都有obj. clone()==obj。
B. 对于对象obj, 都有obj. clone(). getClass()==obj. getClass()。
C. 对于对象obj的成员对象member, 都有obj. clone(). getMember()==obj. getMember()。
D. 对于对象obj的成员对象member, 都有obj. clone(). getMember(). getClass()==obj. getMember(),getClass()。
解析:Object.clone()
方法用于创建对象副本,默认实现为浅拷贝(Shallow Copy),即:
基本类型成员:直接复制值。
引用类型成员:复制引用(新旧对象的引用成员指向同一实例)。
==
:比较引用地址是否相同。
getClass()
:返回对象的运行时类。
所以A是错的,这里比较引用地址,clone创建的是新对象;B就是对的,clone()
创建的是原类的实例,未改变类的类型;C也是正确的,前面说到==是比较引用地址,此时两者引用同一成员对象,类必然相同;D也是对的,跟B差不多,就是因为类型没有改变
答案:A
11, 在 ( ) 时可使用单例模式(Singleton)。
A.使用一个已有的查找算法而不想修改既有代码。
B.不能创建子类,需要扩展一个数据过滤类。
C.隔离菜单项对象的创建和使用。
D.防止一个资源管理器窗口被实例化多次,
解析:单例模式确保一个类仅有一个实例,并提供全局访问点,而选项里面只有D是符合的;A适合适配器模式,B适合装饰器模式,C适合工厂模式
答案:D
12. 关于模式适用性, 不适合使用适配器模式(Adapter)的是( ) 。
A.用户想使用一个已经存在的类,而它的接口不符合用户的需求。
B.如果删除对象的外部状态, 那么可以用相对较少的共享对象取代很多组对象。
C:想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
D.用户想创建一个可以复用的类, 该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
解析:A在前面就提到了, 典型场景;C和D本质是一个意思,都是将父类接口封装以适配所有子类。
答案:B
13. 以下关于桥接模式(Bridge)叙述错误的是( ) 。
A、桥接模式的用意是将抽象部分与实现部分解耦,使得两者可以独立地变化。
B.桥接模式将继承关系转换成关联关系,从而降低了系统的耦合度。
C.桥接模式可以动态的给一个对象增加功能,这些功能也可以动态地撤销。
D.桥接模式可以从接口中分离实现功能,使得设计更具有扩展性。
解析:桥接模式的核心是:将抽象部分与实现部分分离,使它们可以独立变化。通过组合(关联关系)替代继承,降低系统耦合度。所以AB就是对了,D是另外一个说法,一个意思;而C是在描述装饰器模式。
答案:C
14. 以下用来描述包含桥接模式(Bridge)的意图是 ( ) 。
A.动态地给一个对象添加一些额外的职责。
B.将抽象部分与它的实现部分分离,使它们都可以独立变化。
C.提供一个创建一系列相关或相互依赖对象的接口,面无须指定它们具体的类。
D.将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
解析:和前面一样,A是装饰器模式,C是抽象工厂模式,D是建造者模式
答案:B
15. Java IO库的设计使用了装饰模式(Decorator), 局部类图如图1所示, 其中类( )是充当具体构件角色(ConcreteComponent)。
A. OutputStream B. FileOutputStream
C. FilterOutputStream D. BufferedOutputStream
解析:装饰模式包含以下角色:
- 抽象构件(Component):定义一个抽象接口,用于规范需要装饰的对象(原始对象)。
- 具体构件(ConcreteComponent):实现抽象构件接口,是被装饰的原始对象 。
- 抽象装饰(Decorator):持有一个抽象构件的引用,定义与抽象构件一致的接口,用于装饰具体构件。
- 具体装饰(ConcreteDecorator):实现抽象装饰类,通过附加责任来装饰具体构件。
最顶上的是抽象类,而FilterOutputStream
继承自 OutputStream
,它内部持有一个 OutputStream
类型的引用(os: OutputStream
),在装饰模式中充当抽象装饰(Decorator) 角色,用于给其他输出流添加额外功能 。
答案:B
16. 以下关于代理模式(Proxy)的叙述错误的是 ( ) 。
A.代理模式能够协调调用者和被调用者,从而在一定程序上降低系统的耦合度。
B.代理模式的缺点是请求的处理速度会变慢,并且实现代理模式需要额外的工作。
C.控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限时可以考虑使用远程代理。
D.代理模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
解析:代理模式为某一个对象提供一个代理,并由代理对象控制对原对象的引用,主要用于在不改变原对象的情况下,为其增加额外功能。C是定义错误,远程代理是用在地址空间不同的时候,对于使用权限应该考虑保护代理
答案:C
17.关于行为型设计模式说法不正确的是 ( )。
A.行为对象模式使用继承而不是对象复合。
B.行为设计模式设计算法和对象职责的分配。
C.行为类模式使用继承机制在类间分派行为。
D.行为模式不仅描述对象或类的模式,还描述他们之间的通信模式。
解析:前面我们说到,行为型关注的是对象的交互和职责分配,分为行为类模式(使用继承机制)和行为对象模式(使用对象组合 / 复合),A就是错误的;B选项就是经典场景,C就是解释定义,D:描述通信模式本质也就是在描述交互
答案:A
18.关于模式适用性, 以下 ( ) 不适合使用命令模式(Command)。
A.抽象出特执行的动作以参数化某对象,使用过程语言中的回调(callback)函数表达这种参数化机制。
B.窗体程序中的事件处理。
C.在需要用比较通用和复杂的对象指针代替简单的指针的时候。
D. 一个系统需要支持交易(Transaction),一个交易结构封装了一组数据更新命令。
解析:命令模式将 “请求” 封装为对象(Command),使请求的发送者(Invoker)和接收者(Receiver)解耦,支持请求的参数化、排队、记录、撤销等操作。而这里面只有C是不符合的,也就是指针替代这种场景并不是命名模式该管的
答案:C
19. 以下选项可用来描述观察者(Observer)的是 ( )。
A.将抽象部分与它的现实部分分离,使它们都可以独立变化。
B.定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
C.用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
D.使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
解析:A是桥接,C是原型,D是职责链
答案:B
20. 在观察者模式(Observer)中, ( ) 。
A. 一个目标(Subject)对象可对应多个观察者(Observer)对象
B. 目标(Subject)只能有一个具体目标(ConcreteSubject)子类
C. 观察者(Observer)只能有一个具体观察者(Concrete0bserver)子类
D. 一个目标(Subject)对象必须至少对应一个观察者(Observer)对象
解析:着重讲一下D:目标可以不关联任何观察者(如一个未被订阅的新闻主题)
答案:A
二、判断题
1.在GOF 提出的设计模式中,按照范围分为创建型、结构型和行为型。 ( × )
2.抽象类是对一种事物的抽象,而接口是对行为的抽象,因此接口比抽象类还要抽象。( √ )
3.里氏代换原则要求子类尽量不要覆写父类方法,在一定程度上约束了继承泛滥问题。( √ )
4.当完全满足接口隔离原则后,将会导致接口数量爆炸、代码可读性下降,因此系统设计时应该遵守该原则。 ( × )
5.为了降低多层系统间的耦合度,提高类的可扩展性和复用性,在界面表示层和业务逻辑壓之间增加控制层,由控制层来转发表示层对业务逻辑层的调用。 ( √ )
6、对线程池中线程对象的管理是原型模式的典型应用。 ( √ )
7、代理模式本质上与对象适配器模式是一致的。 ( √ )
8、某公司开发一个文档编辑器,该编辑器允许在文档中直接嵌入图形对象,但开销很大。用户在系统设计之初提出编辑器在打开文档时必须十分迅速,可以暂时不显示当前页面以外的图形。针对这种需求,公司可以采用单例模式避免同时创建这些图形对象。 ( × )
9、观察者模式允许用户独立地改变目标和观察者,用户可以在不改动目标和其他观察者的前提下增加新的观察者。 ( √ )
10.在命令模式中,请求以命令的形式包裹在对象中,并传给调用对象。 ( √ )
三、简答题
1、开闭原则的定义是什么,简述其作用。
定义:软件实体应当对扩展开放,对修改关闭。。
作用:使得软件的复用性好,维护性好,易于扩展。
2、简述GOF 设计模式的3大分类的区别。
① 创建型模式主要用于创建对象。
② 结构型模式主要用于处理类或对象的组合。
③ 行为型模式主要用于描述类或对象如何交互和怎样分配职责。
3、描述装饰模式的4个角色及作用。
抽象构件:定义一个抽象接口以规范准备接收附加责任的对象。
具体构件;实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体件的功能。
具体装饰:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
4、商述观察者模式的适用环境。
① 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方封装在独立的对象中使它们可以各自独立地改变和复用。
② 一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
③需要在系统中创建一个触发链。
5.简述代理模式的优缺点。
优点:能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
缺点:由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理);实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)。
四、程序设计题
1.某软件公司想要开发一款商务查询系统(SearchSystem),该系统包含的查询方法(SearchFun)已经开发完成,现在要在不修改原功能的前提下利用验证代理模块(CheckProxy)为查询系统附加一个身份验证方法(IdCheck),现已给出模拟实现该业务的部分代码,见CheckProxy类,请结合代理模式的设计思想,参考现有代码和类图并使用自己所学专业的开发语言完成SearchSystem类、RealSearch类和客户端类代码的实现。 (10分)。
注: 代码中的extends代表继承关系, implements代表实现关系。
(1) CheckProxy 类:
public class CheckProxy extends SearchSystem{
private RealSearch realSearch = new RealSearch();
public void searchFun(){
this. ldCheck();
realSearch. searchFun();
}
public void idCheck() {
System. out. println("进行身份验证");
}
}
(2) SearchSystem类:
public abstract class SearchSystem{
public abstract void searchFun();
}
(3) RealSearch 类:
public class RealSearch extends SearchSystem(
public void searchFun(){
System. out. println("执行查询模块功能");
}
}
客户端:
public class Client{
public static void main(String[] args) {
CheckProxy checkProxy = new CheckProxy();
checkProxy. searchFun();
}
}
运行结果:
进行身份验证
执行查询模块功能
2.现有一种音乐播放软件(MusicSoft),支持2种运行命令模式(Command),手动模式(ManualMode和自动模式(AutoMode) ,在软件上选择手动模式时,可以使用上一首(prev)和下一首(next功能来从音乐库(MusicLibrary) 中选择音乐。选择自动模式, 则播放器会随机(random) 播|音乐库中的音乐。试使用命令模式完成如下要求。
(1)根据题目要求抽取该模式所对应的角色及类。
抽象命令: command
具体命令: ManualMode-手动模式、AutoMode-自动模式
调用者: MusicSoft-音乐软件
接受者: MusicLibrary-音乐库
(2)类图绘制
五、实作题
1.费明程序设计一个简单的动物园场景,要求用户输入动物种类,如狗(Dog)、猫(Cat)、鸟(Bird),解序根据用户的输入动物类型创建相应的动物对象,并输出动物的叫声(Speak)。请使用简单工厂模式设计并实现该功能。
(1)根据题目完成该场景的结构图设计。 (10分)
(2)使用相应编程语言进行模拟实现并调试运行。 (15分)
动物类型枚举:
public enum AnimalType {
Dog,
Cat,
Bird
}
动物抽象类:
public abstract class Animal {
//动物的名称
public string Name { get; set; }
//构造函数,传入动物名称
public Animal(string name) {
this. Name= name;
}
//抽象方法,返回动物的叫声
public abstract string Speak();
具体动物类-狗;
public class Dog:Animal {
public Dog(string name) : base(name) { }
public override string Speak() {
return "汪汪汪";
}
}
具体动物类-猫;
public class Cat:Animal {
public Cat(string name): base(name) ( )
public override string Speak() {
return "喵";
具体辅物类-鸟:
public class Hird : Animal {
public Bird(string name) : base(name) { }
public override string Speak() {
return "叽叽喳喳";
简单工厂类:
public static class AnimalFactory {
//根据动物类型创建相应的动物对象
public static Animal CreateAnimal(AnimalType type, string name) {
switch (type) {
case AnimalType. Dog;
return new Dog(name);
case AnimalType. Cat:
return new Cat(name);
case AnimalType. Bird:
return new Bird(name);
default:
throw new ArgumentException("Invalid animal type,");
测试:
class Program.{
static void Main(string[] args) {
while (true) {
//获取用户输入的动物类型和名称
Console. Write("请输入动物类型 (狗、猫、鸟) ; ");
string typeString= Console. ReadLine();
Console. Write("请输入动物名称: ");
string name = Console. ReadLine();
//将用户输入的动物类型字符串转为枚举类型
AnimalType type;
if(!Enum. TryParse(typeString, out. type)) {
Console. WriteLine("无效的动物类型, 请重新输入。");continue;
//创建相应的动物对象并输出其叫声
Animal animal = AnimalFactory. CreateAnimal(type, name);
Console. WriteLine($"{animal. Name}的叫声是: {animal. Speak()}");
//询问用户是否继续
Console. Write("是否继续? (Y/N) ");
if(Console. ReadLine(). ToLower() !="y"){
break;
}
}