本篇文章基于 程细柱主编的软件设计模式(Java版)一书的期末知识点总结复习。
复习内容根据学校期末试卷知识范围编写,并非涵盖整本书中的所有23种设计模式。
本篇主要探讨软件设计模式期末考试大题中:
诸如给出程序代码选设计模式、画出结构图/类图、解释类图中的成员 此类题的通用做题思路。
(仅用作及格选手拿到题的解题通法)。
一、观察(模式判断)
首先通览代码,观察代码中是否在类中声明了数组 ArrayList 或者 集合 HashMap。
若找到了数组或者集合的声明,则优先考虑是否为享元模式、组合模式、观察者模式或者迭代器模式。
迭代器模式有明显的聚合类(用数组存储将要遍历的对象)和迭代器类(有类似next first hasnext等成员函数)。
聚合类:
public class ClassList implements StudentAggregate{
private List<Student> students = new ArrayList<>();
// 聚合类特征 类名常命名为 xxxList
// 且存在明显声明一个数组List ArrayList或者集合HashMap的地方
@Override
public StudentIterator iterator() {
return new StudentListIterator(students);
}
@Override
public void add(Student student) {
students.add(student);
}
}
迭代器类:
public class StudentListIterator implements StudentIterator{
private List<Student> students;
private int index;
public StudentListIterator(List<Student> students) {
this.students = students;
this.index = 0;
}
@Override
public boolean hasNext() {
return (index < students.size());
}
@Override
public Student next() { //迭代器特征
if (!hasNext()) { // 存在诸如next first hasnext等函数
throw new NoSuchElementException();
}
Student student = students.get(index);
index++;
return student;
}
}
若找到明显特征,则优先确定迭代器模式,若无特征则进一步判断是否为享元模式或者组合模式。
组合模式的主类也就是Main启动类中会存在大量对象的声明,也就是new,接着会有大量的add去给一个对象添加声明好的对象,也就是去构造树状的层次结构。
public static void main(String args[]){
Component c0 = new Composite(); // 有大量的new
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1); // 同时又把new好的对象add
c0.add(c1);
c0.add(leaf2);
c0.add(leaf3);
c0.operation();
}
出现此类特征则可以确定为组合模式。
而享元模式的主类中,存在定义很多存在重复相同的变量(也就是享元角色的具体享元),以及少量不同的特征性变量(非享元角色的非享元信息);或者有大量给对象赋值的特征。
判断是否为享元模式,仅需抓住“享元”中的“元”,也就是变量这一特征即可。
public class FlyweightPatternDemo {
private static final String colors[] =
{ "Red", "Green", "Blue", "White", "Black" };
// 会声明一堆变量
public static void main(String[] args) {
//下面是循环给对象赋值 set变量
for(int i=0; i < 20; ++i) {
Circle circle =
(Circle)ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int)(Math.random()*colors.length)];
}
private static int getRandomX() {
return (int)(Math.random()*100 );
}
private static int getRandomY() {
return (int)(Math.random()*100);
}
}
观察者模式在声明数组或者集合的基础上还会存在一个“通知”的功能,也就是for循环去遍历通知观察者目标发生了改变。
排除以上几个存在数组 ArrayList 或者 集合 HashMap的设计模式之外,
仅剩下了抽象工厂、代理、适配器、装饰、外观、策略、职责链、中介者模式。
八大类。
在这九大类中,策略模式和职责链模式又比较好分辨:
策略模式中会存在一个极为明显特征,即:调用对象的成员函数名相同,但传入对象不同。
public static void main(String args[]){
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();//两次调用的成员函数名相同,但是传入的封装对象不同。
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
策略模式的核心在于封装算法的灵活替换,为了实现替换,则必须统一接口,那么我们只需要去观察是否存在相同的接口函数,而去驱动这些函数的作为参数传入的封装对象不同,则可以判断为策略模式。
职责链模式则去寻找明显的链的特征:
启动主类中存在声明若干个责任类,然后通过类似setNext的函数,将责任类依次添加到一个类中,最后给这个被添加的类,传入一个参数进行责任链的判断。
public static main(String args[]){
Handler h1 = new ConcreteHandler1();
Handler h2 = new ConcreteHandler2();
h1.setNext(h2);
h1.handleRequest("two");
}
通俗来说就是根据我上一篇帖子里对于每个类的定义,去脑海中预想代码的实现形式,然后根据不同模式的特征找到特征代码进而去判断设计模式。
还剩下抽象工厂、代理、适配器、装饰、外观、观察者、中介者模式。
抽象工厂模式:寻找工厂类和产品类,存在声明一个或者多个工厂函数,利用声明的工厂类去创造商品,最后由工厂类输出。
代理模式:由于不能直接调用目标对象,寻找两个高度相似的类,都同时实现相同的接口,但是其中一个类是在另一个类基础上进一步“包装”,提供“中间人”的作用。
适配器模式:存在统一的顶层接口,存在适配器类,其特征是利用extends和implements两个关键字来实现目标顶层接口的适配函数逻辑。
装饰模式:找super关键字,找到就是装饰模式,它和代理模式很类似,但是有独一无二的super关键字。
外观模式:new了一堆算法类,定义了一个外观类由这个外观类统一去调用这些new好的算法类。
中介者模式:找this和equals关键字,存在对象对调的特征也就是 !对象.euqals(另一个对象) 这个特征就是中介者模式。
二、结构图画法
首先完整通读并理解教材从第5页开始到第8页有关UML类图定义的部分内容,理解类的画法及类之间关系的画法。
落实到具体的题中:
首先画出接口和实现接口的实现类,然后如果题干中涉及到抽象类的定义那么接下来画出抽象类及实现类,最后画Main主类。将所有的类与接口画完之后,从接口出发去理类之间的关系(接口基本上都是空心三角箭头的指向,因为实现类实现了这个接口),当理清接口与实现类,抽象类与实现类之间的关系之后,最后去看主类中都调用了什么,画出主类与其他类的关系,最后在草纸上进行简要排版,合理安排空间再誊抄到答题纸上。
如果时间紧迫或者干脆分许不出来题干中用到了什么设计模式又或者大脑一片空白
保命技巧:
1.找到代码里的interface,这就是接口,按照接口画法画出。
2.找到代码里所有class,这就是类,按照类的画法画出。
3.找到所有class后面带implements的类,从这个类出发,指向implements后面哪个名字的接口画虚线,空心箭头指向接口。
4.画出主类(启动类),这个画法是固定的,死记硬背。
5.从主类画一条虚线到所有接口,实线箭头指向接口。
6.祈祷老天保佑,完活。