java 设计模式之 修饰/装饰 者模式⑧
学会转移心情,因为只有这样才会从悲伤挣扎出来,才会让自己快乐起来。
设计模式学习,近期我会把23中设计模式都写成博客,敬请期待~
—2021/1/11
定义
修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。
应用场景
在一个类需要添加新功能的情况下,而不改变当前类,
比如说一个类的功能是’炒饭’,我想在这个类里面加一个’鸡蛋’的功能,或者加一个’培根’的功能,把原本的’炒饭’,变成’鸡蛋炒饭’;
角色分析
- 抽象构造(Component)角色:定义一个抽象接口以规范准备接受附加责任的对象
- 具体构造(Concrete Component)角色:实现抽象构造,通过装饰角色为其添加一些职责.
- 抽象装饰(Decorator)角色:继承或抽象构建,并包含具体构建实例,可以通过其子类扩展具体构建功能.
- 具体装饰(Concrete Decorator)角色:实现抽象装饰的相关方法,并给具体构建对象添加附加的责任
简单分析:
- 抽象构造角色:被装饰器抽象类
- 具体构造角色:实现被装饰器抽象类,被装饰器
- 抽象装饰角色:装饰器抽象类
- 具体装饰角色:装饰器,实现装饰器抽象类
为什么要定义这么多抽象类呢?
遵守依赖倒置原则(面向接口编程)
分析
UML类图(1.1):
- Noodles 面条类
- FriedRice 炒饭类
- Food 食物
面条和炒饭都属于食物,所以都继承自食物类(Food),咋们需要在各个食物上添加一些新的产品,
比如说我要加一个鸡蛋
现在这个食物类就变成了被装饰者,通过鸡蛋来装饰达到’面条鸡蛋’或者’炒饭鸡蛋’的效果,鸡蛋就变成了装饰者
假如原本一份炒饭10元,一个鸡蛋1元,我点一份鸡蛋炒饭就应该是11元
UML类图(1.2):
\
这是咋们要完成的最终效果图,这个图看不懂没关系,先来看看代码,等最后在返回来看这张图就一目了然了
代码实现
Food食物类:
public abstract class Food {
private String name;//名字
private float price;//价格
//总价
public abstract float totalPrice();
set...
get...
}
FriedRice炒饭类:
public class FriedRice extends Food {
public FriedRice() {
setName("炒饭");
setPrice(12);
}
@Override
public float totalPrice() {
//调用父类的价格方法 将set的值获取出来
return getPrice();
}
}
分析:
代码流程图(2.1):
\
这里如果调用FriedRice(炒饭)构造器的话先把炒饭的价格赋值上去
Noodles 面条类:
public class Noodles extends Food {
public Noodles() {
setName("面食");
setPrice(9);
}
@Override
public float totalPrice() {
return getPrice();
}
}
和FriedRice(炒饭类)一样
实现代码:
//面条
Noodles noodles = new Noodles();
Log.i("装饰器模式", noodles.getName() + "\t" +
noodles.getPrice() + "\t总价:" +
noodles.totalPrice()
);
// 炒饭
FriedRice friedRice = new FriedRice();
Log.i("装饰器模式", friedRice.getName() + "\t" +
friedRice.getPrice() + "\t总价:" +
friedRice.totalPrice()
);
Log图(3.1):
\
这里非常简单,我就不叙述了,这里就称之为被装饰者,拿一个鸡蛋类,吧他变成’鸡蛋面条’或者’鸡蛋炒饭’,这里的鸡蛋就是装饰者.
GarnishFood装饰类:
public abstract class GarnishFood extends Food {
Food food;
public GarnishFood(Food food) {
this.food = food;
}
set..
get..
}
Egg鸡蛋类:
public class Egg extends GarnishFood{
public Egg(Food food) {
super(food);
setName("鸡蛋");
setPrice(1);
}
@Override
public float totalPrice() {
return getPrice()+ getFood().totalPrice();
}
}
Egg鸡蛋类totalPrice()参数分析:
- getPrice() 参数:
红框:这里的getPrice()通过构造器设置为1
黄框:因为鸡蛋类(Egg)继承自装饰类(GarnishFood),装饰类继承自Food,所以这里的调用的都是顶级Food里面的方法 - getFood().totalPrice()参数:
这里调用的也是顶级父类中的Food方法
所以 getFood().totalPrice() = 总价
使用代码:
Noodles nd = new Noodles();
Log.i("装饰器模式", nd.getName() + "\t" +
nd.getPrice() + "\t总价:" +
nd.totalPrice());
Egg egg = new Egg(nd);
Log.i("装饰器模式", egg.getName() + "\t" +
egg.getPrice() + "\t总价:" +
egg.totalPrice());
Egg egg1 = new Egg(egg);
Log.i("装饰器模式", egg1.getName() + "\t" +
egg1.getPrice() + "\t总价:" +
egg1.totalPrice());
Log图(3.2):
可以看出,没加一个鸡蛋,总价就+1元
如果说现在还可以添加新的产品,比如说可以添加培根,来看看代码怎么写吧~
Bacon培根类:
public class Bacon extends GarnishFood{
public Bacon(Food food) {
super(food);
setName("培根");
setPrice(3.5f);
}
@Override
public float totalPrice() {
return super.getPrice() + super.getFood().totalPrice();
}
}
使用代码:
Bacon bacon = new Bacon(egg1);
Log.i("装饰器模式", bacon.getName() + "\t" +
bacon.getPrice() + "\t总价:" +
bacon.totalPrice());
Log图(3.3):
总结
扮演角色:
- 抽象构造角色:Food()
- 具体构造角色:FriedRice().Noodles()
- 抽象装饰角色:GarnishFood()
- 具体装饰角色:Egg().Bacon()
装饰者模式就是把原本一个类的功能在不改变的情况下装饰成一个新的东西,还有原本类的功能,遵守开闭原则(对扩展开放,对修改关闭)
好处:
- 装饰者模式可以带来比继承更加灵活的扩展功能
- 装饰则模式和被装饰者可以分别独立发展,不会相互耦合,
- 装饰者模式遵守了开闭原则(对扩展开放,对修改关闭)
缺点:
- 因为需要用到继承,会把各个类的耦合性弄的非常高
原创不易,您的点赞就是对我最大的支持,留下您的点赞吧~