设计模式(装饰者模式)- 扩展原有功能

本贴最后更新于 2427 天前,其中的信息可能已经物是人非

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。


怎么做?

  1. 新建一个类,该类耦合了需要装饰的类,将实现传递进来,保证原有功能的完整性。为了表明该类是某接口的包装类,所以一般声明为 abstract 类,即使该类并无抽象方法。
  2. 新建一个需要扩充功能的类,继承上面新建的抽象类,增加新扩展的功能即可。

代码示例:


接口的抽象包装类

/**
 * {@link org.springframework.core.env.Environment} 接口的抽象包装类。
 * @author pleuvoir
 */
public abstract class AbstractEnvironmentWrap implements Environment {

	protected Environment environment;

	public AbstractEnvironmentWrap(Environment environment) {
		this.environment = environment;
	}

	@Override
	public boolean containsProperty(String key) {
		return environment.containsProperty(key);
	}

	@Override
	public String getProperty(String key) {
		return environment.getProperty(key);
	}

	@Override
	public String getProperty(String key, String defaultValue) {
		return environment.getProperty(key, defaultValue);
	}

	@Override
	public <T> T getProperty(String key, Class<T> targetType) {
		return environment.getProperty(key, targetType);
	}

	@Override
	public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
		return environment.getProperty(key, targetType, defaultValue);
	}

	@Override
	public <T> Class<T> getPropertyAsClass(String key, Class<T> targetType) {
		return environment.getPropertyAsClass(key, targetType);
	}

	@Override
	public String getRequiredProperty(String key) throws IllegalStateException {
		return environment.getRequiredProperty(key);
	}

	@Override
	public <T> T getRequiredProperty(String key, Class<T> targetType)
			throws IllegalStateException {
		return environment.getRequiredProperty(key, targetType);
	}

	@Override
	public String resolvePlaceholders(String text) {
		return environment.resolvePlaceholders(text);
	}

	@Override
	public String resolveRequiredPlaceholders(String text)
			throws IllegalArgumentException {
		return environment.resolveRequiredPlaceholders(text);
	}

	@Override
	public String[] getActiveProfiles() {
		return environment.getActiveProfiles();
	}

	@Override
	public String[] getDefaultProfiles() {
		return environment.getDefaultProfiles();
	}

	@Override
	public boolean acceptsProfiles(String... profiles) {
		return environment.acceptsProfiles(profiles);
	}

}

需要扩充功能的实现类

/**
 * {@link org.springframework.core.env.Environment} 包装类,提供了更为简便的数据获取方法
 * @author pleuvoir
 * 
 */
public class EnvironmentWrap extends AbstractEnvironmentWrap {

	public EnvironmentWrap(Environment environment) {
		super(environment);
	}

	/**
	 * 获取Integer类型的数据,若数据格式不可转换为Integer类型,将抛出异常{@link NumberFormatException}
	 * @param key
	 * @return
	 */
	public Integer getInteger(String key) {
		String val = super.getProperty(key);
		return Integer.valueOf(val);
	}

	/**
	 * 获取Integer类型的数据,并提供默认的值,若数据格式不可转换为Integer类型或者为null时,将会返回默认值
	 * @param key
	 * @param defaultVal
	 * @return
	 */
	public Integer getInteger(String key, Integer defaultVal) {
		Integer i = null;
		try {
			i = getInteger(key);
		} catch (NumberFormatException e) {
		}
		if (i == null) {
			i = defaultVal;
		}
		return i;
	}

	/**
	 * 获取Long类型的数据,若数据格式不可转换为Long类型,将抛出异常{@link NumberFormatException}
	 * @param key
	 * @return
	 */
	public Long getLong(String key) {
		String val = super.getProperty(key);
		return Long.valueOf(val);
	}

	/**
	 * 获取Long类型的数据,并提供默认的值,若数据格式不可转换为Long类型或者为null时,将会返回默认值
	 * @param key
	 * @param defaultVal
	 * @return
	 */
	public Long getLong(String key, Long defaultVal) {
		Long i = null;
		try {
			i = getLong(key);
		} catch (NumberFormatException e) {
		}
		if (i == null) {
			i = defaultVal;
		}
		return i;
	}

	/**
	 * 获取Double类型的数据,若数据格式不可转换为Double类型,将抛出异常{@link NumberFormatException}
	 * @param key
	 * @return
	 */
	public Double getDouble(String key) {
		String val = super.getProperty(key);
		return Double.valueOf(val);
	}

	/**
	 * 获取Double类型的数据,并提供默认的值,若数据格式不可转换为Double类型或者为null时,将会返回默认值
	 * @param key
	 * @param defaultVal
	 * @return
	 */
	public Double getDouble(String key, Double defaultVal) {
		Double i = null;
		try {
			i = getDouble(key);
		} catch (NumberFormatException e) {
		}
		if (i == null) {
			i = defaultVal;
		}
		return i;
	}

	/**
	 * 获取BigDecimal类型的数据,若数据格式不可转换为BigDecimal类型,将抛出异常
	 * {@link NumberFormatException}
	 * @param key
	 * @return
	 */
	public BigDecimal getBigDecimal(String key) {
		String val = super.getProperty(key);
		return new BigDecimal(val);
	}

	/**
	 * 获取BigDecimal类型的数据,并提供默认的值,若数据格式不可转换为BigDecimal类型或者为null时,将会返回默认值
	 * @param key
	 * @param defaultVal
	 * @return
	 */
	public BigDecimal getBigDecimal(String key, BigDecimal defaultVal) {
		BigDecimal i = null;
		try {
			i = getBigDecimal(key);
		} catch (NumberFormatException e) {
		}
		if (i == null) {
			i = defaultVal;
		}
		return i;
	}

	/**
	 * 获取Boolean类型的数据,若数据格式不可转换为Boolean类型,将返回false
	 * @param key
	 * @return
	 */
	public Boolean getBoolean(String key) {
		String val = super.getProperty(key);
		return Boolean.valueOf(val);
	}

	/**
	 * 获取String类型的数据
	 * @param key
	 * @return
	 */
	public String getString(String key) {
		return super.getProperty(key);
	}
	
}

实际上除了扩展原有功能外,还可以修改原有方法的实现,在原有方法前后增加新的执行方法,这种使用方法有点代理模式的感觉。
建议:如果只是扩展新功能(提供新方法),类的后缀使用 Wrap,如果对原有方法进行了修改则使用 Decorator

结论

在不修改原有类的基础上进行扩展功能,可代替继承。特别适合对一些 jar 包或者不能修改源代码功能的扩展。如果多装饰几次,就会变得无比的麻烦,可参考 java IO 的实现方式。


  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3453 回帖 • 203 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...