การมีชั้นเรียน 'Util' เป็นสาเหตุของความกังวลหรือไม่ [ปิด]


14

บางครั้งฉันสร้างคลาส 'Util' ซึ่งส่วนใหญ่ใช้เพื่อเก็บวิธีการและค่านิยมที่ไม่ได้อยู่ในที่อื่น แต่ทุกครั้งที่ฉันสร้างหนึ่งในชั้นเรียนเหล่านี้ฉันคิดว่า "เอ่อ - โอ้ฉันจะเสียใจในภายหลัง ... " เพราะฉันอ่านที่ไหนสักแห่งที่มันไม่ดี

แต่ในทางกลับกันดูเหมือนจะมีสองกรณี (อย่างน้อยสำหรับฉัน) ที่น่าสนใจสำหรับพวกเขา:

  1. ความลับการนำไปใช้งานที่ใช้ในหลายคลาสภายในแพ็กเกจ
  2. นำเสนอฟังก์ชั่นที่มีประโยชน์เพื่อเพิ่มคลาสโดยไม่ทำให้หน้าจอของคุณยุ่ง

ฉันกำลังจะไปสู่ความพินาศหรือไม่? คุณพูดอะไร !! ฉันควร refactor หรือไม่


11
หยุดใช้Utilในชื่อคลาสของคุณ แก้ไขปัญหา.
yannis

3
@ MattFenwick Yannis มีจุดดี การตั้งชื่อชั้นเรียนSomethingUtilเป็นบิตขี้เกียจและเพียงแค่ปิดกั้นจุดประสงค์ที่แท้จริงของการเรียน - เดียวกันกับชั้นเรียนชื่อหรือSomethingManager SomethingServiceถ้าชั้นเรียนนั้นมีความรับผิดชอบเดียวมันควรจะง่ายที่จะตั้งชื่อที่มีความหมาย ถ้าไม่ใช่นั่นเป็นปัญหาที่แท้จริงในการจัดการกับ ...
MattDavey

1
@Mavey จุดดีมันอาจเป็นทางเลือกคำที่ไม่ดีที่จะใช้Utilแม้ว่าเห็นได้ชัดว่าฉันไม่ได้คาดหวังว่าจะได้รับการแก้ไขและคำถามที่เหลือไม่สนใจ ...

1
หากคุณสร้างคลาส * Util หลายคลาสคั่นด้วยวัตถุประเภทใดที่พวกเขาจัดการฉันคิดว่ามันใช้ได้ ฉันไม่เห็นปัญหาใด ๆ กับการมี StringUtil, ListUtil ฯลฯ ยกเว้นว่าคุณอยู่ใน C # คุณควรใช้วิธีการขยาย
marco-fiset

4
ฉันมักจะมีชุดของฟังก์ชั่นที่ใช้สำหรับวัตถุประสงค์ที่เกี่ยวข้อง พวกเขาไม่ได้แมปกับคลาสได้ดีจริงๆ ฉันไม่ต้องการคลาส ImageResizer ฉันแค่ต้องการฟังก์ชั่น ResizeImage ดังนั้นฉันจะใส่ฟังก์ชั่นที่เกี่ยวข้องลงในคลาสแบบคงที่เช่น ImageTools สำหรับฟังก์ชั่นที่ไม่ได้อยู่ในการจัดกลุ่มใด ๆ ฉันมี Util static class เพื่อเก็บไว้ แต่เมื่อฉันมีจำนวนน้อยที่เกี่ยวข้องกันพอที่จะใส่ในชั้นเรียนหนึ่งฉันจะย้ายพวกเขา ฉันไม่เห็นวิธีที่ดีกว่านี้ในการจัดการกับ OO
ฟิลิป

คำตอบ:


26

การออกแบบ OO สมัยใหม่ยอมรับว่าไม่ใช่ทุกสิ่งที่เป็นวัตถุ บางสิ่งเป็นพฤติกรรมหรือสูตรและบางอย่างไม่มีสถานะ เป็นการดีที่จะจำลองสิ่งเหล่านี้เป็นฟังก์ชั่นที่บริสุทธิ์เพื่อรับประโยชน์จากการออกแบบนั้น

Java และ C # (และอื่น ๆ ) ต้องการให้คุณสร้างคลาส util และกระโดดผ่านห่วงนั้นเพื่อทำ น่ารำคาญ แต่ไม่ใช่วันสิ้นโลก และไม่ลำบากจากมุมมองการออกแบบจริงๆ


ใช่นั่นเป็นสิ่งที่ฉันคิด ขอบคุณสำหรับคำตอบ!

ตกลงแม้ว่ามันควรจะกล่าวถึงว่า. NET ขณะนี้มีวิธีการขยายและชั้นเรียนบางส่วนเป็นทางเลือกแทนวิธีการนี้ หากคุณนำไอเท็มนี้ไปใช้อย่างสุดขีดคุณจะต้องจบลงด้วย Ruby mixins ซึ่งเป็นภาษาที่มีความต้องการเล็กน้อยสำหรับคลาสยูทิลิตี้
neontapir

2
@neontapir - ยกเว้นการดำเนินการตามวิธีการขยายพื้นบังคับให้คุณทำ util ชั้นด้วยวิธีการขยาย ...
Telastyn

จริง มีสองสถานที่ที่ Util ชั้นเรียนขัดขวางหนึ่งอยู่ในชั้นเรียนของตัวเองและอื่น ๆ ที่เป็น callsite ด้วยการทำให้วิธีการดูเหมือนว่าพวกเขามีอยู่ในวัตถุที่ปรับปรุงวิธีการขยายที่อยู่ปัญหา callsite ฉันเห็นด้วยยังคงมีปัญหาของการมีอยู่ในชั้นเรียนของ Util
neontapir

16

ไม่เคยพูดว่า "ไม่เคย"

ฉันไม่คิดว่ามันจะไม่ดี แต่ถ้าคุณทำมันไม่ดีและเป็นการละเมิด

เราทุกคนต้องการเครื่องมือและยูทิลิตี้

สำหรับผู้เริ่มต้นเราทุกคนใช้ห้องสมุดบางอย่างซึ่งบางครั้งก็ถือว่าเป็นที่แพร่หลายและต้องห้าม ตัวอย่างเช่นในโลก Java, Google GuavaหรือApache Commonsบางส่วน( Apache Commons Lang , Apache Commons Collections , ฯลฯ ... )

ดังนั้นจึงมีความจำเป็นอย่างชัดเจนสำหรับสิ่งเหล่านี้

หลีกเลี่ยง Hard-Word, การทำซ้ำและการแนะนำบั๊ก

หากคุณคิดว่าเกี่ยวกับเหล่านี้จะสวยมากเพียงพวงใหญ่มากของเหล่านี้Utilเรียนคุณอธิบายยกเว้นคนที่เดินผ่านช่วงที่ดีที่จะได้รับพวกเขา (ค่อนข้าง) ขวาและพวกเขาได้รับเวลา - ผ่านการทดสอบอย่างหนักและตา balled โดยคนอื่น

ดังนั้นฉันจะบอกว่ากฎข้อแรกของหัวแม่มือเมื่อรู้สึกว่าคันที่จะเขียนUtilชั้นเรียนคือการตรวจสอบว่าUtilชั้นเรียนไม่ได้มีอยู่จริง

ข้อโต้แย้งเดียวที่ฉันเคยเห็นคือเมื่อคุณต้องการ จำกัด การพึ่งพาของคุณเพราะ:

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

แต่ทั้งสองแบบนี้สามารถ tackled โดยใหม่บรรจุภัณฑ์ lib โดยใช้ProGuardหรือเทียบเท่าหรือการทำให้มันแตกต่างด้วยตัวคุณเอง (สำหรับMavenผู้ใช้ที่Maven สีปลั๊กอินข้อเสนอบางรูปแบบการกรองเพื่อบูรณาการนี้เป็นส่วนหนึ่งของการสร้างของคุณ)

ดังนั้นถ้ามันอยู่ใน lib และตรงกับกรณีการใช้งานของคุณและไม่มีเกณฑ์มาตรฐานบอกให้คุณใช้มัน หากมันแตกต่างจากสิ่งที่คุณเล็กน้อยให้ขยาย (ถ้าเป็นไปได้) หรือขยายมันหรือในทางเลือกสุดท้ายเขียนใหม่

อนุสัญญาการตั้งชื่อ

อย่างไรก็ตามในคำตอบนี้ฉันโทรหาพวกเขาUtilเหมือนคุณ อย่าตั้งชื่อพวกเขาว่า

ตั้งชื่อที่มีความหมาย นำ Google Guava มาเป็นตัวอย่างที่ดี (อย่างมาก) ว่าควรทำอย่างไรและเพียงจินตนาการว่าcom.google.guavaเนมสเปซนั้นเป็นutilรากของคุณ

โทรหาแพ็คเกจของคุณutilที่แย่ที่สุด แต่ไม่ใช่ชั้นเรียน หากข้อตกลงกับStringวัตถุและการจัดการของสตริงสร้างเรียกมันว่าStringsไม่StringUtils(ขอโทษApache Commons Lang - ฉันยังคงชอบและใช้คุณ!) ถ้ามันทำสิ่งที่เฉพาะเจาะจงเลือกชื่อคลาสที่เฉพาะเจาะจง (เช่นSplitterหรือJoiner)

หน่วยทดสอบ

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

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

การออกแบบ API

การตัดสินใจออกแบบของคุณจะใช้กับ API เหล่านี้จะติดตามคุณไปอีกนาน ดังนั้นในขณะที่ไม่ใช้เวลาเขียนข้อความSplitter-clone ให้ระมัดระวังเกี่ยวกับวิธีการแก้ไขปัญหาของคุณ

ถามตัวเองคำถามสองสามข้อ:

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

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

ตามปกติเรียนรู้จากยักษ์ใหญ่ที่นี่:

ใช่สิ่งเหล่านี้จำนวนมากให้ความสำคัญกับคอลเลกชันและโครงสร้างข้อมูล แต่อย่าบอกฉันว่าไม่ว่าที่ไหนหรืออะไรที่คุณมักจะใช้ประโยชน์ส่วนใหญ่ของคุณไม่ว่าทางตรงหรือทางอ้อม


+1 สำหรับIf deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
Tulains Córdova


ทำไมคุณถึงเรียกมันว่า Strings แทนที่จะเป็น StringUtil เงื่อนไขสำหรับฉันฟังดูเหมือนวัตถุรวบรวม
bdrx

@bdrx: สำหรับฉันแล้ววัตถุคอลเลกชันอาจเป็นได้StringsCollectionTypeHereหากคุณต้องการนำไปปฏิบัติอย่างเป็นรูปธรรม หรือชื่อที่เฉพาะเจาะจงยิ่งขึ้นถ้าสตริงเหล่านี้มีความหมายเฉพาะในบริบทของแอปของคุณ สำหรับกรณีนี้ Guava ใช้Stringsสำหรับผู้ช่วยที่เกี่ยวข้องกับสตริงซึ่งต่างจากStringUtilsCommons Lang ฉันคิดว่ามันเป็นที่ยอมรับอย่างสมบูรณ์มันหมายถึงว่าชั้นเรียนจัดการStringวัตถุหรือเป็นชั้นเรียนอเนกประสงค์ในการจัดการ Strings
haylem

@bdrx: โดยทั่วไปNameUtilsสิ่งที่ฉันไม่สิ้นสุดเพราะถ้ามันอยู่ภายใต้ชื่อแพคเกจที่มีข้อความชัดเจนฉันจะรู้แล้วว่ามันเป็นคลาสยูทิลิตี้ มันเป็นเรื่องที่น่ารำคาญให้ฉันเป็นคนประกาศสิ่งที่เป็นSomethingInterface, หรือISomething SomethingImplฉันค่อนข้างโอเคกับสิ่งของเช่นเมื่อเขียนโค้ดใน C และไม่ใช้ IDE วันนี้ฉันไม่ต้องการสิ่งเหล่านี้โดยทั่วไป
haylem

8

คลาสที่ใช้ประโยชน์ไม่ได้มีความเหนียวแน่นและโดยทั่วไปแล้วการออกแบบที่ไม่ดีเนื่องจากคลาสต้องมีเหตุผลเดียวในการเปลี่ยนแปลง (Single Responsability Principle)

แต่ผมเคยเห็น "util" เรียนในมาก Java API ชอบMath, และCollectionsArrays

อันที่จริงแล้วคลาสยูทิลิตี้ แต่วิธีการทั้งหมดของพวกเขาเกี่ยวข้องกับธีมเดียวหนึ่งมีการดำเนินการทางคณิตศาสตร์หนึ่งมีวิธีการจัดการคอลเลกชันและอื่น ๆ สำหรับการจัดการอาร์เรย์

ลองไม่มีวิธีการที่ไม่เกี่ยวข้องทั้งหมดในคลาสยูทิลิตี้ หากเป็นเช่นนั้นโอกาสที่คุณสามารถนำไปไว้ในที่อื่น ๆ ได้

หากต้องมีการเรียน util แล้วลองพวกเขาจะถูกแยกจากกันโดยรูปแบบเหมือนของ Java Math, และCollections Arraysอย่างน้อยมันก็แสดงให้เห็นถึงความตั้งใจในการออกแบบแม้ว่าพวกเขาจะเป็นเพียงเนมสเปซก็ตาม

ฉันหนึ่งหลีกเลี่ยงคลาสยูทิลิตี้เสมอและไม่จำเป็นต้องสร้างเลย


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

ไม่ใช่ทุกอย่างที่อยู่ในชั้นเรียน หากคุณติดอยู่กับภาษาที่ยืนยันว่ามันจะมีการเรียนการสอนเกิดขึ้น
jk

1
คลาสยูทิลิตี้ไม่มีหลักการออกแบบเพื่อให้คุณรู้ว่ามีวิธีการมากเกินไปเนื่องจากไม่มีการทำงานร่วมกันในการเริ่มต้น คุณจะรู้วิธีหยุดได้อย่างไร หลังจาก 30 วิธี? 40? 100 หลักการ OOP ในทางกลับกันจะช่วยให้คุณรู้ว่าเมื่อใดที่วิธีการไม่ได้อยู่ในชั้นเรียนเฉพาะและเมื่อชั้นเรียนทำมากเกินไป นั่นเรียกว่าการติดต่อกัน แต่อย่างที่ฉันบอกคุณในคำตอบของฉันคนที่ออกแบบ Java ใช้คลาสยูทิลิตี้ คุณทำมันไม่ได้เหมือนกัน ฉันไม่ได้สร้างคลาสยูทิลิตี้และไม่เคยอยู่ในสถานการณ์ที่บางสิ่งไม่อยู่ในชั้นเรียนปกติ
Tulains Córdova

1
คลาส Utils ไม่ใช่คลาสที่เป็นเพียงเนมสเปซที่มีฟังก์ชั่นล้วนๆ ฟังก์ชั่นที่บริสุทธิ์มีความเหนียวแน่นเท่าที่คุณจะหาได้
jk

1
@jk ฉันหมายถึง Utils ... โดยวิธีการหาชื่อเช่นMathหรือArraysแสดงอย่างน้อยก็ตั้งใจออกแบบ
Tulains Córdova

3

เป็นที่ยอมรับได้อย่างสมบูรณ์แบบที่จะมีคลาสutilแต่ฉันชอบคำClassNameHelperนี้ .NET BCL ยังมีคลาสผู้ช่วยอยู่ด้วย สิ่งที่สำคัญที่สุดที่ต้องจำไว้ก็คือจัดทำเอกสารวัตถุประสงค์ของชั้นเรียนอย่างละเอียดรวมถึงวิธีการช่วยเหลือของแต่ละบุคคลและเพื่อให้ได้รหัสที่สามารถรักษาคุณภาพได้

และอย่าไปเรียนกับผู้ช่วย


0

ฉันใช้แนวทางสองระดับ คลาส "Globals" ในแพ็คเกจ "util" (โฟลเดอร์) สำหรับบางสิ่งที่จะเข้าสู่แพ็คเกจ "Globals" หรือ "util" มันจะต้อง:

  1. ที่เรียบง่าย
  2. ไม่มีการเปลี่ยนแปลง
  3. ไม่ขึ้นอยู่กับสิ่งใดในแอปพลิเคชันของคุณ

ตัวอย่างที่ผ่านการทดสอบเหล่านี้:

  • รูปแบบวันที่และระบบทั้งทศนิยม
  • ข้อมูลโกลบอลที่ไม่สามารถเปลี่ยนแปลงได้เช่น EARLIEST_YEAR (ที่ระบบรองรับ) หรือ IS_TEST_MODE หรือค่าระดับโลกที่แท้จริงและไม่เปลี่ยนแปลง (ในขณะที่ระบบกำลังทำงาน)
  • วิธีการช่วยเหลือขนาดเล็กมากที่ใช้แก้ไขช่องว่างในภาษากรอบงานหรือชุดเครื่องมือของคุณ

นี่คือตัวอย่างของวิธีการช่วยเหลือที่เล็กมากโดยไม่ขึ้นอยู่กับแอพพลิเคชั่นที่เหลือ:

public static String ordinal(final int i) {
    return (i == 1) ? "1st" :
           (i == 2) ? "2nd" :
           (i == 3) ? "3rd" :
           new StringBuilder().append(i).append("th").toString();
}

เมื่อมองดูสิ่งนี้ฉันจะเห็นข้อผิดพลาดที่ 21 ควรเป็น "21", 22 ควรเป็น "22" เป็นต้น แต่นั่นอยู่ด้านข้าง

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

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

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