Groovy/Grails วิธีทำให้คอนสตรัคเตอร์เป็นส่วนตัว - วิธีที่ถูกต้อง

ฉันมีคลาสโดเมนใน 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();
  }
}

เพื่อให้แน่ใจว่าวัตถุของ Color ถูกสร้างขึ้นโดยใช้วิธี getColor แบบคงที่เท่านั้น ฉันต้องการทำให้ตัวสร้างเป็นแบบส่วนตัว จนถึงขณะนี้ ฉันสามารถสร้างวัตถุที่มีสีได้ แต่เมื่อฉันใช้อินสแตนซ์นี้เพื่อสร้างวัตถุของ 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 จึงจำเป็นต้องเข้าถึงตัวสร้างสี ฉันผ่านวัตถุไปแล้ว .. และโดยทั่วไปแล้ววิธีที่ดีที่สุดใน groovy/grails คืออะไรในการซ่อนตัวสร้างคลาสโดเมนเพื่อควบคุมวิธีการสร้างวัตถุ โดยเฉพาะการใช้งานตัวควบคุมแผนที่ก็ควรปิดการใช้งานด้วยเช่นกัน


person tObi    schedule 16.08.2013    source แหล่งที่มา
comment
ทำไมคุณไม่ใช้ Enums สำหรับเรื่องประเภทนี้?   -  person Nathan Hughes    schedule 16.08.2013
comment
เพราะผู้ใช้ควรจะสามารถเพิ่มวัตถุใหม่แบบไดนามิกได้   -  person tObi    schedule 16.08.2013
comment
ฉันคิดว่าคุณควรพิจารณาถึงการบังคับใช้เอกลักษณ์เฉพาะผ่านข้อจำกัดของฐานข้อมูล   -  person Nathan Hughes    schedule 16.08.2013
comment
ใช่ คิดเกี่ยวกับมันเหมือนกัน และฉันคิดว่ามันจะเป็นทางเลือก .. แต่ฉันก็ยังคิดว่า เนื่องจากเป็นรูปแบบการเขียนโปรแกรมที่แพร่หลายเพื่อต้องการควบคุมเกี่ยวกับการสร้างวัตถุในบางกรณี มันควรจะเป็นไปได้ในแบบ Groovy เช่นกัน   -  person tObi    schedule 16.08.2013
comment
อะไรรั้งคุณไว้ในการทำให้ name เป็นคีย์หลัก และ Color เกี่ยวข้องกับ Mood อย่างไร   -  person dmahapatro    schedule 16.08.2013
comment
ใช่อย่างที่ฉันบอกว่าการทำให้ชื่อมีเอกลักษณ์เฉพาะด้วยข้อจำกัดจะเป็นอีกทางเลือกหนึ่งในกรณีนี้ แต่คำถามนี้มีความหมายกว้างกว่า ฉันจะควบคุมการสร้างวัตถุได้อย่างไร และคุณพูดถูก ลืมความสัมพันธ์ระหว่างสีและอารมณ์ไปเสีย มันมีมากมาย ฉันจะแก้ไขในโพสต์ของฉัน   -  person tObi    schedule 16.08.2013
comment
หากฉันจำไม่ผิด Grails จะแทนที่ Constructor เริ่มต้นสำหรับคลาสโดเมน ดังนั้นฉันคิดว่าการสร้าง Private Constructor ไม่ใช่ความคิดที่ดี คุณสามารถบังคับใช้ความไม่ซ้ำกันได้ด้วยข้อจำกัดของฐานข้อมูล   -  person    schedule 16.08.2013


คำตอบ (2)


เกี่ยวกับข้อยกเว้น:

ข้อยกเว้นเกิดขึ้นเนื่องจากตัวสร้างแผนที่ ด้วยคลาส Groovy ปกติ นี่ไม่ใช่ปัญหา แต่ Grails จะลงทะเบียนคลาสโดเมนเป็นต้นแบบ Bean จากนั้นจะแทนที่ตัวสร้างในเมตาคลาสเพื่อใช้การสร้าง bean และกลไกการเดินสายอัตโนมัติเพื่อรับอินสแตนซ์ บางสิ่งในตัวสร้างแผนที่และการเดินสายอัตโนมัติทำให้มีการสร้างถั่วสีว่างก่อนที่จะตั้งค่าโดยแผนที่

หากคุณเปลี่ยนรหัสเป็น:

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

ข้อยกเว้นควรหายไป

คุณอาจพิจารณายื่นปัญหา JIRA สำหรับกรณีการใช้งานเฉพาะนี้ แต่ฉันไม่รู้ว่าหรือ ไม่ใช่ทีม grails ที่จะถือว่านี่เป็นข้อบกพร่องหรือการตัดสินใจในการออกแบบ มันไม่ได้บันทึกไว้ที่ใดอย่างแน่นอน

เกี่ยวกับการออกแบบ:

ฉันเห็นด้วยกับ dmahapatro เกี่ยวกับการเปลี่ยนความรับผิดชอบด้านความสมบูรณ์ของข้อมูลไปยังฐานข้อมูลและข้อจำกัด GORM ของคุณโดยไม่ทราบอะไรมากนักเกี่ยวกับโมเดลของคุณ นั่นคือสิ่งที่พวกเขาทำเพื่อ

การหลีกเลี่ยงสิ่งนี้ทำให้เกิดรูปแบบการใช้งานที่ผิดปกติในโค้ด เช่น การรู้ว่าจะใช้ Color.getColor ซึ่งตรงข้ามกับการสร้างอินสแตนซ์คลาสโดเมนปกติ

อุดมการณ์เบื้องหลังแบบแผนเหนือกรอบการกำหนดค่าเช่น Grails คือการปฏิบัติตามแบบแผนให้มากที่สุดเท่าที่จะเป็นไปได้ เพื่อให้ใครก็ตามที่คุ้นเคยกับแบบแผนสามารถก้าวเข้ามาและรู้ทันทีว่าเกิดอะไรขึ้น

person codelark    schedule 16.08.2013

การใช้ name เป็นคีย์หลักสำหรับ Color จะทำให้คุณมีเอกลักษณ์เฉพาะตัว นอกจากนี้ findOrSaveBy* ยังสามารถใช้เพื่อแทนที่เมธอดคงที่แบบกำหนดเอง getColor ได้

หากการสร้าง name เป็นคีย์หลักไม่ใช่ตัวเลือกที่ใช้ได้ คุณสามารถเพิ่มข้อจำกัดตามที่บุคคลเกือบทั้งหมดกล่าวไว้ในความคิดเห็นต่อคำถามได้ :)

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()
person dmahapatro    schedule 16.08.2013
comment
ดังนั้นจึงเป็นวิธีที่ดีที่จะทำในกรณีนี้ .. แต่นั่นหมายความว่าคุณใน grails ไม่ได้ทำให้คอนสตรัคเตอร์คลาสโดเมนของคุณเป็นแบบส่วนตัวใช่ไหม - person tObi; 16.08.2013
comment
@tobi ไม่เคยใช้หรือคิดถึงเรื่องนี้เนื่องจากฉันได้รับความยืดหยุ่นในการใช้การแมปและบล็อกข้อ จำกัด - person dmahapatro; 16.08.2013
comment
@SérgioMichels Buddy GORM คือแก้ว Ray Ban มาเป็นเวลานาน ขึ้นอยู่กับนักพัฒนาที่สวมมัน ;) ลองนึกถึง Ratpack สิ แผนคือต้องมี GORM อยู่ในนั้น ฉันคงจะเป็นนักพัฒนาที่มีความสุขที่สุดที่ได้เห็นบางอย่างเช่น GORM ใน Node.js หรือพบบางสิ่งที่คล้ายกันเมื่อฉันใช้งาน :) - person dmahapatro; 16.08.2013
comment
@tobi คำตอบมีประโยชน์หรือไม่? ยอมรับคำตอบใดก็ตามที่คุณรู้สึกว่าเหมาะสมที่จะยอมรับ - person dmahapatro; 22.08.2013
comment
มีความแตกต่างในทางปฏิบัติในกรณีนี้ในการเขียน static mapping = {id name 'name'} และเพียงแค่ทำให้ชื่อไม่ซ้ำกันหรือไม่ เขียนเช่นนั้น: contraints แบบคงที่ = { ชื่อว่าง:false, ไม่ซ้ำกัน:true} - person tObi; 05.09.2013
comment
@โทบิ ใช่ครับ เนื่องจากชื่อในกรณีเดิมคือคีย์หลัก ความเป็นเอกลักษณ์จึงถูกบังคับตามค่าเริ่มต้น ในกรณีภายหลัง ทุกครั้งที่คุณดำเนินการ CRUD ใดๆ constraints สำหรับคลาสโดเมนจะเริ่มต้นขึ้นเพื่อเริ่มการตรวจสอบความถูกต้อง ไม่ว่ากรณีใดก็ดี แต่คุณจะได้รับความยืดหยุ่นมากขึ้นในกรณีหลัง หากคุณไม่ต้องการใช้คีย์หลักที่กำหนด แต่ขอให้ไฮเบอร์เนตดูแล id และมี name เป็นฟิลด์อื่น :-) - person dmahapatro; 05.09.2013
comment
ขอบคุณสำหรับคำตอบ ดังนั้นฉันเดาว่าการทำให้ฟิลด์ชื่อเป็น id จะช่วยเร่งการดำเนินการ CRUD หากฉันมีวัตถุสีจำนวนมาก อีกทางเลือกหนึ่งคือการใช้indexColumn คุณต้องการอะไรสักอย่างไหม? - person tObi; 05.09.2013