【问题标题】:Can I avoid writing boilerplate code for classes that derive from an interface?我可以避免为从接口派生的类编写样板代码吗?
【发布时间】:2018-05-08 17:48:02
【问题描述】:

我有许多实现接口的类。每当我定义一个类时,我都必须在类定义中编写类似这样的内容:

interface Model.IModel with

        member this.Assets          = modelArg.Assets
        member this.AuxData         = modelArg.AuxData
        member this.Benchmark       = modelArg.Benchmark
        member this.CalcPeriods     = modelArg.CalcPeriods
        member this.CashTicker      = modelArg.CashTicker
        member this.Description     = modelArg.Description
        member this.FirstValidDate  = modelArg.FirstValidDate
        member this.Name            = modelArg.Name
        member this.ParamData       = modelArg.ParamData
        member this.ParamList       = modelArg.ParamList
        member this.Strategy        = modelArg.Strategy

        member this.CalcStartTime with get() = calcStartTime and
                                       set v = calcStartTime <- v

        member this.Counter with get() = counter and
                                 set v = counter <- v

有没有办法避免对从接口 IModel 派生的每个类重复此操作?代码在所有情况下都是相同的。

【问题讨论】:

  • 实现接口的基类和从该基类继承的各个实现是我能想到的唯一方法。 (我有点怀疑这里更有趣的部分是更大的上下文和目的,因为很可能完全不同的方法可能更符合 F# 的习惯。)
  • 是的,尽可能避免类和继承是 F# 中的一个重要目标,但它是一种多范式语言,在某些情况下使用“对象编程”(如 Don Syme 所说)是必要的或只是更好的方法。但是你提到的事情就是我所说的更大的上下文将是有趣的事情时的意思,因为这可能允许做出完全不同的设计决策。不过,我猜这将是一个不同的问题,并且需要查看更多您可能无法共享的代码。
  • 我不介意共享代码,但它会很大而且很麻烦。我将尝试找出一种简单的方法来展示我正在尝试做的事情并将其放在另一个问题中。
  • 为什么每堂课都要写这个?拥有大量需要在任何地方包含的属性似乎是一个巨大的设计问题。
  • 为什么不能只提供一个默认实现?

标签: inheritance interface f# boilerplate


【解决方案1】:

为了让讨论更具体一些,并且由于 cmets 的空间有限,下面的内容对您有用还是不合适?

 type ModelArg = {
     Number: int
     Name: string
 }

 type IModel(modelArg: ModelArg) = 
    abstract member Number: int
    abstract member Name: string
    default __.Number = modelArg.Number
    default __.Name = modelArg.Name

type ConcreteModel1(modelArg: ModelArg) =
    inherit IModel(modelArg)

type ConcreteModel2(modelArg: ModelArg) =
    inherit IModel(modelArg)

let modelArg1 = {Number=2; Name ="Joe"}
let modelArg2 = {Number=3; Name = "Jim"}

let getNumberAndName(x: IModel) =
    (x.Number, x.Name)

let model1 = ConcreteModel1(modelArg1)
let model2 = ConcreteModel2(modelArg2)

getNumberAndName(model1)
//val it : int * string = (2, "Joe")
getNumberAndName(model2)
//val it : int * string = (3, "Jim")

【讨论】:

  • 可能。我会试试。三个基本问题: 1) Microsoft 文档说default 关键字“表示抽象方法的实现;与抽象方法声明一起使用以创建虚拟方法。”但在这种情况下,default 是抽象成员,而不是抽象方法。这个可以吗? 2) 在您的示例中,可以 not 将 Number 和 Name 作为抽象吗?如果是的话,将它们作为抽象是否有优势? 3)我的类中有三个抽象方法。我必须用[&lt;AbstractClass&gt;] 注释基类吗?
  • @Soldalma 1. 是的。 2. 是的,您可以创建一个包含您需要的逻辑的类型,并将其作为构造函数参数 ala 依赖注入传递,然后将整个类型公开为属性并对其进行操作。您问题的第二部分与此有关。 3. 使用抽象类的好处是您可以混合实现和未实现的成员。您还可以在以后的实现中覆盖它们。如果您提供没有默认实现的抽象成员,我相信您需要使用[&lt;AbstractClass&gt;] 进行注释。
  • 好的,成功了。消除了许多样板。谢谢。
猜你喜欢
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多