Java8:如何针对传入类型调用方法?装饰器模式实现疑问
解决装饰器模式中多类型生成源的掩码适配问题
嘿,这个场景我之前做用户数据脱敏模块时也碰到过!咱们来聊聊怎么优雅解决这个问题,同时也说说你考虑的类型强转思路的优缺点:
先说说你提到的「传入类型强制转换」方案
这个思路确实能快速凑效,但有两个明显的坑:
- 类型安全风险:如果传入的对象类型和预期不匹配,会直接抛出
ClassCastException,而且这类问题往往要到运行时才暴露,排查成本很高。 - 违反开闭原则:以后新增一个生成源类型,你就得修改掩码器的代码,加新的类型判断和强转逻辑,代码会越来越臃肿难维护。
所以更推荐下面两种更优雅的替代方案:
方案一:用适配器模式封装统一接口
核心思路是给每个没有公共父类的生成源,写一个适配器类,让适配器实现统一的掩码操作接口,内部把生成源的特定getter/setter转换成接口方法。这样你的掩码器只需要依赖这个统一接口,完全不用关心具体是哪个生成源。
举个Java代码例子:
// 定义统一的掩码操作接口 public interface Maskable { // 获取需要掩码的字段值 String getMaskTarget(); // 设置掩码后的字段值 void setMaskedResult(String maskedValue); } // 针对生成源SourceA的适配器 public class SourceAAdapter implements Maskable { private SourceA source; public SourceAAdapter(SourceA source) { this.source = source; } @Override public String getMaskTarget() { return source.getPersonalId(); // 对应SourceA的getter } @Override public void setMaskedResult(String maskedValue) { source.setPersonalId(maskedValue); // 对应SourceA的setter } } // 针对生成源SourceB的适配器 public class SourceBAdapter implements Maskable { private SourceB source; public SourceBAdapter(SourceB source) { this.source = source; } @Override public String getMaskTarget() { return source.getUserPhone(); // 对应SourceB的getter } @Override public void setMaskedResult(String maskedValue) { source.setUserPhone(maskedValue); // 对应SourceB的setter } }
之后你的掩码装饰器只需要处理Maskable类型的对象就行:
public class MaskDecorator implements Maskable { private Maskable target; public MaskDecorator(Maskable target) { this.target = target; } @Override public String getMaskTarget() { return target.getMaskTarget(); } @Override public void setMaskedResult(String maskedValue) { // 这里可以加装饰逻辑,比如不同类型的掩码规则 String finalMasked = applyMaskRule(target.getMaskTarget()); target.setMaskedResult(finalMasked); } private String applyMaskRule(String original) { // 示例:身份证号掩码 return original.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2"); } }
使用时把生成源包进适配器,再传给装饰器即可:
SourceA sourceA = new SourceA(); Maskable adapterA = new SourceAAdapter(sourceA); MaskDecorator decorator = new MaskDecorator(adapterA); decorator.setMaskedResult(null); // 触发掩码逻辑
方案二:泛型+函数式接口解耦(更灵活)
如果不想写一堆适配器类,可以用泛型结合函数式接口,把getter和setter作为参数传入掩码器,完全摆脱对具体类型的依赖。
还是用Java举例:
// 泛型掩码器,通过函数式接口接收getter和setter public class GenericMasker<T> { // 用于获取需要掩码的字段 private Function<T, String> valueGetter; // 用于设置掩码后的字段 private BiConsumer<T, String> valueSetter; public GenericMasker(Function<T, String> valueGetter, BiConsumer<T, String> valueSetter) { this.valueGetter = valueGetter; this.valueSetter = valueSetter; } // 执行掩码操作 public void mask(T target) { String original = valueGetter.apply(target); String masked = doMask(original); valueSetter.accept(target, masked); } private String doMask(String original) { // 自定义掩码逻辑:手机号/默认规则 if (original.length() == 11) { return original.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); } else { return original.substring(0, 2) + "****" + original.substring(original.length()-2); } } }
使用时直接传入方法引用,灵活度拉满:
// 处理SourceA SourceA sourceA = new SourceA(); GenericMasker<SourceA> sourceAMasker = new GenericMasker<>(SourceA::getPersonalId, SourceA::setPersonalId); sourceAMasker.mask(sourceA); // 处理SourceB SourceB sourceB = new SourceB(); GenericMasker<SourceB> sourceBMasker = new GenericMasker<>(SourceB::getUserPhone, SourceB::setUserPhone); sourceBMasker.mask(sourceB);
这种方案完全不需要修改原有生成源的代码,类型安全,新增类型时只需要创建对应的GenericMasker实例就行,完美符合开闭原则。
总结
- 如果你需要严格遵循装饰器模式的结构,适配器模式是最稳妥的选择;
- 如果追求代码简洁和灵活性,泛型+函数式接口的方案更高效;
- 尽量避免直接类型强转,除非是临时小项目,否则长期维护风险太高。
内容的提问来源于stack exchange,提问作者Amer Qarabsa
相关产品推荐
相关产品推荐

