刚开始接触这个设计模式 感觉有点难懂,个人的学习办法是先拿着前辈的代码就行断点调试 然后再回头想想设计的初衷,
这样可能效果要更好 安利给刚学这个的童靴。
1. 装饰者模式
动态的给一个对象(需要装饰的对象)添加一些额外的职责,就增加功能而言,装饰模式比生成子类更为灵活 。、
砸门看看对应的结构图
1.Component是定义对象职责的接口 可以给对象动态的增加职责
2.ConcreteCompoment 是具体的需要装饰对象
3.Decorator 是一个抽象类 它和 ConcreteCompoment都实现了Component接口 只不过Decorator可以更好的扩展对Component的修饰
4.ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰对象 里面的方法主要是对Component 需要装饰的target进行装饰
还是老模式 我们来一个简单的案例
假如小白今天要出去约会(第一次哦)那么怎么搭配自己的穿法
我们拿出简单粗暴的方式
package 装饰模式.Method3;
public class PersonMethod1 {
private String name;
public PersonMethod1(String name) {
this.name = name;
}
public void WearTshirt() {
System.out.println("穿一件黄色的Tshirt");
}
public void WearBigTrouser() {
System.out.println("穿一件垮裤");
}
public void WearSnakers() {
System.out.println("穿一双蛇皮鞋");
}
public void WearSuit() {
System.out.println("穿一件西装");
}
public void WearTie() {
System.out.println("打一下领带");
}
public void Wearshershoes() {
System.out.println("穿一双布鞋");
}
}
测试:
package 装饰模式.Method3;
import org.junit.Test;
public class test1 {
@Test
public void testMethod(){
PersonMethod1 method1=new PersonMethod1("小明");
System.out.println("第一种方式");
method1.WearBigTrouser();
method1.WearTie();
method1.WearTshirt();
System.out.println("第二种方式");
method1.Wearshershoes();
method1.WearSuit();
method1.WearTshirt();
}
}
看到上面这一坨 表面上是可以完成我所想要的需求 但是如果我要增加一个“超人”打扮 ,我们在PersonMethod1方法里面改改相应的方法吗?显然这违背了开放-封闭原则(面对新需求时候通过新加代码进行的 而不是更改现有的代码)
好了 我们看看装饰者的结构图将代码修改如下
要点: 装饰者和被装饰者同时拥有同一个父类
父类:
package 装饰模式.Method3;
public interface Dress {
public void dressWhat();
}
抽象装饰者:
package 装饰模式.Method3;
public abstract class Decorate implements Dress{
private Dress dress;
public Decorate(Dress dress) {
this.dress = dress;
}
@Override
public void dressWhat() {
dress.dressWhat();
}
}
被装饰者:具体的装饰对象(要装饰者)
package 装饰模式.Method3;
public class Person implements Dress {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void dressWhat() {
System.out.println("出去约会该怎么穿呢");
}
}
接下来3个就是我们怎么去装饰的具体实现(也可以是n个)
DressMatching1 具体装饰者1
package 装饰模式.Method3;
public class DressMatching1 extends Decorate {
public DressMatching1(Dress dress) {
super(dress);
}
@Override
public void dressWhat() {
super.dressWhat();
System.out.println("穿一件垮裤");
}
}
DressMatching2 具体装饰者2
package 装饰模式.Method3;
public class DressMatching2 extends Decorate {
public DressMatching2(Dress dress) {
super(dress);
}
@Override
public void dressWhat() {
super.dressWhat();
System.out.println("穿一双蛇皮鞋");
}
}
DressMatching3 具体装饰者3
package 装饰模式.Method3;
public class DressMatching3 extends Decorate {
public DressMatching3(Dress dress) {
super(dress);
}
@Override
public void dressWhat() {
super.dressWhat();
System.out.println("穿一件西装");
}
}
。。。。。我们还可以加很多具体装饰者
接下来我们测试一下
package 装饰模式.Method3;
import org.junit.Test;
public class test {
@Test
public void testDress() {
Person person = new Person("小白");
DressMatching3 dress3 = new DressMatching3(new DressMatching2(new DressMatching1(person)));
dress3.dressWhat();
}
}
通过结果我们可以看到 穿什么这个完全不是由person发起 而是由decorate主动去装饰person
关键点:
1、Decorate抽象类中,持有Dress接口,方法全部委托给该接口调用,目的是交给该接口的实现类即子类进行调用。
2、DressMatching1,DressMatching2,DressMatching3抽象类的子类(具体装饰者),里面都有一个构造方法调用super(dress),这一句就体现了抽象类依赖于子类实现即抽象依赖于实现的原则。因为构造里面参数都是Dress接口,只要是该Dress的实现类都可以传递进去,即表现出 DressMatching3 dress3 = new DressMatching3(new DressMatching2(new DressMatching1(person)));这种结构的样子。所以当调用 dress3.dressWhat();的时候,又因为每个具体装饰者类中,都先调用 super.dressWhat();方法,而该super已经由构造传递并指向了具体的某一个装饰者类(这个可以根据需要调换顺序),那么调用的即为装饰类的方法,然后才调用自身的装饰方法,即表现出一种装饰、链式的类似于过滤的行为。
- 是不是特别不好理解呢?没关系 刚好中午没怎么吃 再来一个日常的例子 咋们来用一个例子写出一个陕北西饼的教程
- 现在需要一份陕北西饼,主体是饼,可以选择添加油条、热狗、地沟油、正宗地沟油、辣皮子等等许多其他的配料,配料不同导致卖的价格也不同 这种情况下就可以使用装饰者模式
首先我们来一个西饼的父类 (带有名字和价格) 每一个具体类型的西饼价格不同
package 装饰模式.Method4;
public abstract class Cake {
protected String name ;
public String getName(){
return name;
}
public abstract double getPrice();
}
然后来一个装饰者 (也就是配菜 是一个抽象类)
package 装饰模式.Method4;
public abstract class CakeMaterials extends Cake {
public abstract String getName();
}
接着就是具体的西饼对象
package 装饰模式.Method4;
public class ShanbeiCake extends Cake {
public ShanbeiCake() {
name="陕北西饼";
}
@Override
public double getPrice() {
return 5;
}
}
接下来的几个类 就是不同的装饰者 也就是不同的配菜材料
HotDogMaterals 热狗 LettuceMaterals 蔬菜 SpicyMaterals 辣条 YoutiaoMaterials 油条 俺就写在一起了
package 装饰模式.Method4;
public class HotDogMaterals extends CakeMaterials {
private Cake cake;
public HotDogMaterals(Cake cake) {
this.cake = cake;
}
@Override
public String getName() {
return cake.getName()+"热狗";
}
@Override
public double getPrice() {
return cake.getPrice()+5;
}
}
package 装饰模式.Method4;
public class LettuceMaterals extends CakeMaterials {
private Cake cake;
public LettuceMaterals(Cake cake) {
this.cake = cake;
}
@Override
public String getName() {
return cake.name+"生菜";
}
@Override
public double getPrice() {
return cake.getPrice()+2;
}
}
package 装饰模式.Method4;
public class SpicyMaterals extends CakeMaterials {
private Cake cake;
public SpicyMaterals(Cake cake) {
this.cake = cake;
}
@Override
public String getName() {
return cake.getName()+"辣条";
}
@Override
public double getPrice() {
return cake.getPrice()+3;
}
}
package 装饰模式.Method4;
public class YoutiaoMaterials extends CakeMaterials {
private Cake cake;
public YoutiaoMaterials(Cake cake) {
this.cake = cake;
}
@Override
public String getName() {
return cake.getName()+"油条";
}
@Override
public double getPrice() {
return cake.getPrice()+3;
}
}
咋现在就去买小小和wy的西饼
package 装饰模式.Method4;
import org.junit.Test;
public class TestCake {
@Test
public void buyCake(){
//构建一个 小小的西饼
Cake cake=new ShanbeiCake();
//接下来开始装饰这个对象 老板加点辣条和热狗 多少钱
SpicyMaterals spicyMaterals=new SpicyMaterals(cake);
HotDogMaterals hotDogMaterals=new HotDogMaterals(spicyMaterals);
//价格为
System.out.println("小小的西饼价格为"+hotDogMaterals.getPrice());
//构建一个wy西饼(油条加青菜+热狗)
Cake cake1=new ShanbeiCake();
//接下来开始装饰这个对象 老板加点油条青菜+热狗 多少钱
HotDogMaterals hotDogMaterals1=new HotDogMaterals(new LettuceMaterals(new YoutiaoMaterials(cake1)));
//价格为
System.out.println("wy西饼价格为"+hotDogMaterals1.getPrice());
}
}
结果:
到此为止 如果还是不是很明白 个人建议下次可以去买个西饼试试 哈哈哈。。。。
总结下:把类中的装饰功能从类中移除出去,这样可以简化原来的类 ,有效的把类的核心功能和原始功能区分开来 而且可以去除重复的装饰逻辑。