您的位置:首页 > 编程语言 > Java开发

设计模式之装饰模式Java实现

2017-06-21 00:01 435 查看
装饰模式---对象结构型模式

写在前面

简明的说装饰模式

是你还有你,一切拜托你

1.意图

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

2.别名

包装器Wrapper

3.动机

有时我们希望给某个对象而不是整个类添加一些功能。例如,一个图形用户界面工具箱允许你对任意一个用户界面组件添加一些组件,例如边框,或是一些行为,例如窗口滚动等。

举个肉夹馍的栗子



假如我们要加入向肉夹馍中添加卫龙时使用继承的方式类图就变成了这样



这样使得类变得有些冗余和臃肿。

于是我们引入了装饰模式来解决这个问题



示例代码

//肉夹馍
public interface Roujiamo {
public int cost();
}
//脆皮饼
public class Cuipibing implements Roujiamo {
public Cuipibing(){
System.out.println("脆皮饼的价格是2元");
}
@Override
public int cost() {
return 2;
}
}
//馅儿
public class Xianer implements Roujiamo {
private Roujiamo roujiamo;
public Xianer(Roujiamo roujiamo){
this.roujiamo=roujiamo;
}
@Override
public int cost() {
return this.roujiamo.cost();
}
}
//煎蛋
public class Omelette extends Xianer {
public Omelette(Roujiamo roujiamo){
super(roujiamo);
System.out.println("煎蛋的价格是1元");
}
public int cost(){
return super.cost()+1;
}
}
//卫龙
public class WeiLong extends Xianer {
public WeiLong(Xianer xianer){
super(xianer);
System.out.println("卫龙的价格是1元");
}
public int cost(){
return super.cost()+1;
}
}
public class DecoratorPatternDemo {
public static void main(String[] args) {
Roujiamo roujiamo1=new Omelette(new Cuipibing());
System.out.println("煎蛋饼的价格:"+roujiamo1.cost()+"元");
Roujiam
4000
o roujiamo2=new HamSausage(new Cuipibing());
System.out.println("肠饼的价格:"+roujiamo2.cost()+"元");
Roujiamo roujiamo3=new HamSausage(roujiamo2);
System.out.println("煎蛋肠饼的价格:"+roujiamo3.cost()+"元");
Roujiamo roujiamo4=new WeiLong(new Omelette(new Cuipibing()));
System.out.println("煎蛋卫龙饼的价格:"+roujiamo4.cost()+"元");
Roujiamo roujiamo5=new WeiLong(new HamSausage(new Omelette(new Cuipibing())));
System.out.println("煎蛋卫龙肠饼的价格:"+roujiamo5.cost()+"元");
}
}


运行结果

-------------------

脆皮饼的价格是2元

煎蛋的价格是1元

煎蛋饼的价格:3元

脆皮饼的价格是2元

火腿肠的价格是3元

肠饼的价格:5元

火腿肠的价格是3元

煎蛋肠饼的价格:8元

脆皮饼的价格是2元

煎蛋的价格是1元

卫龙的价格是1元

煎蛋卫龙饼的价格:4元

脆皮饼的价格是2元

煎蛋的价格是1元

火腿肠的价格是3元

卫龙的价格是1元

煎蛋卫龙肠饼的价格:7元

-----------------

一般有两种方式可以实现给一个类或对象增加行为:

1.继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。

2.关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)。 

装饰模式的要点

•装饰者与被装饰者具有相同的类型
•可以用多个装饰者装饰一个对象
•由于装饰者与被装饰者具有相同的类型,我们可以用装饰后的对象代替原来的对象。
•装饰者在委派它装饰的对象作某种处理时,可以添加上自己的行为(功能扩展)(在委派之前或/和之后)。
•对象可以在任何时候被装饰,因此我们能在运行时动态的装饰对象。

•在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
•需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
•当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类)。

装饰模式分为半透明装饰模式透明装饰模式
半透明装饰模式
举个栗子
变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。



示例代码:

//抽象类:变形金刚
public interface Transform {
void move();
}

//汽车类
public class Car implements Transform {
public Car() {
System.out.println("变形金刚是一辆车");
}
@Override
public void move() {
System.out.println("在陆地上移动");
}
}


//抽象装饰类
public class Changer implements Transform {
private Transform transform;

public Changer(Transform transform) {
this.transform = transform;
}
@Override
public void move() {
this.transform.move();
}
}


//机器人
public class Robot extends Changer {
public Robot(Transform transform) {
super(transform);
System.out.println("变成机器人");
}
public void say(){
System.out.println("说话");
}
}


//飞机
public class Airplane extends Changer {
public Airplane(Transform transform) {
super(transform);
System.out.println("变成飞机");
}
public void fly(){
System.out.println("在空中飞翔");
}
}

public class DecoratorPatternDemo {
public static void main(String[] args) {
Transform camaro=new Car();
camaro.move();
Robot bumblebee=new Robot(camaro);
bumblebee.move();
bumblebee.say();
}
}


运行结果:

变形金刚是一辆车

在陆地上移动

变成机器人

在陆地上移动

说话

透明装饰模式

多重加密系统

对字符串进行加密,简单的加密的算法通过对字母进行以为来实现,同时提供了稍复杂的逆向输出加密,还提供了更为高级的求模加密。用户可以使用最简单的加密算法对字符串进行加密,如果觉得强度还不够,可以再次对加密后的结果使用其他加密算法进行二次加密,当然也可以进行第三次等多次加密。



示例代码:

public interface Cipher {
String encrypt(String plainText);
}


public class SimpleCipher implements Cipher{
@Override
public String encrypt(String plainText) {
String str="";
for (int i=0;i<plainText.length();i++){
char c=plainText.charAt(i);
if(c>='a'&&c<='z'){
c+=6;
if(c>'z')c-=26;
if(c<'a')c+=26;
}
if(c>='A'&&c<='Z'){
c+=6;
if(c>'z')c-=26;
if(c<'a')c+=26;
}
str+=c;
}
return str;
}
}


public class CipherDecorator implements Cipher {
private Cipher cipher;

public CipherDecorator(Cipher cipher) {
this.cipher = cipher;
}
@Override
public String encrypt(String plainText) {
return this.cipher.encrypt(plainText);
}
}


public class ComplexCipher extends CipherDecorator {
public ComplexCipher(Cipher cipher) {
super(cipher);
}

@Override
public String encrypt(String plainText) {
String result=super.encrypt(plainText);
return reverse(result);
}
public String reverse(String text){
String str="";
for(int i=text.length();i>0;i--){
str+=text.substring(i-1,i);
}
return str;
}
}

public class AdvanceCipher extends CipherDecorator {
public AdvanceCipher(Cipher cipher) {
super(cipher);
}

@Override
public String encrypt(String plainText) {
String result=super.encrypt(plainText);
return mod(result);
}

public String mod(String text){
String str="";
for(int i=0;i<text.length();i++){
String c=String.valueOf(text.charAt(i)%6);
str+=c;
}
return str;
}
}

public class DecoratorPatternDemo {
public static void main(String[] args) {
String password="HelloWorld";
String encryptPassword;
Cipher simpleCipher=new SimpleCipher();
System.out.println(password);
System.out.println("-----------第一次加密-----------");
encryptPassword=simpleCipher.encrypt(password);
System.out.println(encryptPassword);
System.out.println("-----------第二次加密-----------");
Cipher complexCipher=new ComplexCipher(simpleCipher);
encryptPassword=complexCipher.encrypt(encryptPassword);
System.out.println(encryptPassword);
System.out.println("-----------第三次加密-----------");
Cipher advanceCipher=new AdvanceCipher(complexCipher);
encryptPassword=advanceCipher.encrypt(encryptPassword);
System.out.println(encryptPassword);
}
}

运行结果:

HelloWorld

-----------第一次加密-----------

hkrruwuxrj

-----------第二次加密-----------

pxdacaxxqn

-----------第三次加密-----------

2544131444



对比



装饰模式的应用

(1)Java IO中的输入流和输出流的设计

由于Java I/O库需要很多性能的各种组合,如果这些性能都是用继承来实现的,那么每一种组合都需要一个类,这样就会造成大量性能重复的类出现。而如果采用装饰模式,那么类的数量就会大大减少,性能的重复也可以减少至最少。因此装饰模式是Java I/O库的基本模式。



角色分配:

抽象构件类:InputStream

具体构件类:FileInputStream、ByteArrayInputStream等

抽象装饰类:FilterInputStream

具体装饰类:BufferedInputStream、DataInputStream等

(2)javax.swing包中一些图形界面构件功能的增强

在javax.swing包中,可以通过装饰模式动态给一些构件增加新的行为或改善其外观显示。

如JList构件本身并不支持直接滚动,即没有滚动条,要创建可以滚动的列表,可以使用如下代码实现:

JList list=new JList();

JScrollPane scrollPane=new JScrollPane(list); 

装饰模式优点

1.装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

2.通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

3.可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。

4.具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。

装饰模式的缺点

1.使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间
相互连接的方式有所不同,而不是它们的类或者属性值有所不同,
同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,
加大学习与理解的难度。

2.这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,
排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

相关模式
装饰模式和适配器模式
装饰模式不同于适配器模式,因为装饰模式仅改变对象的职责而不改变它的接口;
而是适配器模式将给对象一个全新的接口。



装饰模式和桥接模式
相同点:都是为了解决多子类对象问题
装饰模式是动态地添加一些额外功能的模式,也就是说装饰模式是使用新需求
而添加新功能,并且不影响其他对象的一种模式;
而桥接模式是适应变化维度的一种模式,它在于将对象的各个维度的变化都独立开来,
使一些变化不受其他因素变化的影响。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息