首页 > 解决方案 > 创建 JSON 树时模块之间的循环依赖

问题描述

我开发了一个库,它使用反射为给定的类创建 JSON Hyperschema。我选择了 F# 作为编程语言,但没有太多经验。

像其他人(this or this question)一样,我面临的问题是我想在模块之间创建循环依赖,但我不能......

应该给库的 main 函数 aType并返回 a JsonSchema

type JsonSchema = {
    Type: JsonType
    Links: seq<LinkDescriptor>
}

AJsonType是基元、数组、对象或可为空的。但是数组有一个items类型的字段JsonSchema,一个对象有一个列表JsonProperties,它再次引用JsonSchema

type JsonType =
    | JsonBoolean
    | JsonInteger
    | JsonNumber
    | JsonString
    | JsonArray of items: JsonSchema
    | JsonObject of properties: seq<JsonProperty>
    | Nullable of underlyingType: JsonType
type JsonProperty = {
    Schema: JsonSchema
    Name: string
}

然后是 中LinkDescriptor使用的类型JsonSchema,它再次具有类型字段JsonSchema

type LinkDescriptor = {
    Rel: string
    Href: UriTemplate
    HrefSchema: JsonSchema option
}

如果我采用这种方法并将所有类型写入一个文件并将它们与 链接起来and,我将被迫使创建类型的函数相互递归。

let rec getJsonType (t: Type): JsonType =
    // Handle primitive types
    if (t.IsArray) then
        let itemsType = t.GetElementType()
        let schema = {
            Type = getJsonType itemsType
            Links = getLinksForType itemsType
        }
        JsonArray schema
    else JsonObject <| getJsonProperties t

and getJsonProperties (t: Type): seq<JsonProperty> =
    getPublicProperties t
    |> Seq.map (fun p -> {
           Name = p.Name
           Schema = {
               Type = getJsonType p.PropertyType
               Links = getLinksForProperty p
           }
       })

and getLinksForType (t: Type): seq<LinkDescriptor> =
    getLinkAttributes t
    |> Seq.map (fun attr -> {
           Rel = attr.Rel
           Href = attr.Href
           HrefSchema =
               if isNull attr.HrefSchemaType
               then None
               else Some { Type = getJsonType attr.HrefSchemaType; Links = Seq.empty<LinkDescriptor> }
       })

实际上,这些功能有点复杂,这就是为什么我希望它们位于不同的模块中。

我已阅读Refactoring to remove cyclic dependencies,但不知道如何在我的案例中应用参数化。

我能想到的唯一想法是:我一次只创建一个层,而不是一次创建完整的 JSON 树。也就是说,JsonType存储底层System.Type

type JsonType =
    | JsonBoolean
    // ...
    | JsonArray of Type
    | JsonObject of Type
    | Nullable of underlyingType: JsonType

然后我可以创建一个JsonType包含类似函数的模块getItemsSchema,它JsonSchema根据存储的JsonArray. 也是如此getObjectProperties

但是我对这种方法并不是很满意,因为它感觉有点像一个泄漏的抽象。

您对我如何改进上述代码以摆脱循环依赖有什么建议吗?

标签: f#

解决方案


鉴于您的函数逻辑是紧密耦合的,我认为它们应该保留在同一个模块中。

但是,如果您仍然想分解它们,您可以执行以下操作:

想象一下,您有以下一组相互依赖的函数:

let rec f x = g x + h x
and g x = f x
and h x = f x

为了在自己的模块中声明每一个,您可以显式定义它们的函数依赖关系。例如函数 f 依赖于函数 g 和 h:

module moduleF = 
   let _f (g:int->int) (h:int->int) x = g x + h x

您可以对 g 和 h 执行相同的操作,同时在不同的模块中实现它们:

module moduleG = 
       let _g (f:int->int) x = f x

module moduleH =
       let _h (f:int->int) x = f x

最后,您可以将它们全部集成到第四个模块中,为您的每个功能提供必要的依赖关系。

module Final = 
    open ModuleF
    open ModuleG
    open ModuleH


    let rec f x = _f g h x
    and g x = _g f x
    and h x = _h f x

推荐阅读