คนตัดไม้ควรเป็นแบบคงที่หรือไม่


104

คนตัดไม้ควรประกาศแบบคงที่หรือไม่? โดยปกติฉันเคยเห็นคำประกาศสองประเภทสำหรับคนตัดไม้:

    บันทึกบันทึกที่มีการป้องกัน = Log4JLogger ใหม่ (aClass.class);

หรือ

    บันทึกล็อกแบบคงที่ส่วนตัว = Log4JLogger ใหม่ (aClass.class);

ควรใช้ตัวไหน โปรและคอนของทั้งคู่คืออะไร?


1
การบันทึกเป็นเรื่องที่ต้องคำนึงถึงอย่างมาก ใช้ Aspects และคำถามคือการสงสัย
Dave Jarvis

4
staticคือหนึ่งการอ้างอิงต่อคลาส non-static คือหนึ่งการอ้างอิงต่ออินสแตนซ์ (+ initialization) ดังนั้นในบางกรณีอย่างหลังจะส่งผลกระทบต่อหน่วยความจำอย่างมากหากคุณมีอินสแตนซ์มากมาย อย่าใช้วัตถุไม่คงที่ในวัตถุบ่อยๆ ฉันใช้เวอร์ชันคงที่เสมอ (ซึ่งควรเป็นตัวพิมพ์ใหญ่ LOG )
มี QUIT - Anony-Mousse

2
ตามที่แนะนำไปแล้วให้ใช้ AOP และคำอธิบายประกอบตัวอย่างเช่นjcabi.com/jcabi-aspects/annotation-loggable.html
yegor256

1
RobertHume รุ่นคงมีการใช้อย่างต่อเนื่อง นั่นคือเหตุผลที่ควรเป็นตัวพิมพ์ใหญ่
มี QUIT - Anony-Mousse

2
ไม่ควรprivate static final Log logเป็นตัวพิมพ์เล็ก คนตัดไม้ไม่ใช่ค่าคงที่คนตัดไม้เป็นวัตถุสุดท้ายที่คงที่ (ที่สามารถกลายพันธุ์ได้) loggerส่วนตัวผมมักจะใช้
osundblad

คำตอบ:


99

ข้อดีของรูปแบบไม่คงที่คือคุณสามารถประกาศในคลาสพื้นฐาน (นามธรรม) ดังต่อไปนี้โดยไม่ต้องกังวลว่าจะใช้ชื่อคลาสที่ถูกต้อง:

protected Log log = new Log4JLogger(getClass());

อย่างไรก็ตามข้อเสียคือเห็นได้ชัดว่าอินสแตนซ์คนตัดไม้ใหม่ทั้งหมดจะถูกสร้างขึ้นสำหรับทุกอินสแตนซ์ของคลาส สิ่งนี้อาจไม่แพง แต่จะเพิ่มค่าใช้จ่ายที่สำคัญ หากคุณต้องการหลีกเลี่ยงสิ่งนี้คุณต้องการใช้staticแบบฟอร์มแทน แต่ข้อเสียคือคุณต้องประกาศในแต่ละคลาสและดูแลในทุกคลาสว่ามีการใช้ชื่อคลาสที่ถูกต้องระหว่างการสร้างคนตัดไม้เนื่องจากgetClass()ไม่สามารถใช้ในบริบทคงที่ อย่างไรก็ตามใน IDE โดยเฉลี่ยคุณสามารถสร้างเทมเพลตการเติมข้อความอัตโนมัติสำหรับสิ่งนี้ได้ เช่นlogger+ ctrl+space.

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

protected Log log = LogManager.getLogger(getClass());

6
ประกาศabstract Log getLogger();ในคลาสนามธรรม ใช้วิธีนี้โดยส่งคืนตัวบันทึกแบบคงที่สำหรับอินสแตนซ์เฉพาะ เพิ่มprivate final static Log LOG = LogManager.getLogger(Clazz.class);ในเทมเพลตคลาส IDE ของคุณ
มี QUIT - Anony-Mousse

2
สำหรับ slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt

3
@BalusC ปัญหาในการส่ง getClass () ไปยังเมธอด getLogger คือส่งคืนคลาสของอินสแตนซ์ปัจจุบัน โดยปกติแล้วการบันทึกจะต้องเชื่อมโยงกับคลาสที่มีรหัสมากกว่า ตัวอย่างเช่นถ้ารหัสการบันทึกอยู่ในคลาส Parent เราต้องการให้การบันทึกเชื่อมโยงกับ Parent แม้ว่าอินสแตนซ์การดำเนินการจะเป็นและอินสแตนซ์ของคลาส Child ซึ่งเป็นคลาสย่อยของ Parent ด้วย getClass () ก็จะมีการเชื่อมโยงกับเด็กไม่ถูกต้อง
Inor

@inor: "ไม่ถูกต้อง"? หากคุณไม่ต้องการแยกชั้นเรียนคุณไม่ควรใช้ getClass () ที่สืบทอดมาตั้งแต่แรก มีนักพัฒนาที่พบว่ามันถูกต้องและมีประโยชน์เนื่องจากเปิดเผยข้อมูลในคลาสย่อยที่ตรรกะถูกดำเนินการ
BalusC

2
@ BalusC getLogger (getClass ()) ส่งผลให้บันทึกชื่อของคลาสย่อยไม่ถูกต้องเสมอ คลาสการบันทึกควรทำ getLogger (Clazz.class) เสมอเพื่อเชื่อมโยงการบันทึกโดยรหัสในคลาส Clazz นักพัฒนาที่ต้องการทราบว่าคลาสย่อยใดกำลังดำเนินการอยู่ (เช่น SubClazz ขยาย Clazz) ควรทำใน SubClazz: getLogger (SubClazz.class) และสิ่งที่ชอบ: log.info ("การเรียก <something in my base class>");
Inor

45

ฉันเคยคิดว่าคนตัดไม้ทุกคนควรเป็นแบบคงที่ อย่างไรก็ตามบทความนี้ใน wiki.apache.orgนำเสนอข้อกังวลเกี่ยวกับหน่วยความจำที่สำคัญบางประการเกี่ยวกับการรั่วไหลของ classloader การประกาศตัวบันทึกเป็นแบบคงที่จะป้องกันไม่ให้คลาสที่ประกาศ (และตัวโหลดคลาสที่เกี่ยวข้อง) เป็นขยะที่รวบรวมในคอนเทนเนอร์ J2EE ที่ใช้คลาสโหลดเดอร์แบบแบ่งใช้ ซึ่งจะส่งผลให้เกิดข้อผิดพลาด PermGen หากคุณปรับใช้แอปพลิเคชันของคุณใหม่หลายครั้งเพียงพอ

ฉันไม่เห็นวิธีใดในการแก้ไขปัญหาการรั่วไหลของคลาสตัวโหลดนี้นอกจากการประกาศว่าตัวบันทึกเป็นแบบไม่คงที่


4
ฉันสงสัยว่าฟิลด์คงที่จะมีปัญหาหน่วยความจำรั่วเช่นกัน ไม่คงที่อาจมีปัญหาด้านประสิทธิภาพตามที่คนอื่นกล่าว วิธีที่ดีที่สุดคืออะไร?
เลียง

@piepera ปัญหาหลักที่อธิบายไว้ในบทความที่คุณอ้างถึงคือความสามารถในการควบคุมระดับการบันทึกในแต่ละแอปพลิเคชันเมื่อ "พิจารณากรณีที่มีการปรับใช้คลาสที่ใช้ "แอปพลิเคชัน" อิสระ ฉันไม่เห็นว่าเป็นปัญหาเพราะในสถานการณ์เฉพาะนี้แอปพลิเคชันมี "พื้นดินทั่วไป" และใน "พื้นทั่วไป" ระดับการบันทึกสำหรับชั้นเรียนจะถูกตัดสินและใช่มันถือสำหรับแอปพลิเคชันทั้งหมด ... ในใจว่าชั้นนี้คือ [โหลด] นอกของโปรแกรมเหล่านี้
Inor

18

ความแตกต่างที่สำคัญที่สุดคือส่งผลต่อไฟล์บันทึกของคุณอย่างไร: บันทึกอยู่ในหมวดหมู่ใด

  • ในตัวเลือกแรกของคุณบันทึกของคลาสย่อยจะอยู่ในหมวดหมู่ของซูเปอร์คลาส ดูเหมือนจะสวนทางกับฉันมาก
  • กรณีแรกของคุณมีหลายรูปแบบ:

    บันทึกบันทึกที่มีการป้องกัน = Log4JLogger ใหม่ (getClass ());

    ในกรณีนี้หมวดหมู่บันทึกของคุณจะบอกว่าอ็อบเจ็กต์ใดที่โค้ดที่บันทึกทำงานอยู่

  • ในตัวเลือกที่สองของคุณ (สแตติกส่วนตัว) ประเภทบันทึกคือคลาสที่มีรหัสการบันทึก ดังนั้นโดยปกติแล้วชั้นเรียนที่กำลังทำสิ่งที่กำลังบันทึกอยู่

ฉันขอแนะนำตัวเลือกสุดท้ายนั้นเป็นอย่างยิ่ง มีข้อดีเหล่านี้เมื่อเทียบกับโซลูชันอื่น ๆ :

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

นอกจากนี้ยังมีข้อเสีย:

  • จะต้องมีการประกาศในทุกคลาสที่คุณบันทึกข้อความ (ไม่มีการนำตัวบันทึกระดับสูงมาใช้ซ้ำ)
  • คุณต้องระมัดระวังในการใส่ชื่อคลาสที่ถูกต้องเมื่อเริ่มต้นตัวบันทึก (แต่ IDE ที่ดีจะดูแลคุณ)

4

ใช้การผกผันของการควบคุมและส่งตัวบันทึกไปยังตัวสร้าง หากคุณสร้างคนตัดไม้ในชั้นเรียนคุณจะต้องเสียเวลากับการทดสอบหน่วยของคุณ คุณกำลังเขียนแบบทดสอบหน่วยไม่ใช่เหรอ?


5
การทดสอบหน่วยซึ่งตรวจสอบการบันทึกที่คุณให้เสียงทั้งที่ไร้ประโยชน์และเปราะอย่างไม่น่าเชื่อ
Michael

1
ประโยชน์ขึ้นอยู่กับระบบที่ทดสอบ บางครั้งการบันทึกเป็นสิ่งที่คุณสามารถเข้าถึงได้
Wayne Allen

@ เวย์นอัลเลนเมื่อคุณทำแบบทดสอบหน่วยตามความหมายคุณจะมีผลการทดสอบด้วย คุณกำลังแนะนำสถานการณ์ที่หน่วยทดสอบ แต่ไม่มีผลการทดสอบหรือไม่? มีเพียงบันทึก?
Inor

การสร้างคนตัดไม้ภายในชั้นเรียนไม่ได้สร้างปัญหา คุณสามารถแสดงตัวอย่างง่ายๆที่ยากต่อ UT เนื่องจากคลาสสร้างคนตัดไม้ของตัวเองได้หรือไม่?
Inor

1
แน่นอน แล้วคนตัดไม้ที่ส่งอีเมลล่ะ ไม่อยากทำแบบนั้นทุกครั้งที่เรียกใช้การทดสอบ รวมทั้งคุณยืนยันผลข้างเคียงอย่างไร?
Wayne Allen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.