在JDK
中,使用到了很多设计模式,例如装饰者模式、观察者模式,它广泛存在于Java I/O
中,说实话,刚学习Java I/O
时有点懵,他的类是在太多了,可供选择的就十多种,还有很多叫不上名字的IO类,当时一度怀疑自己不是学编程的选手。
今天重点所说的就是装饰者模式,也算是一个总结吧
场景
这个场景很简单,为某冷饮店制造一个收费系统。
他的业务是这样的,主要的为饮品收费,其他的收费项目为配料收费,例如在点了饮品的基础上增加了珍珠、椰果等配料,然后计算总的费用。
问题解决
大家的想法可能是这样:当购买哪一种类的饮料时,就创建出来这个对象,如果想另外增加配料,再创建出来配料类的对象,最后计算出价格。但是怎么实现呢,要讲弹性,以方便日后的扩展
最初可以设计出一个抽象类,将饮料抽象出来
public abstract class Beverage {
public String getDescription() {
return "unknown beverage";
}
public abstract float cost();
}
接下来就是围绕这个类展开,从而引出装饰者模式。在往下进行之前,先说一个设计原则:
对修改关闭,对扩展开放(开放封闭原则)
新创建的饮料可以完全继承自Beverage
类,并且实现其中的cost()
和覆盖getDescription()
,我们创建两个饮料类拿铁(Latte)和卡布奇诺(Cappuccino)
public class Latte extends Beverage{
@Override
public float cost() {
return 12.1f;
}
@Override
public String getDescription() {
return "Latte";
}
}
public class Cappuccino extends Beverage{
@Override
public float cost() {
return 7.21f;
}
@Override
public String getDescription() {
return "Cappuccino";
}
}
在装饰者模式中,这两类被称为被装饰者,而这个场景中,真正的装饰者是调料,我们看一下调料的抽象类
public abstract class Flavour extends Beverage {
@Override
public String getDescription() {
return "Flavour";
}
}
另外再去实现两种调料珍珠(Pearl)、椰肉(Coconut)
public class Pearl extends Flavour{
private Beverage beverage;
public Pearl(Beverage beverage) {
this.beverage = beverage;
}
@Override
public float cost() {
return beverage.cost() + 2.1f;
}
@Override
public String getDescription() {
return beverage.getDescription() + " Pearl";
}
}
public class Coconut extends Flavour{
private Beverage beverage;
public Coconut(Beverage beverage) {
this.beverage = beverage;
}
@Override
public float cost() {
return beverage.cost() + 4.2f;
}
@Override
public String getDescription() {
return beverage.getDescription() + " Coconut";
}
}
目前我们就完成了所有所需的类,UML
类图如下
其中Pearl
与Coconut
装饰者类,负责装饰Latte
与Cappuccino
,且装饰者与被装饰者应该有相同的类型,即Beverage
如果我想点一份Latte,并且加上Pearl配料,应该如何计算金钱呢
public class Consumption {
public static void main(String[] args) {
Latte latte = new Latte();
Pearl pearl = new Pearl(latte);
System.out.println("total cost: " + pearl.cost());
System.out.println(pearl.getDescription());
}
}
最后打印出
total cost: 14.200001
Latte Pearl
这样的扩展是否很灵活呢,通过装饰者对被装饰者的装饰,在不修改原有代码的前提下,完成了如此具有弹性的功能。
此时我想点一份Cappuccino加两份Coconut
public class Consumption {
public static void main(String[] args) {
Cappuccino cappuccino = new Cappuccino();
Coconut coconut = new Coconut(cappuccino);
coconut = new Coconut(coconut);
System.out.println("total cost: " + coconut.cost());
System.out.println(coconut.getDescription());
}
}
输出
total cost: 15.61
Cappuccino Coconut Coconut
I/O类中的装饰者模式
以输入流举例(输出流也是同理),InputStream
可以看成是抽象的组件,FilterInputStream
可以看成是抽象装饰者,而她下面的类就可以看成是具体的装饰者,他们都是对以及InputStream
的装饰
既然说的这么细了,那就进到FilterInputStream
的源码中看一看
public class FilterInputStream extends InputStream {
// 被装饰的对象
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
// 调用被装饰的对象方法
return in.read();
}
// 部分省略
}
他和咱们上面饮品的例子结构差不多吧
Java I/O
也引出来了装饰者模式的一个缺点,利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会给使用此API
的程序员造成困扰。
我们在知道了Java I/O
类的原理后,可以自己动手写一个Java I/O
类的API
,功能是将文件中的全部大写转换为小写
public class LowerFileInputStream extends FileInputStream {
private FileInputStream fileInputStream;
@Override
public int read() throws IOException {
int read = super.read();
return read == -1 ? read : Character.toLowerCase((char)read);
}
@Override
public int read(byte[] b) throws IOException {
int read = super.read(b);
for (int i = 0; i < read; i++) {
b[i] = (byte) Character.toLowerCase((char) b[i]);
}
return read;
}
public LowerFileInputStream(String name) throws FileNotFoundException {
super(name);
fileInputStream = new FileInputStream(name);
}
}
测试
public static void main(String[] args) throws Exception {
LowerFileInputStream lowerFileInputStream = new LowerFileInputStream("lower.txt");
int read = 0;
StringBuilder stringBuilder = new StringBuilder();
while((read = lowerFileInputStream.read()) != -1) {
stringBuilder.append((char)read);
}
lowerFileInputStream.close();
System.out.println(stringBuilder.toString());
}
输出
# 原始文件内容
I am Programmer
# 输出内容
i am programmer
总结
本篇文章以某饮料公司的消费系统为场景,引出了装饰者模式,并且得到了一个重要的设计原则:
类只对扩展开放,对修改关闭(开放封闭原则)
还总结出Java IO中存在着大部分的装饰者模式,同时也指出了装饰者模式的不足之处。
最后自己动手实现了一个依靠装饰者模式自定义的IO类。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!