【问题标题】:How to handle redundant types in Crystal?如何处理 Crystal 中的冗余类型?
【发布时间】:2018-04-01 02:01:31
【问题描述】:

我正在使用crystal language,到目前为止效果很好。不幸的是,我觉得 我的 代码变得有点太乱了,到处都有类型。

例如:

  # ---------=====----++---
  # Grab characters
  # ---------=====----++---
  def handle_character_list(msg, client)
    result = {} of String => Array(Tuple(Int64, String, String, Int16, Int8)) | Int32 | Int16 | Int64 | String

    result["characters"] = db.query_all "select character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc from rpg_characters where user_id = ? ", client.user_id,
      as: {Int64, String, String, Int16, Int8}

    result["max_char_slots"] = client.user_data["max_char_slots"]

    puts result
  end

看着db.query_all method,它说:

返回一个数组,其中每一行的值被读取为给定类型

有了上述,为什么我需要再次明确地将这些类型设置为我的result 变量,如果它们要返回?

我觉得我做错了什么,感谢任何建议/见解。

【问题讨论】:

    标签: crystal-lang


    【解决方案1】:

    首先引起我注意的是哈希类型的大小。您似乎在使用 Hash 与在 Ruby 中的方式相同。不要。

    在 Ruby 或其他动态语言中,Hashes 或对象用作通用数据容器,几乎就像未命名的类一样。在 Crystal 中,散列具有单一类型的键和单一类型的值,这使得它们不适合该任务。您想告诉 Crystal 更多有关数据结构的信息,因此您不必一直重复。

    首先要做的是查看result 对象。它可以简单地转换成record Result

    record Result,
      characters: Array({Int64, String, String, Int16, Int8}),
      max_char_slots: Int32
    

    方法就变成了

    def handle_character_list(msg, client)
      sql = <<-SQL
        SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
        FROM rpg_characters
        WHERE user_id = ?
        SQL
      characters = db.query_all sql, client.user_id, as: {Int64, String, String, Int16, Int8}
    
      max_char_slots = client.user_data["max_char_slots"]
    
      Result.new(characters, max_char_slots)
    end
    

    但是,通过查看该方法,可能会发现这条Result 记录仅用于一个地方——从该方法返回数据。在这种情况下,您不太可能想给它一个更正式的名称。在这种情况下,您可以使用NamedTuple。它们有点像匿名记录。

    def handle_character_list(msg, client)
      sql = <<-SQL
        SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
        FROM rpg_characters
        WHERE user_id = ?
        SQL
    
      {
        characters: db.query_all(sql, client.user_id, as: {Int64, String, String, Int16, Int8}),
        max_char_slots: client.user_data["max_char_slots"]
      }
    end
    

    再进一步,我们可以看到“字符”也是一种类型:

    class Character
      getter id : Int64
      getter name : String
      getter created : Time
      getter level : Int16
      getter cc : Int8
    
      def initialize(@id, @name, @created, @level, @cc)
      end
    end
    

    然后我们可以使用DB.mapping 来定义Character 类在数据库中的外观。

    class Character
      DB.mapping({
        id: Int64,
        name: String.
        created: Time,
        level: Int16,
        cc: Int8
      })
    
      def initialize(@id, @name, @created, @level, @cc)
      end
    end
    

    这个定义和上一个是等价的,因为DB.mapping会自动为我们生成getter。

    def handle_character_list(msg, client)
      sql = <<-SQL
        SELECT character_id, character_name, created, level, cc
        FROM rpg_characters
        WHERE user_id = ?
        SQL
    
      {
        characters: db.query_all(sql, client.user_id, as: Character),
        max_char_slots: client.user_data["max_char_slots"]
      }
    end
    

    更进一步,我会将其提取为两种方法,每种方法只做一件事,我可能会让client.user_data 更安全:

    def characters_for_user(user_id)
      sql = <<-SQL
        SELECT character_id, character_name, created, level, cc
        FROM rpg_characters
        WHERE user_id = ?
        SQL
      db.query_all(sql, user_id, as: Character)
    end
    
    def handle_character_list(msg, client)
      {
        characters: characters_for_user(client.user_id),
        max_character_slots: client.user_data.max_char_slots
      }
    end
    

    这只是我对如何编写您展示的代码的思考过程。我对您的代码和数据库做了很多假设,这些假设可能是错误的(即“创建”是 mysql 中的 DATETIME)。我试图展示一个思考过程,而不是一个完成的解决方案。希望对您有所帮助。

    【讨论】:

    • 哇,在Character 类中使用DB.mapping 正是减少冗余的原因,并且比我所做的要简单得多。这个答案超越了它,真的很感激。事实上,我已经为它添加了书签
    猜你喜欢
    • 1970-01-01
    • 2014-02-20
    • 1970-01-01
    • 2019-02-19
    • 2019-01-11
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 2015-12-09
    相关资源
    最近更新 更多