มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะเก็บค่าบางอย่างเป็นสตริง


19

มันเป็นชื่อที่คลุมเครือมาก แต่ฉันก็นึกไม่ออกว่าจะพูดอะไรดี แต่เพียงแค่เป็นตัวอย่างที่คิดเกี่ยวกับทิศทางที่ตัวละครในเกมที่มีการเคลื่อนไหวใน. if(character.direction == "left")มันก็รู้สึกชนิดของความผิดที่จะต้องใช้สตริงแล้วทำสิ่งที่ชอบ ดูเหมือนว่าผมว่ามันออกจากห้องมากเกินไปสำหรับข้อผิดพลาดโง่เหมือนตั้งใจใช้Leftหรือหรือสิ่งแทนl leftความสงสัยของฉันถูกต้องหรือไม่? ถ้าเป็นเช่นนั้นแล้ววิธีที่ต้องการในการบรรลุสิ่งนี้คืออะไร?


10
คุณหมายถึงนอกจาก enums ถ้าภาษาของคุณสนับสนุนเหล่านั้น?
hoffmale

12
คุณหมายถึงStringly Typedหรือเปล่า
RubberDuck


bashบางภาษาไม่สามารถที่จะเก็บค่าอื่นที่ไม่ใช่เป็นสตริงเช่น
mouviciel

ไม่ทราบว่าทำไม แต่ฉันจำ "define" ใน C. คุณสามารถทำสิ่งต่าง ๆ เช่น: #define false true
linuxunil

คำตอบ:


28

หากภาษาที่คุณใช้รองรับการใช้ enums ฉันจะใช้มัน ช่วยให้คุณสามารถ จำกัด จำนวนของตัวเลือกสำหรับประเภทที่กำหนด เช่นใน Java:

public enum Direction {
    LEFT,
    RIGHT,
    UP,
    DOWN
}

2
คำตอบนี้ไม่ได้มีส่วนช่วยให้เข้าใจปัญหา OP ฉันไม่เห็นเหตุผลที่นี่มีเพียงความคิดเห็นที่ จำกัด ในขอบเขตของภาษาที่มีคลาสenumอย่าง java หลายภาษา (เช่น VBA) มี enumแต่อาจไม่ใช่ตัวเลือกที่ดีที่สุดเนื่องจากไม่มีการพิสูจน์อักษรในอนาคตเหมือนกัน ดูคำตอบของฉันสำหรับการอภิปรายว่าทำไมenumคนเดียวถึงไม่สมบูรณ์มักจะเปราะและโซลูชันภาษาเฉพาะ
sqykly

12

มันเป็นการปฏิบัติที่แย่มากที่จะใช้สตริงตัวอักษร (หรือตัวเลขเวทย์มนตร์) ในโค้ด

Enums เป็นสิ่งที่ดีหรืออย่างน้อยก็ใช้ค่าคงที่ (แน่นอนว่า enums เป็นเพียงประเภทของ wrapper สำหรับค่าคงที่ที่เกี่ยวข้องอย่างน้อยหนึ่งค่า)

const string LEFT = "left"
if (someThing == LEFT) { doStuff(); }

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

นี่คือการอ้างอิงที่รวดเร็วหนึ่งรายการฉันแน่ใจว่ามีอีกหลายร้อยรายการ: /programming/47882/what-is-a-magic-number-and-why-is-it-bad


1
ปัญหาที่ฉันมีด้วยวิธีนี้คือคุณได้รับข้อผิดพลาดที่พิมพ์เช่น --const string LEFT = "letf" - ในขณะที่ถ้าคุณทำงานกับ enums ที่โดนจับได้ในเวลารวบรวม
Pieter B

@PieterB - ฉันคิดว่าคำถาม OPs นั้นกว้างพอและตัวอย่างสำหรับ "ซ้าย" เป็นเพียงตัวอย่างโดยพลการ - กรณีทั้งหมดไม่ตรงกับ enum ฉันยอมรับว่าสำหรับชุดของทิศทางโดยเฉพาะ Enum เป็นตัวเลือกที่ดีที่สุด แต่คำตอบของฉันควรจะเป็นคำสามัญใน "ถ้าคุณใส่มันลงในตัวอักษรอย่างน้อยก็ทำให้มันคงที่" (แน่นอนแน่นอน เป็นประเภทของการคงที่)
jleach

3
ฉันมีความสุขที่ได้ทำงานกับแอปพลิเคชันที่กำหนดวันของวันหยุดสุดสัปดาห์เป็นวันที่ชื่อเริ่มต้นด้วย "s" แปลกใจมากที่พวกเขาเกี่ยวกับ "ความจริง" ว่าเมื่อพวกเขาเป็นสากลใบสมัครที่ฝรั่งเศสมีเพียงวันหยุดสุดสัปดาห์หนึ่งวัน
Pieter B

4
เรียกว่า microoptimization แต่บางภาษาอาจตีความว่าเป็นการเปรียบเทียบค่าและเสียเวลาในการตรวจสอบอักขระแต่ละตัวในสตริง มีโอกาสมากขึ้นถ้าคุณทำตามที่ผู้ถามทำและเปรียบเทียบกับสตริง hardcoded "left" หากไม่จำเป็นต้องให้ค่าคงที่เป็นสตริง (นั่นคือคุณไม่ได้พิมพ์) มันจะเป็นการดีกว่าถ้าใช้ const int แทน
Devsman

4

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

คำตอบยาว: ภาษาส่วนใหญ่เสนอประเภทที่ใกล้กับโดเมนของปัญหาของคุณและแม้ว่าพวกเขาจะไม่พวกเขาอาจมีวิธีที่คุณสามารถกำหนดleftเป็นเอนทิตีที่แตกต่างจากright(ฯลฯ ฯลฯ ) เปลี่ยนเป็นสตริงโดยใช้"left"เป็นเพียงการปรับภาษาและการทำงานที่เป็นประโยชน์ของ IDE เช่นการตรวจสอบข้อผิดพลาด แม้ว่าคุณจะต้องใช้

left = "left";

หรือสิ่งที่เทียบเท่ามันมีข้อดีที่จะใช้สตริงเมื่อคุณอ้างถึงมันในรหัสของคุณ ตัวอย่างเช่น,

if (player.direction == Left)

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

นอกจากนี้การมีแนวคิดleftว่าเป็นเอนทิตีที่อ้างอิงได้จะช่วยให้คุณปรับทิศทางใดก็ตามที่คุณตัดสินใจว่าจะไปกับประเภททิศทางนั้น โดยใช้ทิศทางมีความมุ่งมั่นที่จะเป็น"left" หากคุณพบหรือสร้างสิ่งที่เป็นนามธรรมที่ดีกว่าสำหรับชนิดที่คุณต้องไปล่าสัตว์ผ่านร่างกายรหัสทั้งหมดของคุณเปลี่ยนอินสแตนซ์ของทุกString "left"ค่าคงที่หรือตัวแปรจะไม่ต้องการการเปลี่ยนแปลงใด ๆ หากอธิบายรายละเอียด

ประเภทที่คุณต้องการนั้นขึ้นอยู่กับโดเมนของคุณ การวางแผนรหัสของคุณจะทำอย่างไรกับของคุณleft? บางทีคุณอาจต้องการสะท้อนแกน x ของพื้นผิวหรือสไปรต์ที่หันไปทางซ้ายหรือเคลื่อนที่ไปข้างหน้า ถ้าเป็นเช่นนั้นคุณอาจต้องการสร้างleftวัตถุที่มีคุณสมบัติหรือวิธีการที่สะท้อนถึงพฤติกรรมนั้นโดยที่rightวัตถุของคุณมีผลลัพธ์ที่ไม่ทำมิเรอร์ตรงกันข้าม คุณสามารถทำตรรกะนั้นในวัตถุที่แสดงถึงสไปรต์หรือพื้นผิวซึ่งในกรณีนี้คุณจะต้องใช้"left"แทนleftเหตุผลที่ระบุข้างต้น

ใน Java (และอาจเป็นภาษาอื่นที่ฉันยังไม่รู้) enumเป็นทางเลือกที่ดีเพราะใน Java enumมีประโยชน์เช่นเดียวfinal classกับชุดจำนวน จำกัด คุณสามารถกำหนดลักษณะการทำงานได้หากต้องการและคุณสามารถสลับไปใช้คลาสแบบเปิดโดยไม่ต้องเพิ่มความยุ่งยากใด ๆ นอกเหนือจากไฟล์ที่ประกาศenumแต่หลายภาษามองenumว่าเป็นประเภทดั้งเดิมหรือต้องการใช้ไวยากรณ์พิเศษเพื่อใช้งาน สิ่งนั้นรั่วenumไปที่รหัสที่ไม่มีธุรกิจและอาจจำเป็นต้องแก้ไขในภายหลัง ตรวจสอบให้แน่ใจว่าคุณเข้าใจแนวคิดของภาษาenumก่อนที่จะพิจารณาตัวเลือก


2

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

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

ตามที่แนะนำให้ใช้การแจกแจงหากภาษาของคุณอนุญาต ในตัวอย่างเฉพาะของคุณมันเป็นทางเลือกที่ดีกว่าอย่างชัดเจน


ความเร็วและหน่วยความจำไม่น่าจะมีความสำคัญ ที่กล่าวว่าชอบ enums หรือค่าคงที่ เงื่อนไขจะมีความหมายมากกว่าถ้าข้อมูลมาจากรูปแบบข้อความเช่น XML แต่ก็สอดคล้องกับตัวพิมพ์ใหญ่ทั้งหมดหรือ uncaps ทั้งหมด หรือแปลงทุกครั้ง e กรัม if (uppercase(foo) == "LEFT")
user949300

2
@ user949300 การแปลงสตริงเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กทั้งหมดไม่น่าเชื่อถือเช่นกัน มีความเป็นไปได้ที่บางคนอาจตัดสินใจที่จะเยี่ยมชมสถานที่อื่น (หรือขยายธุรกิจที่นั่น) เช่นตุรกีและทำลายการเปรียบเทียบสตริงที่ไร้เดียงสา (หรือปลอกที่ต่ำกว่า)
8bittree

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

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

1

เพียงใช้ประเภทข้อมูลที่เหมาะสมกับสถานการณ์ของคุณ

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

ปัญหาที่แท้จริงของโค้ดของคุณอยู่ที่การทำซ้ำค่า

อย่าทำสิ่งนี้:

if (direction == 'left') {
  goLeft();
}

หากคุณพิมพ์ผิดในเงื่อนไขอย่างใดอย่างหนึ่งเหล่านี้หรือการใช้ "ซ้าย" ในที่อื่นคุณอาจไม่สามารถค้นหารหัสฐานกว้างได้อย่างมีประสิทธิภาพเพราะคุณพิมพ์ผิด!

ให้ใช้รหัสแทนการแจงนับหรือค่าคงที่ - ขึ้นอยู่กับชนิดของข้อมูลที่คุณต้องการในภาษาที่คุณใช้

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

const string LEFT = "left";

if (direction == LEFT) {
  goLeft();
}

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


1

มันเป็นการปฏิบัติที่ไม่ดี?

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

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

  • enumประเภทจะดีกว่าถ้าเขียนโปรแกรมภาษาของคุณสนับสนุนนี้
  • มิฉะนั้นค่าคงที่จำนวนเต็มชื่อจะเป็นวิธีที่จะไปด้วยคำจำกัดความเดียวสำหรับค่าคงที่

แต่คำตอบอาจแตกต่างกันหากชุดของค่าสามารถแก้ไขได้แบบไดนามิกหรือต้องการที่จะพัฒนาเมื่อเวลาผ่านไป ในภาษาส่วนใหญ่การเปลี่ยนenumประเภท (เช่นเพื่อเพิ่มหรือลบค่า) ต้องมีการคอมไพล์ใหม่โดยสมบูรณ์ (ตัวอย่างเช่นการลบค่า enum ใน Java จะหยุดความเข้ากันได้ของไบนารี) และเช่นเดียวกับค่าคงที่จำนวนเต็ม

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


หากคุณกำลังใช้งานใน Java character.direction == "left"การปฏิบัติที่ไม่ถูกต้องด้วยเหตุผลอื่น คุณไม่ควรใช้==เพื่อเปรียบเทียบสตริงเนื่องจากเป็นการทดสอบข้อมูลเฉพาะตัวของวัตถุมากกว่าความเท่าเทียมกันของสตริง (มันจะทำงานถ้าคุณสามารถรับประกันได้ว่าอินสแตนซ์ทั้งหมดของ"left"สตริงได้รับการฝึกงาน แต่ที่ต้องมีการวิเคราะห์ทั้งหมดของแอพลิเคชัน)


1
สิ่งที่ดูเหมือนว่าคงที่ตลอดเวลามักจะกลายเป็นว่าไม่ได้รับการแก้ไขเลย ยกตัวอย่างเช่นสี่ทิศทางได้กลายเป็นแปด
Jimmy T.

@JimmyT - ขึ้นอยู่กับบริบท ตัวอย่างเช่นในเกมการเปลี่ยนจำนวนของทิศทางที่ตัวละครสามารถย้ายจาก 4 เป็น 8 จะทำให้ยกเครื่องสมบูรณ์ของกฎของเกมและรหัสที่ใช้กฎ การใช้Stringเพื่อเป็นตัวแทนทิศทางจะสร้างความแตกต่างใกล้เคียงกับปริมาณงานที่เกี่ยวข้องเพื่อทำการเปลี่ยนแปลง แนวทางที่สมเหตุสมผลมากขึ้นคือการสมมติว่าจำนวนเส้นทางจะไม่เปลี่ยนแปลงและรับรู้ว่าคุณจะต้องทำงานเป็นจำนวนมากในการทำเช่นนั้นหากกฎของเกมเปลี่ยนไปโดยพื้นฐาน
สตีเฟ่นซี

@JimmyT - เห็นด้วยอย่างยิ่งว่าฉันเคยเห็นสิ่งนี้เกิดขึ้นหลายครั้งคุณยังได้รับการพัฒนาที่กำหนดให้สั้นของการกำหนดสตริงดังนั้นตัวอย่างเช่น product = "ตัวเลือก" กลายเป็นผลิตภัณฑ์ = "Option_or_Future" ทำให้ความหมายของรหัสสมบูรณ์
Chris Milburn

-3

ตามที่แนะนำคุณควรใช้ Enums อย่างไรก็ตามการใช้ Enum เพื่อแทนที่ Strigns นั้นไม่เพียงพอสำหรับ IMHO ในตัวอย่างของคุณความเร็วของผู้เล่นควรเป็นเวกเตอร์ด้วยฟิสิกส์:

class Vector {
  final double x;
  final double y;
}

ควรใช้เวกเตอร์นี้เพื่ออัปเดตตำแหน่งผู้เล่นแต่ละเห็บ

จากนั้นคุณสามารถรวมเข้ากับ enum เพื่อให้อ่านง่ายขึ้น:

enum Direction {
  UP(new Vector(0, 1)),
  RIGHT(new Vector(1, 0)),
  DOWN(new Vector(0, -1)),
  LEFT(new Vector(-1, 0));

  private Vector direction;

  Direction(Vector v) {
    this.direction = v;
  }

  public Vector getVector() { return this.direction; }
}

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