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

如何禁止Fluent Builder Interface中可选字段方法重复调用

解决Fluent Builder可选方法重复调用问题

核心思路是通过拆分阶段接口,在编译层面限制已调用的可选方法无法重复调用,同时配合运行时校验防止绕过编译限制。以下是具体实现方案(以C#为例):

1. 定义阶段接口

拆分不同的可选字段阶段,每个阶段仅暴露未被调用过的方法:

// 第一步:必填FirstName,返回Prefix选择阶段
public interface IFirstNameSelectionStage
{
    IPrefixSelectionStage WithFirstName(string firstName);
}

// 第二步:必填Prefix,进入可选字段初始阶段
public interface IPrefixSelectionStage
{
    IOptionalFieldsInitialStage WithPrefix(string prefix);
}

// 可选字段初始阶段:可调用WithMiddleName或WithLastName
public interface IOptionalFieldsInitialStage : IFacultyNumberSelectionStage
{
    IOptionalFieldsWithoutMiddleNameStage WithMiddleName(string middleName);
    IOptionalFieldsWithoutLastNameStage WithLastName(string lastName);
}

// 已调用WithMiddleName后:仅能调用WithLastName和必填FacultyNumber
public interface IOptionalFieldsWithoutMiddleNameStage : IFacultyNumberSelectionStage
{
    IOptionalFieldsWithoutBothStage WithLastName(string lastName);
}

// 已调用WithLastName后:仅能调用WithMiddleName和必填FacultyNumber
public interface IOptionalFieldsWithoutLastNameStage : IFacultyNumberSelectionStage
{
    IOptionalFieldsWithoutBothStage WithMiddleName(string middleName);
}

// 两个可选字段都已调用:仅能调用必填FacultyNumber
public interface IOptionalFieldsWithoutBothStage : IFacultyNumberSelectionStage
{
}

// 必填FacultyNumber阶段:填完后进入完成阶段
public interface IFacultyNumberSelectionStage
{
    IBuilderCompletionStage WithFacultyNumber(string facultyNumber);
}

// 最终完成阶段:创建实例
public interface IBuilderCompletionStage
{
    Person Build();
}

2. 实现Builder类

实现所有阶段接口,同时添加运行时校验防止强制类型转换绕过限制:

public class PersonBuilder : IFirstNameSelectionStage, IPrefixSelectionStage,
    IOptionalFieldsInitialStage, IOptionalFieldsWithoutMiddleNameStage,
    IOptionalFieldsWithoutLastNameStage, IOptionalFieldsWithoutBothStage,
    IFacultyNumberSelectionStage, IBuilderCompletionStage
{
    private readonly Person _person = new Person();

    public IPrefixSelectionStage WithFirstName(string firstName)
    {
        _person.FirstName = firstName;
        return this;
    }

    public IOptionalFieldsInitialStage WithPrefix(string prefix)
    {
        _person.Prefix = prefix;
        return this;
    }

    public IOptionalFieldsWithoutMiddleNameStage WithMiddleName(string middleName)
    {
        if (!string.IsNullOrEmpty(_person.MiddleName))
            throw new InvalidOperationException("MiddleName已设置,不可重复赋值");
        _person.MiddleName = middleName;
        return this;
    }

    public IOptionalFieldsWithoutLastNameStage WithLastName(string lastName)
    {
        if (!string.IsNullOrEmpty(_person.LastName))
            throw new InvalidOperationException("LastName已设置,不可重复赋值");
        _person.LastName = lastName;
        return this;
    }

    // 实现IOptionalFieldsWithoutLastNameStage的WithMiddleName方法
    public IOptionalFieldsWithoutBothStage WithMiddleName(string middleName)
    {
        if (!string.IsNullOrEmpty(_person.MiddleName))
            throw new InvalidOperationException("MiddleName已设置,不可重复赋值");
        _person.MiddleName = middleName;
        return this;
    }

    // 实现IOptionalFieldsWithoutMiddleNameStage的WithLastName方法
    public IOptionalFieldsWithoutBothStage WithLastName(string lastName)
    {
        if (!string.IsNullOrEmpty(_person.LastName))
            throw new InvalidOperationException("LastName已设置,不可重复赋值");
        _person.LastName = lastName;
        return this;
    }

    public IBuilderCompletionStage WithFacultyNumber(string facultyNumber)
    {
        _person.FacultyNumber = facultyNumber;
        return this;
    }

    public Person Build()
    {
        return _person;
    }
}

// 目标实体类
public class Person
{
    public string FirstName { get; set; }
    public string Prefix { get; set; }
    public string FacultyNumber { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
}

3. 效果验证

  • 编译层面限制:调用过WithMiddleName后,返回的接口不再包含该方法,编译器会直接报错,无法重复调用:
    // 错误示例:编译报错,IOptionalFieldsWithoutMiddleNameStage无WithMiddleName方法
    var invalidBuilder = new PersonBuilder()
        .WithFirstName("John")
        .WithPrefix("Mr.")
        .WithMiddleName("Michael")
        .WithMiddleName("James");
    
  • 运行时兜底:即使通过强制类型转换绕过编译限制,也会抛出异常,避免重复赋值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 13:50:25