You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:40:23