创建型设计模式实验

实验目的

  1. 结合实例,熟练绘制常见的创建型设计模式结构图
  2. 结合实例,熟练使用任意一种面向对象编程语言实现常见的创建型设计模式
  3. 通过本实验,理解每一种创建型设计模式的模式动机,掌握模式结构,学习如何使用代码实现这些设计模式

实验内容

练习1

使用简单工厂模式设计一个可以创建不同几何形状(Shape)(例如圆形(Circle)、矩形(Rectangle)和三角形(Triangle)等)的绘图工具类,每个几何图形均具有控制方法draw()和擦除方法erase(),要求在绘制不支持的几何图形时,抛出一个UnsupportedShapeException异常。绘制类图并编程模拟实现

练习2

在某网络管理软件中,需要为不同的网络协议提供不同的连接类,例如针对POP3协议的连接类POP3Connection、针对IMAP协议的连接类IMAPConnection、针对HTTP协议的连接类HTTPConnection等。由于网络连接对象的创建过程较为复杂,需要将其创建过程封装到专门的类中,该软件还将支持更多类型的网络协议。现采用工厂方法模式进行设计,绘制类图并编程模拟实现

练习3

某系统为例改进数据库操作的性能,用户可以自定义数据库连接对象Connection和语句对象Statement,针对不同类型的数据库提供不同的连接对象和语句对象,例如提供Oracle或MySQL专用连接类和语句类,而且用户可以通过配置文件等方式根据实际需要动态更换系统数据库。使用抽象工厂模式设计该系统,绘制对应的类图并编程模拟实现

练习4

在某赛车游戏中,赛车包括方程式赛车、场地越野赛车、运动汽车、卡车等类型,不同类型的赛车的车身、发动机、轮胎、变速箱等部件有所区别。玩家可以自行选择赛车类型,系统将根据玩家的选择创建出一辆完整的赛车。现采用建造者模式实现赛车的构建,绘制对应的类图并编程模拟实现

练习5

在某在线招聘网站中,用户可以创建一个简历模板。针对不同的工作岗位,可以复制该简历模板并进行适当修改后,生成份新的简历。在复制简历时,用户可以选择是否复制简历中的照片,如果选择“是”,则照片将一同被复制,用户对新简历中的照片进行修改不会影响到简历模板中的照片,对模板进行修改也不会影响到新简历;如果选择"否”,则直接引用简历模板中的照片,修改简历模板中的照片将导致新简历中的照片一同修改,反之亦然。现采用原型模式设计该简历复制功能并提供浅克隆和深克隆两套实现方案,绘制对应的类图并编程模拟实现

练习6

某 Web 性能测试软件中包含一个虚拟用户生成器( Virtual User Generator )。为了避免生成的虚拟用户数量不一致,该测试软件在工作时只允许启动唯一一个虚拟用户生成器。采用单例模式设计该虚拟用户生成器,绘制类图并分别使用饿汉式单例、双重检测锁和 IoDH 三种方式编程模拟实现

实验要求

  1. 结合实例,绘制常见创建型设计模式的结构图
  2. 使用任意一种面向对象编程语言实现常见创建型设计模式实例,代码运行正确

实验步骤

  • 练习1:结合实例,绘制简单工厂模式实例结构图并用面向对象编程语言实现该模式实例
  • 练习2:结合实例,绘制工厂方法模式实例结构图并用面向对象编程语言实现该模式实例
  • 练习3:结合实例,绘制抽象工厂模式实例结构图并用面向对象编程语言实现该模式实例
  • 练习4:结合实例,绘制建造者模式实例结构图并用面向对象编程语言实现该模式实例
  • 练习5:结合实例,绘制原型模式实例结构图并用面向对象编程语言实现该模式实例。练习6:结合实例,绘制单例模式实例结构图并用面向对象编程语言实现该模式实例。
  • 练习6:结合实例,绘制单例模式实力结构图并用面向对象编程语言实现该模式实例

实验结果

练习1:需要提供简单工厂模式的实例的结构图(类图)和实现代码

使用简单工厂模式,首先要创建Shape以及ShapeFactory类、实例中需要实现圆形、矩形和三角形等图形类,即创建CircleRectangle以及Triangle三个实现类来分别实现Shape接口

类图和部分实现代码如下所示

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package dp_lab2_q1;
// Shape
public interface Shape {
void draw( /* 绘制图形 */ );
void erase( /* 擦除图形 */ );
}
// ShapeFactory
public class ShapeFactory throws Exception{
Shape createShape(String shape) {
// 根据所需类型返回图形类的实例对象
if(shape.equals("圆形")) return new Circle();
else if(shape.equals("矩形")) return new Rectangle();
else if(shape.equals("三角形")) return new Triangle();
else throw new Exception("UnsupportedShapeException");
}
void init() {}
}
// shapes
public class Circle implements Shape {
public void draw() { /* 绘制圆形 */ }
public void erase() { /* 擦除圆形 */ }
}
public class Rectangle implements Shape {
public void draw() { /* 绘制矩形 */ }
public void erase() { /* 擦除矩形 */ }
}
public class Triangle implements Shape {
public void draw() { /* 绘制三角形 */ }
public void erase() { /* 擦除三角形 */ }
}

练习2:需要提供工厂方法模式实例的结构图(类图)和实现代码

使用工厂方法模式,首先要创建Connection以及ConnectionFactory接口。实例中需要实现POP3、IMAP以及HTTP等连接类,即创建POP3ConnectionIMAPConnection以及HTTPConnection实现类来分别实现Connection接口。同时创建POP3ConnectionFactoryIMAPConnectionFactory以及HTTPConnectionFactory实现类来分别实现ConnectionFactory接口

类图和部分实现代码如下所示

2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package dp_lab2_q2;
// Connection
public interface Connection {
void connect();
}
// ConnectionFactory
public interface ConnectionFactory {
Connection createConnection();
}
// connections
public class POP3Connection implements Connection {
public void connect() {
// POP3
}
}
public class IMAPConnection implements Connection {
public void connect() {
// IMAP
}
}
public class HTTPConnection implements Connection {
public void connect() {
// HTTP
}
}
// connectionFactories
public class POP3ConnectionFactory implements ConnectionFactory {
public Connection createConnection() {
return new POP3Connection();
}
}
public class IMAPConnectionFactory implements ConnectionFactory {
public Connection createConnection() {
return new IMAPConnection();
}
}
public class HTTPConnectionFactory implements ConnectionFactory {
public Connection createConnection() {
return new HTTPConnection();
}
}

练习3:需要提供抽象工厂模式实例的结构图(类图)和实现代码

使用抽象工厂方法模式,首先要创建ConnectionStatement以及DatabaseFactory接口。实例中需要实现OracleConnection、OracleStatement、MySQLConnection以及MySQLStatement四种数据库连接类,即创建OracleConnectionOracleStatementMySQLConnection以及MySQLStatement实现类来分别实现ConnectionStatement接口。同时创建OracleFactory以及MySQLFactory实现类来分别实现DatabaseFactory接口

类图和部分实现代码如下所示

3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package dp_lab2_q3;
// DatabaseFactory
public interface DatabaseFactory {
Connection createConnection();
Statement createStatement();
}
// Connection
public interface Connection {
void connect();
}
// Statement
public interface Statement {
void statement();
}
// databaseFactories
public class OracleFactory implements DatabaseFactory {
public Connection createConnection() {
return new OracleConnection();
}
public Statement createStatement() {
return new OracleStatement();
}
}
public class MySQLFactory implements DatabaseFactory {
public Connection createConnection() {
return new MySQLConnection();
}
public Statement createStatement() {
return new MySQLStatement();
}
}
// connections
public class OracleConnection implements Connection {
public void connect() {
// 连接到Oracle
}
}
public class MySQLConnection implements Connection {
public void connect() {
// 连接到MySQL
}
}
// statements
public class OracleStatement implements Statement{
public void connect() {
// 执行Oracle数据库操作语句
}
}
public class MySQLStatement implements Statement {
public void connect() {
// 执行MySQL数据库操作语句
}
}

练习4:需要提供建造者模式实例的结构图(类图)和实现代码

使用建造者模式,将复杂对象的构建与表示分离。要创建赛车,所以首先创建Car类用来表示用户所创建的赛车。再创建Director以及Builder类,前者控制后者来创建实体类。在实例中Builder类又被四种赛车子类所继承,分别为FormulaRacingBuilderOffRoadRacingBuilderSportCarBuilder以及TruckBuilder

类图和部分实现代码如下所示

4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package dp_lab2_q4;
// Director
public class Director {
private Builder builder;
public Car buildCar() {
builder.buildBody();
builder.buildEngine();
builder.buildTyre();
builder.buildGearBox();
return builder.assemble();
}
}
// Builer
public abstract class Builder {
private Car car;
public abstract void buildBody();
public abstract void buildEngine();
public abstract void buildTyre();
public abstract void buildGearBox();
public Car assemble() { return this.car; }
}
// Car
public class Car {
// 赛车的基本属性
private String body;
private String engine;
private String tyre;
private String gearBox;
// 赛车基本属性的setter
public void setBody() {}
public void setEngine() {}
public void setTyre() {}
public void setGearBox() {}
}
// builders
public class FormulaRacingBuilder extends Builder {
public void buildBody() { car.setBody("FormulaRacingBody") }
public void buildEngine() { car.setEngine("FormulaRacingEngine") }
public void buildTyre() { car.setTyre("FormulaRacingTyre") }
public void buildGearBox() { car.setGearBox("FormulaRacingGearBox") }
}
public class OffRoadRacingBuilder extends Builder {
public void buildBody() { car.setBody("OffRoadRacingBody") }
public void buildEngine() { car.setEngine("OffRoadRacingEngine") }
public void buildTyre() { car.setTyre("OffRoadRacingTyre") }
public void buildGearBox() { car.setGearBox("OffRoadRacingGearBox") }
}
public class SportCarBuilder extends Builder {
public void buildBody() { car.setBody("SportCarBody") }
public void buildEngine() { car.setEngine("SportCarEngine") }
public void buildTyre() { car.setTyre("SportCarTyre") }
public void buildGearBox() { car.setGearBox("SportCarGearBox") }
}
public class TruckBuilder extends Builder {
public void buildBody() { car.setBody("TruckBody") }
public void buildEngine() { car.setEngine("TruckEngine") }
public void buildTyre() { car.setTyre("TruckTyre") }
public void buildGearBox() { car.setGearBox("TruckGearBox") }
}

练习5:需要提供原型模式实例的结构图(类图)和实现代码

使用原型模式,需要实现原型接口,用来创建当前对象的克隆,实现类的浅克隆与深克隆

类图和部分实现代码如下所示

5

1
2
3
4
5
6
7
8
9
10
11
12
13
package dp_lab2_q5;
// Resume
public class Resume implements Clonerable,Serializable {
// ...
private ContentExceptPicture;
private Picture picture;
public Resume clone() {
return super.clone();
}
public Resume deepClone() {}
}
// Picture
public class Picture implements Serializable {}

练习6:需要提供单例模式实例的结构图(类图)和实现代码

使用单例模式,要确保一个类仅有一个实例对象,并且可以提供一个访问它的方法

分别使用饿汉式、双重校验所以及IoDH来实现单例模式

类图和部分实现代码如下所示

6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package dp_lab2_q6;
// VirtualUserGenerator
public class VirtualUserGenerator {
public VirtualUser generateUser() {
return VirtualUser.userInstance;
}
}
public class VirtualUser {
private static VirtualUser userInstance;
public VirtualUser() {}
public static getInstance() { return userInstance; }
}
// Eager
public class EagerVirtualUser {
private static VirtualUser userInstance = new VirtualUser();
public VirtualUser() {}
public static getInstance() { return userInstance; }
}
// DCL
public class DCLVirtualUser {
private volatile static VirtualUser userInstance;
public VirtualUser() {}
public static getInstance() {
if(userInstance == null) {
synchronized (VirtualUser.class) {
if(userInstance == null) {
userInstance = new VirtualUser();
}
}
}
return userInstance;
}
}
// IoDH
public class IoDHVirtualUser {
private static class VirtualUserHolder {
private static final VirtualUser userInstance = new VirtualUser();
}
public VirtualUser() {}
public static getInstance() { VirtualUserHolder.userInstance; }
}

实验小结

请总结本次实验的体会,包括学会了什么、遇到哪些问题。如何解决这些问题以及存在哪些有待改进的地方

在做一个项目之前的需求分析过程中就应该构思好整个系统的完整架构,运用的模式,以及类与类,类与接口之间的关系,以免造成不必要的麻烦。设计时要尽可能符合七大开发原则。对扩展开发而对修改关闭;面向接口编程,养成封装类以及使用抽象方法的习惯;控制每个类的粒度大小,尽量符合高内聚,低耦合的特性;确保父类所拥有的性质在子类中仍然成立;给每个类都建立他们需要的专用接口;每个类尽量只与具有直接关系的类进行交互;尽量使用组合或聚合的关联关系来实现系统的具体功能

在练习1中,使用简单工厂模式,用户通过工厂类来创建图形实例,对客户端不会暴露创建逻辑,并且是通过一个共同的接口来创建新的对象。解决了系统中接口选择的问题,可以明确地计划在不同条件下创建不同实例对象。增加了系统的扩展性。但是在新加工厂时会违反开闭原则

在练习2中,使用工厂方法模式,将简单工厂模式中的工厂类分为多个子类,进一步提高了整个系统的可扩展性,更加符合但一职责原则。同时有效的解决了简单工厂模式下违反开闭原则的问题。在工厂方法模式中,增加新功能时,直接增加新的工厂类就可以实现,而不需要修改之前的代码,从而符合开闭原则

在练习3中,使用抽象工厂模式,提供一个创建一系列相关或相关依赖对象的接口,而无需指定它们具体的类。在一个工厂类中可聚合多种产品。抽象工厂模式是一种创建对象的最佳方式,由接口生成的每个工厂类都能按照工厂模式提供对象,是一种最具有一般性的工厂模式

在练习4中,使用建造者模式,可以将复杂对象的构建与表示分离。有时一个对象需要使用多个简单的对象一步一步构建而成,建造者模式通过Builder类来一步一步构造最终的对象。提高了系统的独立性以及扩展性,便于控制细节风险。但建造者模式的使用范围是有一定局限性的,对象必须有共同点。当系统内部极为复杂,频繁相互调用可能会导致创建很多建造类

在练习5中,使用原型模式,在保证性能的条件下创建重复的对象。首先实现一个原型接口,用于创建当前对象的克隆。在创建对象代价较大时,可有效提升性能,节省空间,提高便捷性。实例中用户可选择是否克隆图片,为用户提供便捷方法的同时可减少大量存储空间的消耗,减少数据库的调用。此外,在原型模式下,类的创建可以挣脱构造函数的束缚,通过克隆获取实例对象

在练习6中,使用单例模式,创建一个类与实例对象一比一的系统结构。每个类都会提供一种访问其唯一实例对象的方法。单例模式有懒汉式、饿汉式、双重校验所、IoDH以及枚举的方式来实现,每种方式分别具有各自的优缺点。在单例模式下,由于每个类只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例时所消耗的内存。同时避免了对资源的多重占用等情况。但是缺点也很明显,单例模式使得系统中基本不存在子类继承父类或是实现类实现接口,与单一职责原则冲突