【问题标题】:Groovy/Grails how to make constructor private - the right wayGroovy/Grails 如何使构造函数私有 - 正确的方法
【发布时间】:2013-08-16 16:43:13
【问题描述】:

我在 grails 中有一个域类,它只应使用相同的名称创建一次。为了确保我有一个静态方法 getColor 和私有构造函数,如下所示:

class Color {
  String name

  static hasMany = [moods: Mood] 

  // not accessible
  private Color() {}

  // not accessible because getColor should be used
  private Color(String name) {
    this.name = name
  }

  static getColor(String name) {
    def color = Color.findByName(name.toLowerCase())
    color ? color : new Color(name).save(flush:true) 
  }

  def beforeValidate() {
    name = name.toLowerCase();
  }
}

为了确保仅使用静态 getColor 方法创建 Color 对象,我想将构造函数设为私有。到目前为止,它工作得很好,我可以创建颜色对象。但是当我使用这个实例来创建 Object Mood 的对象时

class Mood {

  static belongsTo = [color:Color]

}

def color = Color.getColor('verylightgreen')
def mood = new Mood(color: color)

我得到一个例外:

error initializing the application: Could not instantiate bean class [de.tobi.app.Color]: Is the constructor accessible?

这个异常是由

抛出的
def mood = new Mood(color: color)

那么为什么创建 Mood 需要访问 Color 的构造函数。我已经通过了对象.. 一般来说,在 groovy/grails 中隐藏域类的构造器以控制对象的创建方式的最佳方法是什么。特别是地图控制器的使用也应该被禁用。

【问题讨论】:

  • 为什么不用枚举来做这种事情呢?
  • 因为用户应该能够动态添加新对象
  • 我认为您应该考虑通过数据库约束强制执行唯一性。
  • 是的,我也考虑过,我认为这将是一个替代方案.. 但我仍然认为,因为它是一种广泛传播的编程模式,想要在某些情况下控制对象创建,它应该是可能的太时髦了
  • 是什么阻碍了您将name 设为主键? ColorMood 是如何关联的?

标签: grails groovy grails-orm


【解决方案1】:

使用name作为Color的主键可以实现唯一性。另外,findOrSaveBy*可以用来替换自定义的静态方法getColor

如果将 name 设为主键不是一个可行的选择,那么您可以继续添加一个约束,正如 cmets 中几乎所有个人都提到的那样。 :)

class Color {
  String name

  static mapping = {
      //column is optional
      id name: 'name', generator: 'assigned', type: 'string', column: 'NAME' 
  }

  static hasMany = [moods : Mood] //for example

  //DO NOT NEED THIS
  //This can be achieved by findOrSaveBy*
  /*static getColor(String name) {
    def color = Color.findByName(name.toLowerCase())
    color ? color : new Color(name).save(flush:true) 
  }*/

  def beforeValidate() {
    name = name.toLowerCase();
  }
}

那你可以做得很好

def color = Color.findOrSaveByName('verylightgreen')
def mood = new Mood()

color.addToMoods(mood)
color.save()

【讨论】:

  • 所以在这种情况下它是一个很好的方法......但这是否意味着您在 grails 中根本不将您的域类构造函数设为私有?
  • @tobi 从未使用过或想过它,因为我使用映射和约束块获得了这种灵活性。
  • @SérgioMichels Buddy,GORM 作为雷朋玻璃已经很久了,这取决于佩戴它的开发者。 ;) .想想 Ratpack,计划是在里面放一些像 GORM 这样的东西。如果我在 Node.js 中看到类似 GORM 的东西,或者在我使用它时找到类似的东西,我会是最开心的开发者。 :)
  • @tobi 答案有帮助吗?接受您认为合适的答案。
  • @tobi 是的。由于前一种情况下的 name 是主键,因此默认情况下强制唯一性。在后一种情况下,每次执行任何 CRUD 操作时,域类的 constraints 都会启动以触发验证。任何一种情况都很好,但是如果您不想使用分配的主键但要求 hibernate 处理 id 并将 name 作为另一个字段,则在后一种情况下您将获得更大的灵活性。 :-)
【解决方案2】:

关于例外情况:

由于地图构造函数而发生异常。对于普通的 groovy 类,这不是问题,但 grails 将域类注册为原型 bean。然后它会覆盖元类中的构造函数以使用 bean 创建和自动装配机制来获取实例。地图构造函数和自动装配中的某些内容导致在地图设置之前创建一个空的颜色 bean。

如果您将代码更改为:

Color c = Color.getColor('red')
Mood m = new Mood()
m.color = c
m.save()

异常应该消失。

您可以考虑为这个特定用例提交JIRA issue,但我不知道grails 团队是否会认为这是一个错误或设计决定。它肯定没有记录在任何地方。

关于设计:

在不了解您的模型的情况下,我同意 dmahapatro 将数据完整性的责任转移到您的数据库和 GORM 约束上。这就是他们的目的。

避免这种情况会导致代码中出现不寻常的使用模式,例如知道使用Color.getColor 而不是正常的域类实例化。

像 Grails 这样的约定优于配置框架背后的理念是尽可能地遵守约定,这样任何熟悉约定的人都可以介入并立即知道发生了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-24
    • 2021-12-13
    • 1970-01-01
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 2019-04-17
    相关资源
    最近更新 更多