装饰器模式
装饰模式就是将其实现的功能拆分成一个个装饰器类,然后这些装饰器类实现功能并组装起来达成预期功能,还可以在不知不觉中增加其他装饰器增加新功能,而被装饰类是无感知的
1.装饰器模式的本质
装饰模式的本质:动态组合。
装饰器模式是一种结构型设计模式,它允许在不改变现有对象结构的情况下,动态地将新行为添加到对象中。它通过将对象包装在具有相同接口的装饰器类中,然后通过组合来添加新的功能或修改原始对象的行为。
装饰器模式的本质在于通过组合而非继承的方式来扩展对象的功能。它提供了一种灵活的方式来添加或修改对象的行为,同时遵循开放封闭原则(Open-Closed Principle)。通过使用装饰器模式,可以在运行时动态地添加、移除或修改对象的功能,而无需修改现有的代码。
2.何时选用装饰器模式
建议在以下情况中选用装饰模式。
-
在不改变现有对象接口的情况下,需要动态地添加额外功能或修改对象的行为。
-
需要通过组合而非继承来扩展对象的功能,以避免类爆炸或静态绑定的限制。
-
需要在运行时动态地添加、移除或修改对象的功能,而不影响其他对象。
-
需要对现有的对象进行功能扩展,但是不希望对现有代码进行大规模修改。
3.优缺点
装饰模式有以下优点。
灵活性
- 装饰器模式允许在运行时动态地添加、移除或修改对象的功能,而无需修改现有的代码。它提供了一种灵活的方式来扩展对象的行为。
开放封闭原则
- 装饰器模式遵循开放封闭原则,即对扩展是开放的,对修改是封闭的。通过装饰器模式,可以在不修改现有代码的情况下,通过添加装饰器来扩展对象的功能。
可组合性
- 由于装饰器模式通过组合的方式来添加功能,可以使用多个装饰器进行组合,实现不同的行为组合。这使得装饰器具有很高的可组合性,可以按需组合装饰器,以满足不同的需求。
缺点:
复杂性增加
- 使用装饰器模式会引入许多额外的类和对象,这增加了代码的复杂性和理解难度。
装饰器顺序依赖
- 装饰器的顺序是有意义的,不同的装饰器组合可能会产生不同的结果。因此,需要仔细管理和组织装饰器的顺序。
4.装饰器模式的结构
- Component:组件对象的接口,可以给这些对象动态地添加职责。
- ConcreteComponent:具体的组件对象,实现组件对象接口,通常就是被装饰器装饰的原始对象,.也就是可以给这个对象添加职责。
- Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Component对象,其实就是持有一个被装饰的对象。
5.实现
不使用设计模式写法
1.咖啡类
/**
* 咖啡接口
*/
interface Coffee {
/**
* 获取描述信息
* @return
*/
String getDescription();
/**
* 获取花费
* @return
*/
double getCost();
}
/**
* 黑咖啡
*/
class BlackCoffee implements Coffee {
@Override
public String getDescription() {
return "黑咖啡";
}
@Override
public double getCost() {
return 2.0;
}
}
2.测试类
public class Client {
public static void main(String[] args) {
// 订一杯黑咖啡
Coffee coffee = new BlackCoffee();
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
}
}
3.结果
订单:黑咖啡
总价:2.0
功能实现完,又想给咖啡加糖,以为直接在BlackCoffee
类中加个方法就行了,但实际上不能这么做,这么做违反了开闭原则。
那怎么实现新增功能呢,新增一个子类,让他继承BlackCoffee
即可,但如果还要加功能就还要新增子类,子类或越来越庞大。
4.给咖啡加糖
/**
* 加糖黑咖啡
*/
class SugarBlackCoffee extends BlackCoffee {
@Override
public String getDescription() {
return super.getDescription() + ", 加糖";
}
@Override
public double getCost() {
return super.getCost() + 0.3;
}
}
5.修改测试类
public class Client {
public static void main(String[] args) {
// 加糖黑咖啡
Coffee sugarBlackCoffee = new SugarBlackCoffee();
System.out.println("订单:" + sugarBlackCoffee.getDescription());
System.out.println("总价:" + sugarBlackCoffee.getCost());
}
}
6.结果
订单:黑咖啡, 加糖
总价:2.3
如果想要在不继承类的情况下增加功能,一种较好的方式是增加装饰器模式
使用设计模式优化
1.定义组件接口及真实实现对象
/**
* 咖啡接口
*/
interface Coffee {
/**
* 获取描述信息
* @return
*/
String getDescription();
/**
* 获取花费
* @return
*/
double getCost();
}
/**
* 黑咖啡
*/
class BlackCoffee implements Coffee {
@Override
public String getDescription() {
return "黑咖啡";
}
@Override
public double getCost() {
return 2.0;
}
}
2.装饰器抽象类及具体的装饰器对象
/**
* 咖啡装饰器抽象类
*/
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double getCost() {
return coffee.getCost();
}
}
/**
* 真实的糖装饰器
*/
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", 加糖";
}
@Override
public double getCost() {
return super.getCost() + 0.3;
}
}
/**
* 真实的奶装饰器
*/
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return super.getDescription() + ", 加牛奶";
}
@Override
public double getCost() {
return super.getCost() + 0.5;
}
}
3.测试类
public class Main {
public static void main(String[] args) {
// 订一杯黑咖啡
Coffee coffee = new BlackCoffee();
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
// 再加糖
coffee = new SugarDecorator(coffee);
System.out.println("订单:" + coffee.getDescription());
System.out.println("总价:" + coffee.getCost());
// 加两份糖,一份奶的黑咖啡
MilkDecorator coffee2 = new MilkDecorator(new SugarDecorator(new SugarDecorator(new BlackCoffee())));
System.out.println("订单:" + coffee2.getDescription());
System.out.println("总价:" + coffee2.getCost());
}
}
4.结果
订单:黑咖啡
总价:2.0
订单:黑咖啡, 加牛奶
总价:2.5
订单:黑咖啡, 加牛奶, 加糖
总价:2.8
订单:黑咖啡, 加糖, 加糖, 加牛奶
总价:3.0999999999999996
io中装饰模式的实现
1.测试类
public class IoTest {
public static void main(String[] args) throws Exception {
DataInputStream dis = null;
try {
dis = new DataInputStream(new BufferedInputStream(new FileInputStream("C:\\Users\\Lenovo\\Desktop\\test.txt")));
byte[] bs = new byte[dis.available()];
dis.read(bs);
String content = new String(bs);
System.out.println("文件内容为:" + content);
} finally {
dis.close();
}
}
}
2.结果
如图所示,它的结构和装饰模式的结构几乎是一样的。
- InputStream 就相当于装饰模式中的Component。
- 其实 FileInputStream、ObjectInputStream、StringBufferInputStream这几个对象是直接继承了InputSream,还有几个直接继承 - InputStream的对象,比如ByteArrayInputStream、PipedInputStream等。这些对象相当于装饰模式中的ConcreteComponent,是可以被装饰器装饰的对象。
- FilterInputStream就相当于装饰模式中的Decorator ,而它的子类DataInputStream 、 BufferedInputStream , LineNumberInputStream和PushbackInputStream就相当于装饰模式中的 ConcreteDecorator了。另外FilterInputStream和它的子类对象的构造器,都是传入组件InputStream类型,这样就完全符合前面讲述的装饰器的结构了。
3.新增输入流装饰器
/**
* @description:新增输入流装饰器
*/
public class AddInputStream extends FilterInputStream {
public AddInputStream(InputStream in) {
super(in);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
System.out.println("--->AddInputStream中进行自定义操作:"+ Arrays.toString(b));
return super.read(b, off, len);
}
}
4.修改测试类
public class IoTest {
public static void main(String[] args) throws Exception {
DataInputStream dis = null;
try {
dis = new DataInputStream(new AddInputStream(new BufferedInputStream(new FileInputStream("C:\\Users\\Lenovo\\Desktop\\test.txt"))));
byte[] bs = new byte[dis.available()];
dis.read(bs);
String content = new String(bs);
System.out.println("文件内容为:" + content);
} finally {
dis.close();
}
}
}
5.结果
看到新增的装饰器类生效了