sjms 装饰器

本文探讨了如何使用装饰器模式来灵活地为消息日志添加转换功能,如大写转换或XML格式化,同时保持代码复用和扩展性。通过抽象基类Decorator和具体装饰类UpLogger、XMLLogger的实例,展示了如何通过装饰来替换继承带来的类爆炸问题。
摘要由CSDN通过智能技术生成

问题的提出

在消息日志功能中,接收到的消息可以直接送往屏幕显示,也可以用文件保存。

在这里插入图片描述
不考虑消息日志的全部实现过程,具体代码如下:

interface ILogger{
 void log(String msg);
}
class ConsoleLogger implements ILogger{
	public void log(String msg) {
		Sout;
	}
}
class FileLogger implements ILogger {
	public void log(String msg) {
		DataOutputStream dos = null;
		try{
		dos = new DataOutputStream(new FielOutputStream("d:log.txt", true));
		dos.writeBytes(msg + "\r\n");
		dos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

加入现在提出了新需求,接收到的信息科转化成大写字母或转化成 XML 文档,然后屏幕显示或日志保存。常规思路是利用派生类实现。
在这里插入图片描述
如果按照继承思路,若需求分析继续变化,则类的数目增加非常快。那么,装饰器模式就是较好的思路之一。

装饰器模式

装饰器模式利用包含代替继承,动态的给一个对象添加一个额外的功能。以消息日志功能为例,装饰器模式类图如图:
在这里插入图片描述
抽象修饰器基类 Decorator

abstract class Decorator implements ILogger {
	protected ILogger logger;
	public Decorator(Logger logger) {
	this.logger = logger;
	}
}

主要体会“implements ILogger” 中的 “ILogger” 与定义的成员变量 “ILogger logger” 中的 “ILogger”语义有什么不同。需求分析变化了,但无论怎么变,它终究还是一个日志类,因此 Decorator 类要从接口 ILogger 派生。而成员变量 logger 标明 Decorator 对象要对已有的 logger 对象进行装饰,也就是说,要对已有的 FileLogger 或 ConsoleLogger 对象进行装饰。但是由于装饰的内容不同,因此该类只能是抽象类。具体的装饰内容由子类完成。

具体实现类

class UpLogger extends Decorator {
	public UpLogger(ILogger logger) {
		super(logger);
	}
	public void log(String msg) {
		// 对字符串进行大写修饰
		msg = msg.toUpperCase();
		// 再执行已有的日志功能
		logger.log(msg);
	}
}

代码解释:
log() 方法先对字符串大写装饰,再执行已有的日志功能。若已有日志能有有 n 个,则装饰后的字符串可能有 n 个去处,也就是说,该类可以表示 n 个动态含义。
若按照 继承模式变成,则需要编制 n 个 具体的类,从中可知 装饰器模式是采用动态编程的,缩小了程序的规模。(这就是上面提到的 两个 ILogger 的第二个含义的体现)

另一个实现类也是同样的形式。

一个简单的测试类

public class Test{
	public static void main(String[] args) throws Exception {
		// 已有的日志功能
		ILogger existobj = new FileLogger();
		// 新的日志装饰类,对 existobj 装饰
		ILogger newobj = new XMLLogger(existObj);
	
		......
	}
}

装饰器模式主要有 4 中角色

  • 抽象构件角色(Component):
    他是一个接口,封装了将要实现的方法,如 ILogger
  • 具体构件角色(ConcreteComponent):
    他是多个类,该类实现了 Component 接口,如 FileLogger、ConsoleLogger
  • 装饰角色(Decorator):
    他是一个抽象类,该类实现了 Component 接口,同时也必须持有 Component 的对象的医用,如 Decorator
  • 具体的装饰角色(Decorator 类的子类):
    这些类继承了 类 Decorator ,实现了 Component 接口,描述了具体的装饰过程。如 UpLogger、XMLLogger

深入理解装饰器模式

一本菜谱已在全国发型,特点是具有通用性,但没有考虑地域差异。加入以做白菜和大头菜为例,时间情况是以菜谱为蓝本,在考虑地域差异,比如A 地先吃辣的,B 地喜欢吃甜的,用计算机如何描述

  1. 定义抽象构件角色 ICook
interface ICook {
	// 做菜
	void cook()
}
  1. 定义做白菜,大头菜具体角色
class Vegetable implements ICook {
	public void cook() {}
}

class Cabbage implements ICook {
	public void cook() {}
}
  1. 定义抽象装饰器 Decorator
abstract class Decorator implements ICook{
	ICook obj;
	public Decorator(ICook obj) {
		this.obj = obj;
	}
}
  1. 定义具体装饰器
class PepperDecorator extends Decorator {
	public PepperDecorator(ICook obj) {
		super(obj);
	}
	private void addPepper() {
	// 添加辣椒
	}
	public void cook() {
		addPepper();
		obj.cook();
	}
}
class SUgarDecorator extends Decorator{
......
}

和上面操作时一样的。

Vegetable 和 Cabbage 是具体角色,也就是说,菜谱中每道菜的做法相当于具体角色,他们都是长期经验的总结。没有这些具体角色,也就他不上与地域相关的特色装饰菜了。好的菜谱有一个重要的特点,一般来说就是只要人们能想到的菜,就能在菜谱中找到。转化为计算机专业术语即是:具体角色相当于底层的具体实现,有哪些实现功能非常重要。如果做得好的话,很大程度上,上层功能相当于对底层功能进行进一步的封装和完善,类似于装饰的功能。

jdk 装饰器模式

在这里插入图片描述
抽象构件相当于 Reader,具体构件相当于 InputStream、CharArrayReder 以及 FileReader。该类图中并没有体现抽象装饰器, BufferedReader、LineNumberReader 都是具体装饰器,因为他们都有Reader 类型的成员变量 in。
也可以查看 菜鸟教程的装饰器模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值