如何设计一个流畅的界面

本文关键字:一个 界面 | 更新日期: 2023-09-27 18:15:46

我一直在尝试为我的一个框架设计一个流畅的界面,但似乎我无法理解这个难题的一个部分。我知道我可以使用接口和类来驱动我想要调用的方法。但是,请考虑以下场景。

假设我有一个Person类我想做一些像

这样的事情
Person.WithName("Steve").WithAge(18).Save();

同样,我也希望API的调用者做类似

的事情
Person.WithName("Steve").Save();
or
Person.WithAge(18).Save();

但是我不希望用户单独调用save方法,比如

Person.Save();

现在,如果我想设计这样一个API,我怎么实现它?如果我从WithName和WithAge方法返回Person类的实例,那么我必须将Save方法也放在Person类中,这意味着用户可以直接调用它。

如何设计一个流畅的界面

正如您所指出的,您可以使用接口来控制什么是可见的。使用显式接口实现可以在某些情况下隐藏方法,而在其他情况下公开方法。它还允许您拥有多个具有相同签名的方法。

在本例中,我们有一个私有构造函数,因此Person只能使用一个静态入口点创建。一旦我们有了名字或年龄,我们返回person的实例,调用WithNameWithAgeSave是有效的。

public class Person : IPersonBuilder
{
  private string _name;
  private int? _age;
  private Person() { }
  public static IPersonBuilder WithName(string name)
  {
    return ((IPersonBuilder)new Person()).WithName(name);
  }
  public static IPersonBuilder WithAge(int age)
  {
    return ((IPersonBuilder)new Person()).WithAge(age);
  }
  IPersonBuilder IPersonBuilder.WithName(string name)
  {
    _name = name;
    return this;
  }
  IPersonBuilder IPersonBuilder.WithAge(int age)
  {
    _age = age;
    return this;
  }
  public void Save()
  {
    // do save
  }
}
public interface IPersonBuilder
{
  IPersonBuilder WithName(string name);
  IPersonBuilder WithAge(int age);
  void Save();
}

如果Person是一个具有超越流畅接口意义的类——它是某种实体——那么我将创建一个单一的静态入口点,返回一个PersonBuilder对象,并将所有其余的流畅关注点移出Person

您可能希望区分创建和属性设置。也许你想要这样写:

public interface IPerson
{
    IPerson WithName(string name);
    IPerson WithAge(int age);
}
public class Person : IPerson
{
    //You can also add required parameters here.  That'll 
    //ensure that a person is not saved before his specifications
    //are atleast minimally specified.
    public Person() { }
}
new Person().WithAge(18).WithName("Steven").Save();

或者,如果你只是希望开发人员能够在不鼓励修改的情况下构建一个人,这个人在构建后。

public interface IPersonBuilder
{
    IPersonBuilder WithName(string name);
    IPersonBuilder WithAge(int age);
    IPerson Save()
}
public interface IPerson
{
    public string Name { get; }
    public int Age { get; }
}
public class PersonBuilder
{
    public PersonBuilder() { }
}
new PersonBuilder().WithAge(18).WithName("Steven").Save();