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

不修改MoneyAdder类如何适配需要客户名参数的OldLogger组件

思路与术语指正

  • 你前面3个适配器的实现完全符合对象适配器模式的规范,也遵循了开闭原则,术语使用正确。
  • 若OldLogger原本是直接在MoneyAdder内部硬编码实例化调用,确实属于紧耦合(Tightly Coupled),你的判断正确。
  • 你之前的认知存在误区:除了方法入参、可计算值、全局信息外,还可以通过组合根(Composition Root)的依赖注入上下文为具体实现传递额外参数,完全不需要修改目标类的代码。

适配OldLogger的方案

方案1:DI上下文工厂注入(推荐,无性能损耗,符合设计规范)

这个方案的核心是:MoneyAdder的构造本身就需要传入Customer实例,你可以在组装MoneyAdder的组合根位置,提前将对应Customer的Name传入OldLoggerAdapter的构造函数,不需要修改ILogger接口和MoneyAdder的任何代码。

代码实现

首先改造OldLoggerAdapter,将客户名作为构造参数传入:

public class OldLoggerAdapter : ILogger
{
    private readonly string _customerName;
    private readonly OldLogger _oldLogger = new OldLogger();

    // 构造函数接收客户名,不需要修改ILogger接口定义
    public OldLoggerAdapter(string customerName)
    {
        _customerName = customerName;
    }

    public void log(string str)
    {
        _oldLogger.log(str, _customerName);
    }
}

然后在你创建MoneyAdder的位置(也就是程序的组合根)组装实例即可:

// 该部分属于你的程序启动/实例组装逻辑,完全不需要修改MoneyAdder的源码
var currentCustomer = GetCustomerFromRequest(); // 你原本就要获取Customer实例传入MoneyAdder
var logger = new OldLoggerAdapter(currentCustomer.Name);
var moneyAdder = new MoneyAdder(logger, currentCustomer);

如果使用DI容器(比如.NET自带的DI、Autofac等),可以通过Scope工厂注册实现自动注入:

// .NET 自带DI注册示例
services.AddScoped<MoneyAdder>(sp =>
{
    var customer = sp.GetRequiredService<Customer>(); // 取当前Scope下的Customer实例
    var logger = new OldLoggerAdapter(customer.Name);
    return new MoneyAdder(logger, customer);
});

方案2:反射获取调用方私有字段(不推荐,仅作方案补充)

如果确实无法在组合根处理参数传递,可以通过调用栈反射获取MoneyAdder实例的私有Customer字段,注意该方案有性能损耗,且依赖MoneyAdder的内部结构,一旦MoneyAdder内部字段改名就会失效。

代码实现

using System.Diagnostics;
using System.Reflection;

public class OldLoggerAdapter : ILogger
{
    private readonly OldLogger _oldLogger = new OldLogger();
    private static readonly FieldInfo _customerField = typeof(MoneyAdder).GetField("customer", BindingFlags.Instance | BindingFlags.NonPublic);

    public void log(string str)
    {
        // 获取调用方的MoneyAdder实例
        var callFrame = new StackFrame(1);
        if (callFrame.GetMethod().DeclaringType != typeof(MoneyAdder))
        {
            throw new InvalidOperationException("该日志实现仅支持MoneyAdder调用");
        }
        var moneyAdderInstance = callFrame.This;
        // 反射拿Customer字段的值
        var customer = (Customer)_customerField.GetValue(moneyAdderInstance);
        _oldLogger.log(str, customer.Name);
    }
}

额外优化建议

  • 你之前的适配器实现中直接硬编码new OtherLogger()/new StrangeLogger(),可以将第三方日志实例改为适配器的构造参数,由DI容器注入,更方便替换和单元测试。
  • 接口命名建议遵循C#规范,方法名首字母大写,比如ILogger的Log方法,不过这个属于编码规范问题,不影响设计逻辑。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 23:09:03