如何解决使用接口编写流畅风格的 API 时避免代码重复
我正在尝试编写一个流畅的 API,用于在我的 C# 程序中构建实体。
我有一个基础实体,它有一些属性和一组多态实体:它们都来自一个共同的基础类型,但有些会有额外的属性。
这是一个例子:
public interface IGarageBuilder
{
public IGarageBuilder Label(string label);
public IGarageBuilder AddCar(Action<ICarBuilder> configure);
public IGarageBuilder AddBike(Action<IBikeBuilder > configure);
}
public interface IVehicleBuilder
{
public IVehicleBuilder Model(string model); // <- That is the member that is causing me trouble
}
public interface IBikeBuilder : IVehicleBuilder
{
}
public interface ICarBuilder : IVehicleBuilder
{
public ICarBuiler HorsePoser(int horsePower);
}
(我写的软件有很多 IVehicleBuilder 共有的属性,我把它简化了很多)。
我在使用 IVehicleBuilder 接口时遇到问题:如上定义,以下代码将无法编译:
public void BuildGarage(IGarageBuilder builder)
{
builder.Label("My dream garage")
.AddCar(opt => opt.Model("Ford").HorsePower(120)); // <- this fails because .Model() returns a IVehicleBuilder interface,not an ICarBuilder
}
现在,我可以不依赖 IVehicleBuilder 和 ICarBuilder 并将“Model()”API 复制到 ICarBuilder 和 IBikeBuilder 但是,在我的真实应用程序中,我有几十个这样的 API,其中几个被覆盖以允许配置实体的不同方式。此外,我还必须复制这些实现,因为它们现在返回不同的类型。
有没有办法避免所有这些代码重复?
解决方法
减少重复的一种方法是将您的界面分为两个级别。一个级别描述了可用的不同类型的方法(或方法组),更高级别使用接口继承将这些方法组合成 API 使用的流畅接口:
public interface IHasModel<T>
{
T Model(string model);
}
public interface ICarBuilder : IHasModel<ICarBuilder>
{
// Things which are specific to just cars can go in here if you want,rather than
// taking up a separate IHasHorsePower<T>
ICarBuilder HorsePower(int horsePower);
}
public interface IBikeBuilder : IHasModel<IBikeBuilder> { }
此外,尽量为所有接口保持相同的底层构建器实现。这可以具有仅由单个构建器使用的方法和状态位,这很好。
internal class Builder : ICarBuilder,IBikeBuilder
{
// May or may not be used,depending on what we're building...
private string model;
private int horsePower;
public Builder Model(string model)
{
this.model = model;
return this;
}
// Annoyingly return type covariance isn't yet supported for implicit interface
// implementations,so we need this boilerplate
ICarBuilder IHasModel<ICarBuilder>.Model(string model) => Model(model);
IBikeBuilder IHasModel<IBikeBuilder>.Model(string model) => Model(model);
// You can avoid the song-and-dance for simple things,which are just referenced
// by a single interface
public ICarBuilder HorsePower(int horsePower)
{
this.horsePower = horsePower;
return this;
}
// I assume you'll have something like this as well...
public Car BuildCar() => new Car(model,horsePower);
public Bike BuildBike() => new Bike(model);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。