设计模式第一次作业
1.题目1
要求:某系统日志记录器要求支持多种日志记录方式,如文件记录、数据库记录等;用户可以根据要求动态选择日志记录方式。使用Factory模式来设计;如果系统中增加一个日志记录方式—控制台日志记录(ConsoleLog),类图将如何改变?
解答
根据工厂模式设计以下类图:
实现代码如下:
// 抽象日志工厂类
public abstract class LogFactory {
abstract Log createLog();
}
// 文本日志工厂类
public class TextLogFactory extends LogFactory {
@Override
Log createLog() {
return new TextLog();
}
}
// 数据库日志工厂类
public class DBLogFactory extends LogFactory {
@Override
Log createLog() {
return new DBLog();
}
}
// 抽象日志产品类
public abstract class Log {
abstract void add();
abstract void delete();
abstract void query();
}
// 文本日志产品类
public class TextLog extends Log {
@Override
void add() {
System.out.println("add a text log");
}
@Override
void delete() {
System.out.println("delete a text log");
}
@Override
void query() {
System.out.println("query a text log");
}
}
用户调用代码如下:
LogFactory factory = new TextLogFactory(); // 根据用户要求动态选择Factory具体类
Log log = factory.createLog();
log.add();
如果系统中增加一个日志记录方式—控制台日志记录,类图改变如下:
2.题目2
要求:某系统为了改进数据库操作的性能,自定义数据连接对象Connection和语句对象Statement,可针对不同类型的数据库提供不同的连接对象和语句对象;用户可以通过配置文件等方式根据实际需要动态更换系统数据库;使用Abstract Factory模式来设计。
解答
类图如下:
代码实现如下:
// 数据库抽象工厂类
public abstract class DBFactory {
public abstract Connection makeConnection();
public abstract Statement makeStatement();
}
// 数据库连接抽象产品类
public abstract class Connection {
public abstract void connect();
}
// 数据库语句抽象产品类
public abstract class Statement {
public abstract void query();
}
// MySql数据库连接
public class MySqlConnection extends Connection {
@Override
public void connect() {
System.out.println("connect to mysql");
}
}
// MySql数据库语句
public class MySqlStatement extends Statement {
@Override
public void query() {
System.out.println("make a mysql query");
}
}
// MySql数据库工厂类
public class MySqlDBFactory extends DBFactory {
@Override
public Connection makeConnection() {
return new MySqlConnection();
}
@Override
public Statement makeStatement() {
return new MySqlStatement();
}
}
用户调用如下:
DBFactory factory = new MySqlDBFactory(); // 可以根据配置文件更换为具体的数据库工厂类
Connection connection = factory.makeConnection();
Statement statement = factory.makeStatement();
connection.connect();
statement.query();
3.题目3
要求:KFC套餐是一个复杂对象,一般包括主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)组成部分,不同套餐有不同组成部分;KFC服务员要根据顾客要求,装配这些组成部分,构造一个完整套餐,返回给顾客;使用Builder模式来设计。
解答
类图如下:
代码如下:
// 套餐创建者抽象类
public abstract class ComboBuilder {
protected Combo combo;
public ComboBuilder() {
this.combo = new Combo();
}
public Combo getCombo() {
return combo;
}
public abstract void buildStaple();
public abstract void buildDrink();
}
// 套餐导演抽象类
public abstract class ComboDirector {
protected ComboBuilder builder;
public ComboDirector(ComboBuilder builder) {
this.builder = builder;
}
public abstract Combo construct();
}
// 套餐导演具体类,同时创建主食和饮料
public class ComboDoubleDirector extends ComboDirector {
public ComboDoubleDirector(ComboBuilder builder) {
super(builder);
}
@Override
public Combo construct() {
builder.buildStaple();
builder.buildDrink();
return builder.getCombo();
}
}
// 套餐创建者具体类,创建各个part(即主食和饮料)
public class FamilyComboBuilder extends ComboBuilder {
@Override
public void buildStaple() {
combo.add("Hamburger");
}
@Override
public void buildDrink() {
combo.add("Coke");
}
}
// 套餐产品类
public class Combo {
private ArrayList<String> foods = new ArrayList<>();
public void add(String food) {
foods.add(food);
}
public void show(){
for (String food : foods) {
System.out.println(food);
}
}
}
用户调用如下:
ComboBuilder builder = new FamilyComboBuilder(); // 指定具体的创建者类
ComboDirector director = new ComboDoubleDirector(builder); // 指定具体的组合规则类
Combo combo = director.construct();; // 导演类按照类定义的规则组合各个part
combo.show();
4.题目4
要求:游戏中的地图:包括天空、地面、背景;人物包括人体、服装、装备等组成部分,如采用Builder模式如何设计?
解答
类图如下:
代码如下:
// 地图创建者抽象类
public abstract class MapBuilder {
protected Map map;
public MapBuilder() {
this.map = new Map();
}
public Map getMap() {
return map;
}
public abstract void buildSky();
public abstract void buildGround();
public abstract void buildBackground();
}
// 沙漠地图创建者具体类
public class DesertMapBuilder extends MapBuilder {
@Override
public void buildSky() {
map.setSky("gray");
}
@Override
public void buildGround() {
map.setGround("sand");
}
@Override
public void buildBackground() {
map.setBackground("yellow");
}
}
// 地图导演类
public class MapDirector {
private MapBuilder builder;
public MapDirector(MapBuilder builder) {
this.builder = builder;
}
public Map construct() {
builder.buildSky();
builder.buildGround();
builder.buildBackground();
return builder.getMap();
}
}
用户调用如下:
MapBuilder builder = new DesertMapBuilder(); // 指定具体的创建者类
MapDirector director = new MapDirector(builder); // 指定具体的组合规则类
Map map = director.construct();
map.show();
Factory模式和Builder模式的对比
从创建出来的产品进行分析,Factory最终创建单一产品,而观察Builder可以发现,其最终也只产生一个产品,但是该产品比较复杂,是一个复合产品,由多个零件构成。如第4题,如果使用Factory模式,单个Factory只能单一产生天空、地面或背景,而一个Builder能分别创建三个零件,创建后零件的组合规则是在Director类中定义,Builder最终产生一个完整的Map。所以,当一个产品构成比较复杂,可能复合了多个零件,但用户不想了解每个零件的生成过程,他最终只想要一个完整的产品,那么就应该采用Builder模式。而当用户最终需要的产品比较简单,可以将该产品看成是Builder模式下的一个part,这时就应该使用Factory模式了,因为用户就很有可能会更换这个产品的实现。
5.题目5
要求:某系统需要提供一个加密模块,将用户信息(如密码等)加密之后再存储在数据库中,系统已经定义好数据库操作类。为了提高开发效率,现要重用已有的加密算法,这些算法由第三方提供,没有源码。如采用Adapter模式如何设计?
解答
类图如下:
代码如下:
// Target
public abstract class Encryption {
public abstract String encrypt(String str);
}
// Adapter
public class EncryptionAdapter extends Encryption {
private ThirdPartyEncryption adaptee;
public EncryptionAdapter(ThirdPartyEncryption adaptee) {
this.adaptee = adaptee;
}
@Override
public String encrypt(String str) {
return adaptee.encrypt(str);
}
}
用户调用如下:
Encryption encryption = new EncryptionAdapter(adaptee);
encryption.encrypt(str);