มันสมเหตุสมผลไหมที่จะใช้อ็อบเจกต์ (แทนที่จะเป็นชนิดดั้งเดิม) สำหรับทุกสิ่งใน C ++?


17

ในช่วงโปรเจ็กต์ล่าสุดที่ฉันทำงานอยู่ฉันต้องใช้ฟังก์ชั่นมากมายเช่นนี้:

static bool getGPS(double plane_latitude, double plane_longitude, double plane_altitude,
                       double plane_roll, double plane_pitch, double plane_heading,
                       double gimbal_roll, double gimbal_pitch, double gimbal_yaw, 
                       int target_x, int target_y, double zoom,
                       int image_width_pixels, int image_height_pixels,
                       double & Target_Latitude, double & Target_Longitude, double & Target_Height);

ดังนั้นฉันต้องการ refactor เพื่อให้มีลักษณะเช่นนี้:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                            PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

สิ่งนี้ดูเหมือนว่าฉันจะสามารถอ่านและปลอดภัยได้ดีกว่าวิธีแรก แต่มันสมเหตุสมผลหรือไม่ที่จะสร้างPixelCoordinateและPixelSizeเรียน? หรือฉันจะดีกว่าเพียงแค่ใช้std::pair<int,int>สำหรับแต่ละ และมีเหตุผลที่จะมีZoomLevelชั้นเรียนหรือฉันควรใช้doubleหรือไม่

สัญชาตญาณของฉันที่อยู่เบื้องหลังการใช้คลาสสำหรับทุกสิ่งขึ้นอยู่กับสมมติฐานเหล่านี้:

  1. หากมีคลาสสำหรับทุกสิ่งมันจะเป็นไปไม่ได้ที่จะส่ง a ไปยังZoomLevelตำแหน่งที่Weightวัตถุถูกคาดหวังดังนั้นมันจึงยากที่จะให้อาร์กิวเมนต์ที่ผิดกับฟังก์ชัน
  2. ในทำนองเดียวกันการดำเนินการที่ผิดกฎหมายบางอย่างจะทำให้เกิดข้อผิดพลาดในการรวบรวมเช่นการเพิ่มGPSCoordinateไปยังZoomLevelหรืออย่างอื่นGPSCoordinate
  3. การดำเนินการทางกฎหมายจะง่ายต่อการเป็นตัวแทนและปลอดภัย เช่นการลบสองGPSCoordinates จะให้ aGPSDisplacement

อย่างไรก็ตามรหัส C ++ ส่วนใหญ่ที่ฉันเห็นใช้ประเภทดั้งเดิมมากมายและฉันคิดว่าต้องมีเหตุผลที่ดี มันเป็นความคิดที่ดีที่จะใช้วัตถุเพื่ออะไรหรือมีข้อเสียที่ฉันไม่ทราบหรือไม่?


8
"รหัส C ++ ส่วนใหญ่ที่ฉันเคยเห็นใช้ประเภทแบบดั้งเดิมจำนวนมาก" - ความคิดสองประการที่อยู่ในใจ: รหัส C ++ จำนวนมากอาจถูกเขียนโดยโปรแกรมเมอร์ที่คุ้นเคยกับ C ซึ่งมีนามธรรมที่อ่อนแอกว่า เช่นเดียวกับกฎหมายของปลาสเตอร์เจียน
congusbongus

3
และฉันคิดว่าเป็นเพราะประเภทดั้งเดิมนั้นเรียบง่ายตรงไปตรงมารวดเร็วเงาและมีประสิทธิภาพ ในตัวอย่างของ OP มันเป็นความคิดที่ดีที่จะแนะนำคลาสใหม่บางอย่าง แต่คำตอบสำหรับคำถามไตเติ้ล (ซึ่งมันบอกว่า "ทุกอย่าง") นั้นไม่ดังก้อง!
Mr Lister

1
@ สูงสุดพิมพ์ดีดคู่ไม่ทำอะไรเลยที่จะแก้ปัญหาของ OP
Mr Lister

1
@CongXu: ฉันเกือบจะคิดเหมือนกัน แต่ในความหมายมากกว่า "รหัสจำนวนมากในภาษาใด ๆ อาจถูกเขียนโดยโปรแกรมเมอร์ที่มีปัญหากับการสร้าง abstractions เลย"
Doc Brown

1
ตามที่พูดไป "ถ้าคุณมีฟังก์ชั่นที่มี 17 พารามิเตอร์คุณอาจพลาดบางอย่าง"
Joris Timmermans

คำตอบ:


24

ได้แน่นอน. ฟังก์ชั่น / วิธีการที่ขัดแย้งกันมากเกินไปคือกลิ่นรหัสและระบุอย่างน้อยหนึ่งอย่างต่อไปนี้:

  1. ฟังก์ชั่น / วิธีการทำหลายสิ่งหลายอย่างพร้อมกัน
  2. ฟังก์ชั่น / วิธีการนั้นจำเป็นต้องมีการเข้าถึงหลาย ๆ อย่างเพราะเป็นการถามไม่บอกหรือละเมิดกฎหมายการออกแบบ OO
  3. ข้อโต้แย้งมีความสัมพันธ์กันอย่างใกล้ชิด

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

static GPSCoordinate getGPS(Plane plane, Angle3D gimbalAngle, GPSView view)

(ฉันไม่คุ้นเคยกับ GPS ดังนั้นนี่อาจเป็นนามธรรมที่ไม่ถูกต้องตัวอย่างเช่นฉันไม่เข้าใจว่าการซูมเกี่ยวข้องกับฟังก์ชั่นที่เรียกว่าgetGPS)

โปรดเลือกคลาสที่ชอบPixelCoordinateมากกว่าstd::pair<T, T>แม้ว่าทั้งสองจะมีข้อมูลเหมือนกัน มันเพิ่มคุณค่าทางความหมายบวกกับความปลอดภัยอีกประเภทหนึ่งเล็กน้อย (คอมไพเลอร์จะหยุดคุณไม่ให้ส่งScreenCoordinatea PixelCoordinateถึงแม้ว่าทั้งคู่จะเป็นpairs ก็ตาม


1
ไม่ต้องพูดถึงคุณสามารถส่งผ่านโครงสร้างที่มีขนาดใหญ่ขึ้นโดยการอ้างอิงสำหรับการเพิ่มประสิทธิภาพขนาดเล็ก ๆ น้อย ๆ ที่เด็กเจ๋ง ๆ ทุกคนพยายามทำ แต่ไม่ควรจริง ๆ
ratchet freak

6

ข้อจำกัดความรับผิดชอบ:ฉันชอบ C ถึง C ++

ฉันจะใช้ structs แทนคลาส จุดนี้เป็นความคิดที่ดีที่สุดเนื่องจาก structs และคลาสเกือบจะเหมือนกัน (ดูที่นี่สำหรับแผนภูมิอย่างง่ายหรือคำถามSOนี้) แต่ฉันคิดว่ามีความแตกต่างในข้อดี

โปรแกรมเมอร์ C คุ้นเคยกับ structs เป็นบล็อคของข้อมูลที่มีความหมายมากกว่าเมื่อจัดกลุ่มในขณะที่เมื่อฉันเห็น clasas ฉันถือว่ามีสถานะภายในและวิธีการที่จะจัดการกับสถานะนั้น พิกัด GPS ไม่มีสถานะเป็นเพียงข้อมูล

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

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

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

ในทำนองเดียวกันการดำเนินการที่ผิดกฎหมายบางอย่างจะทำให้เกิดข้อผิดพลาดในการรวบรวมเช่นการเพิ่ม GPSCoordinate ใน ZoomLevel หรือ GPSCoordinate อื่น

ใช้งานตัวดำเนินการโอเวอร์โหลดได้ดี

การดำเนินการทางกฎหมายจะง่ายต่อการเป็นตัวแทนและปลอดภัย เช่นการลบ GPSCoordinates สองตัวจะให้ตำแหน่ง GPSDisplacement

นอกจากนี้ยังใช้ประโยชน์จากผู้ปฏิบัติงานเกินพิกัด

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


3

การใช้ประเภทเฉพาะมีข้อได้เปรียบที่ทำให้การเรียงลำดับของข้อโต้แย้งยากขึ้น ตัวอย่างเวอร์ชันแรกของฟังก์ชันตัวอย่างของคุณเริ่มต้นด้วย chain 9 doubles เมื่อมีคนใช้ฟังก์ชั่นนี้มันง่ายมากที่จะไขลำดับและส่งมุม gimbal แทนพิกัด GPS และในทางกลับกัน สิ่งนี้สามารถนำไปสู่ข้อผิดพลาดที่แปลกและยากที่จะติดตาม เมื่อคุณส่งอาร์กิวเมนต์เพียงสามชนิดที่มีประเภทต่างกันแทนข้อผิดพลาดนี้สามารถรวบรวมได้โดยคอมไพเลอร์

ในความเป็นจริงฉันคิดว่าจะก้าวไปอีกขั้นและแนะนำประเภทที่เหมือนกันทางเทคนิค แต่มีความหมายที่แตกต่างกัน ตัวอย่างเช่นการมีคลาสPlaneAngle3dและคลาสแยกต่างหากGimbalAngle3dแม้ว่าทั้งคู่จะเป็นคอลเลกชันสามคู่ สิ่งนี้จะทำให้เป็นไปไม่ได้ที่จะใช้สิ่งใดสิ่งหนึ่งเป็นสิ่งอื่นเว้นแต่ว่าจะเป็นการจงใจ

ฉันอยากจะแนะนำให้ใช้ typedefs ซึ่งเป็นเพียงนามแฝงสำหรับประเภทดั้งเดิม ( typedef double ZoomLevel) ไม่เพียง แต่ด้วยเหตุผลนั้น แต่ยังสำหรับอีก: มันช่วยให้คุณสามารถเปลี่ยนประเภทในภายหลังได้อย่างง่ายดาย เมื่อคุณได้รับแนวคิดหนึ่งวันที่การใช้งานfloatอาจจะดีเท่าdoubleแต่อาจมีหน่วยความจำมากขึ้นและมีประสิทธิภาพของ CPU คุณต้องเปลี่ยนสิ่งนี้ในที่เดียวไม่ใช่ใน dozends


+1 สำหรับคำแนะนำ typedef ทำให้คุณได้รับสิ่งที่ดีที่สุดทั้งสองโลก: ประเภทและวัตถุดั้งเดิม
Karl Bielefeldt

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

2

คำตอบที่ดีที่นี่แล้ว แต่มีสิ่งหนึ่งที่ฉันต้องการเพิ่ม:

การใช้PixelCoordinateและPixelSizeชั้นเรียนทำให้สิ่งต่าง ๆ มีความเฉพาะเจาะจงมาก ทั่วไปCoordinateหรือPoint2Dชั้นเรียนอาจจะดีกว่าที่คุณต้องการ A Point2Dควรเป็นคลาสที่ใช้งานการดำเนินการจุด / เวกเตอร์ที่พบบ่อยที่สุด คุณสามารถใช้คลาสดังกล่าวได้โดยทั่วไป (โดยPoint2D<T>ที่ T อาจเป็นintหรือdoubleหรืออย่างอื่น)

การดำเนินการ 2D / vector นั้นเป็นเรื่องธรรมดามากสำหรับงานการเขียนโปรแกรมรูปทรงเรขาคณิต 2D จำนวนมากซึ่งจากประสบการณ์ของฉันการตัดสินใจดังกล่าวจะเพิ่มโอกาสในการนำการดำเนินงาน 2D ที่มีอยู่กลับมาใช้ใหม่ได้อย่างมาก คุณจะหลีกเลี่ยงความจำเป็นในการดำเนินการอีกครั้งพวกเขาสำหรับแต่ละPixelCoordinate, GPSCoordinate"Whatsover" ชั้นประสานงานอีกครั้งและอีกครั้ง


1
คุณสามารถทำได้struct PixelCoordinate:Point2D<int>ถ้าคุณต้องการความปลอดภัยประเภทที่เหมาะสม
ratchet freak

0

ดังนั้นฉันต้องการ refactor เพื่อให้มีลักษณะเช่นนี้:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                        PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

สิ่งนี้ดูเหมือนว่าฉันจะสามารถอ่านและปลอดภัยได้ดีกว่าวิธีแรก

มันเป็น [อ่านง่ายขึ้น] มันก็เป็นความคิดที่ดี

แต่มันสมเหตุสมผลแล้วที่จะสร้างคลาส PixelCoordinate และ PixelSize

หากพวกเขาเป็นตัวแทนแนวคิดที่แตกต่างมันทำให้รู้สึก (นั่นคือ "ใช่.")

หรือฉันจะดีกว่าถ้าใช้ std :: pair สำหรับแต่ละรายการ

typedef std::pair<int,int> PixelCoordinate, PixelSize;คุณสามารถเริ่มต้นด้วยการทำ ด้วยวิธีนี้คุณจะครอบคลุมความหมาย (คุณกำลังทำงานกับพิกัดพิกเซลและขนาดพิกเซลไม่ใช่กับ std :: pair ในรหัสลูกค้าของคุณ) และหากคุณต้องการขยายคุณเพียงแค่เปลี่ยน typedef ด้วยคลาสและรหัสลูกค้าของคุณควร ต้องมีการเปลี่ยนแปลงน้อยที่สุด

และมันก็สมเหตุสมผลที่จะมีคลาส ZoomLevel หรือฉันควรจะใช้ double หรือไม่?

คุณสามารถพิมพ์มันได้เช่นกัน แต่ฉันจะใช้ a doubleจนกว่าฉันจะต้องการฟังก์ชันการทำงานที่เกี่ยวข้องกับมันที่ไม่มีอยู่สำหรับคู่ (ตัวอย่างเช่นการมีZoomLevel::defaultค่าจะต้องให้คุณกำหนดคลาส แต่จนกว่าคุณจะมีอะไรที่ชอบ ให้มันเป็นสองเท่า)

สัญชาตญาณของฉันที่อยู่เบื้องหลังการใช้คลาสสำหรับทุกสิ่งขึ้นอยู่กับสมมติฐานเหล่านี้

นี่เป็นข้อโต้แย้งที่รุนแรง (คุณควรพยายามทำให้ส่วนต่อประสานของคุณใช้งานง่ายและใช้งานไม่ถูกต้อง)

อย่างไรก็ตามรหัส C ++ ส่วนใหญ่ที่ฉันเห็นใช้ประเภทดั้งเดิมมากมายและฉันคิดว่าต้องมีเหตุผลที่ดี

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

มันเป็นความคิดที่ดีที่จะใช้วัตถุเพื่ออะไรหรือมีข้อเสียที่ฉันไม่ทราบหรือไม่?

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

นั่นคือถ้าคุณมีพิกัดที่มี x, y และ z อยู่เสมอมันจะดีกว่าถ้ามีcoordinateคลาสมากกว่าส่งพารามิเตอร์สามตัวทุกที่

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