设计模式考试资料
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、编程论述题:框架设计者
1. 以SortTest和IntSort为例,说明什么是控制反转IoC
(1)SortorTest->BubbleSort。
客户依赖具体的服务BubbleSort。
(2)SortorTest->IntSort<=BubbleSort。
应用程序SortorTest遵循针对接口编程/抽象依赖原则,依赖抽象类型IntSort,而BubbleSort自然地依赖父类型。
(3)【SortorTest->IntSort】。
这一步是关键。
如果需要将控制模块SortorTest设计成框架,可以将控制模块SortorTest和它必须依赖的抽象类型IntSort打包。
控制模块SortorTest从应用程序(上层模块)变为框架(下层模块),为控制反转(Inversion ofControl)。
最好能够提供JavaDoc,并且将方法名sort 改成soooooort。
(4)Main->【SortorTest->IntSort】<=BubbleSort。
(其他程序员)使用框架。
(5)带main的BubbleSort,BubbleSort->【SortorTest ->IntSort】<=BubbleSort。
与第一步“依赖倒置”。
反映了某些应用程序员的失落心情:从自由自在变为受控于人
2.什么是抽象依赖原则,它与应用程序、框架的关系
是程序要依赖于抽象接口,不要依赖于具体实现。
简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
在应用程序设计和框架设计中,抽象依赖原则/ADP均有重要作用。
在应用程序的编程中,可以把ADP视为一种规劝或忠告;而在框架设计中,依赖抽象类型则是条例和军规。
3. 有一个接口如下
package util;
@FunctionalInterface public interface DoubleOP {
double op(double m,double n) ;
}
编写应用程序App,以DoubleOP 的实现类、匿名类和λ表达式提供回调函数,分别完成m-n、m*n*n和求m与n大值功能。
package abc;
import tool.God;
import util.DoubleOP;
import util.Main;
public class App{
public static void main(String[] args){
DoubleOP f = (DoubleOP)God.create("2065");
double d = Main.runFramework(f,1.2,3.4);
System.out.println(d);
}
public static void test(){
double d = Main.runFramework(new HelloWorld(),1,3);
System.out.println(d);
DoubleOP f = (double m,double n)->{return m * n *n;};
d = Main.runFramework(f,1,3);
System.out.println(d);
f = (m,n)->{return m +2*n ;};
d = Main.runFramework(f,1,3);
System.out.println(d);
d = Main.runFramework((m,n)->{return m-n ;},1,3);
System.out.println(d);
}
}
二、论述题:Parnas原则
1. 什么是Parnas原则,它在软件设计中的地位
Parnas原则被称为接口与实现的分离。
Parnas原则是软件工程中最重要的原则,之所以将它作为面向对象的三大原则,是因为“该原则在对象技术中的一系列的推广和应用,Parnas原则是功能抽象的核心,也是数据抽象、封装的底层依据”。
3.什么是针对接口编程,什么是类的接口,Java中如何限定类的接口
类的接口指外界对象能够访问的、类所定义的接口的集合。
通常,类中声明的public、protected域,也作为类接口的一部分。
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。
在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。
小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。
面向接口编程就是指按照这种思想来编程。
(1)接口不提供代码重用性。
接口并不能使你获得代码继承的好处,它没有代码重用能力。
接口的意义在于获得程序设计上的高度可维护性。
(2)接口能够通过多继承进行扩展父接口。
Java支持接口多继承。
interface A extends B, C, D{}
(3)接口不能升级。
定义接口时要考虑周全,因为接口有个十分尴尬的限制:如果定义了接口,将接口送给客户程序员使用,这时定义的接口就不能修改。
因为在接口中添加一个方法,会导致老版本接口的所有实现类的中断。
为了达到在接口中增加一个方法的目的,只能派生/创建一个新的接口。
(所以,Java接口天然符合OCP/开放封闭原则)
三、编程论述题:装饰模式
1.以类图介绍装饰模式,介绍其意图、角色。
意图:装饰者模式动态地将责任附加到对象身上。
若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
角色:
●抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
●具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
●装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
●具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
2. 按照Num模型,讨论职业/ IProfession类层次。
IProfession定义了方法say(String),其实现类教师/ Teacher、医生、律师……按照其职业习惯给出say(String)的实现,这些实现类相当于Num模型中的Zero。
而才艺/Talent类层次(相当于NextOne),可以装饰基本对象,say(String)实现中,在必要的时候会说english、在必要的时候会唱几句……
public interface IProfession {
abstract public void say(String s);
}
package closure.decorator;
import static yqj2065.util.Print.pln;
public class Teacher implements IProfession{
@Override public void say(String s){ pln(teach(s)) ; }
public String teach(String s){
String myStyle="讲解["+s+"]";
return myStyle;
}
}
package closure.decorator;
public abstract class Talent implements IProfession {
protected IProfession base;
public Talent(IProfession base) { this.base = base; }
}
package closure.decorator;
public class TSong extends Talent{
public TSong(IProfession base) {
super(base);
}
@Override public void say(String s){
s = this.song(s);
base.say(s); //
}
public String song(String s){
String strengthen="旋律("+s+")";
return strengthen;
}
}
四什么是抽象依赖原则?
抽象依赖原则是开放封闭原则的主要内容,是“针对接口编程”的一种直观的说法父类型依赖子类型,定义自然数?
package closure.style;
public abstract class Num {
@Override public String toString() {
return "zero";
}
}
public final class Zero extends Num{}
public final class NextOne extends Num{
Num pre;//predecessor
NextOne(Num pre){ this.pre = pre; }
@Override public String toString() {
return "new " +NextOne.class.getSimpleName()+ "(" + pre + ")";
}
}
//class Demo
public static void testNum() {
Num n = new NextOne( new NextOne( new NextOne(new Zero())));
pln(n);
}
五、编程论述题:模板方法模式
1. 以类图介绍模板方法模式,介绍其意图、角色。
或见216页
意图:214页
角色:
抽象模板:定义了一个模板方法和若干抽象方法和具体方法,
具体模板:继承抽象模板类并实现抽象方法
客户端Main
2.下面的抽象类Sum封装了求和函数getSum,
package templateMethod;
public abstract class Sum{
public final double getSum(int a,int b){
double sum=0;
for(int i =a;i<=b; i=next(i)){
sum+=item(i);
}
return sum;
}
public int next(int i){return i++;}
public abstract double item(int x);
}
为什么需要设计IItem、INext分别封装一个抽象方法?并设计4参数的模板方法
若干个求和函数拥有相似的代码结构,代码中不同/变化的部分主要有2处:步进和累加项,可变的两部分设计为抽象方法。
注意,有些代码结构的不同部分,如求和函数返回值不同,则取更大的数据类型;参数个数不同,则取最多的个数;如求PI时返回sum*8,而乘以系数,可以留给调用者自己添加。
public final double getSum(int a,int b,INext iNext,IItem iItem){
//添加代码
}
添加的代码为:
double sum = 0;
for (; a <= b; a = next(a)) {
sum += item(a);
}
return sum;
}
public abstract double item(int x);
public abstract int next(int x);
}
4.说明对“模板方法是一种代码复用的基本技术。
它们在类库中尤为重要,它们提取了类库中的公共行为”的理解。
见设计模式p216页7效果下面
B卷
一、编程论述题:行为参数化
1. 什么是行为参数化,为什么需要行为参数化
★行为参数化,是指将(模板)方法中可变部分设计成该方法的参数,使得该方法具有通用性。
行为参数化的意义,使得函数solve()符合OCP。
从实现技术上看,描述行为参数化的最佳术语或许是高阶函数。
当使用Java实现时,使用一个抽象方法boolean test(int n)来描述进行"何种"测试,但会出现若干不同的结构。
2.什么是闭包,说明C语言在此的缺陷
(1)闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问不在当前作用域范围内的一个函数。
闭包必须是由直接或间接的嵌套函数方式定义的函数。
(2)★闭包/closure ,指一个作为(外包函数)返回值的函数,它捕获外包函数的变量。
C语言中函数能够捕获全局变量,它没有闭包概念
(3)以函数作为参数或返回值的函数,称为高阶函数(Higher-Order function)。
将函数作为返回值,是一种重要的高阶函数形式。
C语言就能够返回已有的函数,即它支持这一种高阶函数;但是,C语言不支持嵌套函数。
3. 什么是高阶函数,其意义是什么
以函数为参数、或以函数为返回值的函数是一个高阶函数。
高阶函数之函数作为参数的原因:若干个函数拥有相似的算法或代码结构,对此加以抽象。
四、论述题:控制反转
1.什么是好莱坞原则,什么是控制反转IoC?(6分)
上层模块需要获得数据/处理事件时,可以轮询和通知。
★好莱坞原则的核心:采用通知而非轮询[1]。
好莱坞原则=通知机制
好莱坞原则,运用于分层结构中,上层模块需要获得数据。
上层模块可以采用轮询获得数据,也可以应用通知方式/好莱坞原则。
一般而言,上层模块Client (即you)调用下层模块Server(即me)天经地义,但是,在获得数据时,下层模块Server(即me)会说“请你不要轮询/骚扰我,我通知你”。
2.框架向上层传递数据时,可以采用通知或轮询,比较两者的优缺点。
(6分)
通知只提醒一次。
轮询——不停地查询。
Server将进度数据保存在一个成员变量x中,并提供getX()。
这样Client 就可以时时刻刻地查询该数据。
轮询方式下,Client0在一个线程中让Server0努力的复制,主线程则在while(true)中随时随地的查询数据,直到复制进度数据为100才结束。
五、编程论述题:适配器模式.
1. 以类图介绍适配器模式,介绍其意图、角色。
(5分)
意图:将一个类的接口转换成客户希望的另外一个接口。
适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
角色:
1)目标接口(Target):客户所期待的接口。
目标可以是具体的或抽象的类,也可以是接口(2)需要适配的类(Adaptee):需要适配的类
(3)适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口
2.假定接口Animal(生命) 有抽象方法eat()、move (),它的实现类有猫/Cat、狗/Dog等等;而第三方提供的(已经存在的)鸟/Bird类,有接口如eat()和fly();编写BirdWrapper,使得Client可以如下统一处理Animal。
package delegate.adapter;
public class Bird{//来自第三方的或不可修改的类
public void eat(){
System.out.println("Bird.eat()");
}
public void fly(){
System.out.println("Bird.fly()");
}
}
package delegate.adapter;
public class BirdWrapper extends Bird implements Being {
@Override public void eat(){ //此方法可以省略
super.eat();
}
@Override public void run(){
super.fly(); // super.可以省略
}
}
3.包装类BirdWrapper与遗留类Bird,既可以是Is-A的继承关系,也可以Has-A的组合关系。
编写上面一小题的另外版本的BirdWrapper,并比较两者的优缺点。
(8分)
public interface Robot{
public void battery();
public void move();
}
package delegate.adapter;
public class RobotWrapper implements Being{ private Robot r;
public RobotWrapper(){
//任一创建对象的模式
}
@Override public void eat(){
r.battery();//
}
@Override public void run(){
r.move();
}
}。