自大学课程初识设计模式以来,就越发觉得有必要系统学习一下设计模式。
刚好在实习前准备期间课比较少,抽出一点时间整理一下记一些笔记,复制粘贴比较多。
笔记比较适合学习过设计模式的同学。
Decorator Pattern(装饰模式)
学习链接:极客学院Wiki_Java设计模式之结构型模式
另外感谢刘伟博士,学习设计模式可以看刘伟博士的博客,很详尽。
刘伟技术博客
装饰模式的适用范围
(1) 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
(2) 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。
不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如 Java 语言中的 final 类)。
应用实例
在软件开发中,装饰模式应用较为广泛,例如在 JavaIO 中的输入流和输出流的设计、javax.swing 包中一些图形界面构件功能的增强等地方都运用了装饰模式。
装饰模式如何实现
角色
Component(抽象构件)
它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(具体构件)
它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类)
它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类)
它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
类图
装饰模式的要点就是装饰必须和被装饰的构件实现一个接口,并且在调用装饰方法的时候参数是这个接口,不然装饰就只能装饰装饰了。另外,透明和半透明就是具体装饰类中是否有自己独特的方法,如果有需要调用这个方法则对于客户端来说这就是半透明。不是面向抽象了。
装饰模式的优缺点
主要优点
(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
(2) 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
(3) 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
主要缺点
(1) 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
练习
题目
Sunny 软件公司欲开发了一个数据加密模块,可以对字符串进行加密。最简单的加密算法通过对字母进行移位来实现,同时还提供了稍复杂的逆向输出加密,还提供了更为高级的求模加密。用户先使用最简单的加密算法对字符串进行加密,如果觉得还不够可以对加密之后的结果使用其他加密算法进行二次加密,当然也可以进行第三次加密。试使用装饰模式设计该多重加密系统。
题目分析
题目还是比较明确的
实现代码
抽象构件\具体构件:StringData.java
package com.joy;
public class StringData {
String data ;
public void display() {
System.out.println(data);
}
public String toString() {
return data;
}
public void setString(String ss) {
data = ss;
}
}
抽象装饰:DataDecorator.java
package com.joy;
public class DataDecorator extends StringData {
StringData sdata;
public DataDecorator(StringData sdata) {
this.sdata = sdata;
}
@Override
public void display() {
sdata.display();
}
@Override
public String toString() {
return sdata.toString();
}
@Override
public void setString(String ss) {
sdata.setString(ss);
}
}
具体装饰:Transportor.java
package com.joy;
public class Transportor extends DataDecorator {
public Transportor(StringData sdata) {
super(sdata);
}
//移位
private void transport(){
System.out.println("移位:");
String ss = toString();
StringBuilder sb = new StringBuilder();
char ch = ss.charAt(0);
for(int i = 1;i<ss.length();i++){
sb.append(ss.charAt(i));
}
sb.append(ch);
setString(sb.toString());
}
@Override
public void display() {
this.transport();
super.display();
}
}
具体装饰:Backing.java
package com.joy;
public class Backing extends DataDecorator {
public Backing(StringData sdata) {
super(sdata);
}
//逆向输出
private void back(){
System.out.println("逆向输出:");
String ss = toString();
StringBuilder sb = new StringBuilder();
for(int i = ss.length()-1;i>=0;i--){
sb.append(ss.charAt(i));
}
setString(sb.toString());
}
@Override
public void display() {
this.back();
super.display();
}
}
具体装饰:Mod.java
package com.joy;
public class Mod extends DataDecorator {
public Mod(StringData sdata) {
super(sdata);
}
//取模加密
private void mod(){
System.out.println("Mod:");
String ss =toString();
StringBuilder sb = new StringBuilder();
for(int i = 0;i<ss.length();i++){
sb.append(ss.charAt(i)%6);
}
setString(sb.toString());
}
@Override
public void display() {
this.mod();
super.display();
}
}
客户端:Clien.java
package com.joy;
public class Client {
public static void main(String[] args) {
StringData demo,DES1,DES2,DES3;
demo = new StringData();
demo.setString("this is the password!");
System.out.println("DEMO:");
demo.display();
DES1 = new Transportor(demo);
DES2 = new Backing(DES1);
DES3 = new Mod(DES2);
System.out.println("三次加密后:");
DES3.display();
// System.out.println("进行二次加密后:");
// DES2.display();
// System.out.println("简单加密后:");
// DES1.display();
}
}
运行结果
这里由于在装饰过程中把原有数据改动了,所以只能分几次测试了。
第一次:简单加密
第二次:二次加密
第三次:三次加密
总结
装饰模式比较好理解,但是在代码实现上需要特别注意,多层次之后会变得复杂,我在写练习的时候就吃了很大的亏,充分体会到了装饰模式的缺点,另外值得主要的是装饰的先后顺序也是要点。