装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。
介绍
意图: 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决: 一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用: 在不想增加很多子类的情况下扩展类。
如何解决: 将具体功能职责划分,同时继承装饰者模式。
关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点: 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点: 多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
注意事项: 可代替继承。
应用示例
在论坛中为了防止用户发表不雅言论,常常需要将某些敏感词汇屏蔽。假设某论坛是将发帖内容保存在磁盘文件中,请利用装饰模式的概念编写一个过滤流类ReplaceWriter,使用该类的Write字符串系列方法会将敏感词汇用“*”替换掉
。需要替换的敏感词汇保存在文件badwords.txt中。请按实验的要求提交实验报告和项目实现的源代码。(提示:参考FilterWriter)
类图设计
设计方案
- 首先定义一个接口
Writer
,有一个write()
方法,返回类型为String - 创建实现接口的实体类
ForumWriter
,重写write()方法,用以读取txt文件中的论坛内容并return。 - 创建实体装饰类
ReplaceWriter
,包含一个ForumWriter的对象,重写write()方法,调用ForumWriter的对象获取论坛内容并返回String,ReplaceWriter调用自身的方法对敏感词汇进行处理后输出。
代码实现
Writer.java
package decorator;
/**
* @author 作者 Long:
* @version 创建时间:2022年5月11日 上午10:36:30
*/
public interface Writer {
String write(String filepath);
}
ForumWriter.java
package decorator;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* @author 作者 Long:
* @version 创建时间:2022年5月11日 上午10:36:59
*/
public class ForumWriter implements Writer {
@Override
public String write(String filepath) {
String ans = "";
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(filepath));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
String strLine;
try {
while (br.ready()) {
strLine = br.readLine();
ans += strLine;
}
} catch (IOException e) {
e.printStackTrace();
}
return ans;
}
}
ReplaceWriter.java
package decorator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
/**
* @author 作者 Long:
* @version 创建时间:2022年5月11日 上午10:44:00
*/
public class ReplaceWriter implements Writer {
protected ForumWriter forumWriter;
private ArrayList<String> sensitivewords = new ArrayList<String>();
public ReplaceWriter(ForumWriter forumWriter) {
this.forumWriter = forumWriter;
try {
getSensitivewords();
} catch (IOException e) {
e.printStackTrace();
}
}
public ReplaceWriter() {
try {
getSensitivewords();
} catch (IOException e) {
e.printStackTrace();
}
}
//读取敏感词汇的方法
private void getSensitivewords() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("./files/badwords.txt"));
String strLine;
while (br.ready()) {
strLine = br.readLine();
sensitivewords.add(strLine);
}
}
// 将敏感词汇替代为同等长度的*的方法
private String getAsteriskString(int num) {
if (num <= 0) {
return "";
}
String asterisk = "";
for (int i = 0; i < num; i++) {
asterisk += "*";
}
return asterisk;
}
public String write(String filepath) {
String content = forumWriter.write(filepath);
for (int i = 0; i < sensitivewords.size(); i++) {
if (content.contains(sensitivewords.get(i))) {
content = content.replace(sensitivewords.get(i), getAsteriskString(sensitivewords.get(i).length()));
}
}
return content;
}
}
Main.java
package decorator;
/**
* @author 作者 Long:
* @version 创建时间:2022年5月11日 上午9:41:46
*/
public class Main {
public static void main(String[] args) {
Writer writer = new ForumWriter();
ReplaceWriter writer2 = new ReplaceWriter(new ForumWriter());
String content = writer.write("./files/articlecontent.txt");
System.out.println("原发帖内容:");
System.out.println(content);
content = writer2.write("./files/articlecontent.txt");
System.out.println("替代敏感词汇后的发帖内容:");
System.out.println(content);
}
}
articlecontent.txt
软件设计模式真是一个小笨蛋
(txt文件的放置位置需要根据自己的项目结构进行更改,代码中的路径也需要进行对应修改)
badwords.txt
笨蛋
敏感词汇还可以增多,一行代表一个敏感词汇
(txt文件的放置位置需要根据自己的项目结构进行更改,代码中的路径也需要进行对应修改)