เป็นการเริ่มต้นที่ดีกว่าการเตรียมแอตทริบิวต์ในชั้นเรียนหรือเพิ่มไปตามทาง?


11

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

ใน Python ฉันมักจะใช้คลาสที่ว่างเปล่าเป็นคอนเทนเนอร์โครงสร้างข้อมูล super-catchall (เรียงลำดับเหมือนไฟล์ JSON) และเพิ่มคุณสมบัติไปพร้อมกัน:

class DataObj:
    "Catch-all data object"
    def __init__(self):
        pass

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

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

อย่างไรก็ตามเมื่อเร็ว ๆ นี้ฉันรู้สึกประทับใจ (โดยโปรแกรมเมอร์ FP) ว่านี่เป็นการฝึกฝนที่แย่มากเพราะมันยากที่จะอ่านรหัส หนึ่งจะต้องผ่านรหัสทั้งหมดเพื่อคิดออกว่าคุณลักษณะ DataObj จริงมีอะไร

คำถาม : ฉันจะเขียนสิ่งนี้ใหม่เพื่อการบำรุงรักษาที่มากขึ้นได้อย่างไรโดยไม่ลดความยืดหยุ่นลง?

มีแนวคิดใดบ้างจากการเขียนโปรแกรมใช้งานที่ฉันสามารถนำไปใช้ได้หรือไม่

ฉันกำลังมองหาแนวปฏิบัติที่ดีที่สุดที่นั่น

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

class DataObj:
    "Catch-all data object"
    def __init__(self):
        data.a = 0
        data.b = ""
        data.c = []

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

นี่เป็นความคิดที่ดีจริงหรือ ถ้าฉันไม่ทราบว่าคุณลักษณะของฉันคืออะไร


โครงสร้างข้อมูลของคุณไม่แน่นอนดังนั้นคุณจึงกังวลกับความสามารถในการบำรุงรักษา ในเวลาว่างของคุณมากมาย™ลองอ่านบทความเกี่ยวกับรูปแบบข้อมูลไม่เปลี่ยนรูป มันอาจเปลี่ยนวิธีการใช้ข้อมูลของคุณอย่างสมบูรณ์
9000

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

คำตอบ:


10

ฉันจะเขียนสิ่งนี้ใหม่เพื่อการบำรุงรักษาที่มากขึ้นโดยไม่ต้องเสียสละความยืดหยุ่นได้อย่างไร

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

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

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

ถ้าฉันไม่ทราบว่าคุณลักษณะของฉันคืออะไร

จากนั้นคุณเมาในกรณีใด ๆ และควรใช้ dict หรือรายการแทนชื่อแอตทริบิวต์ hardcoding แต่ฉันคิดว่าคุณหมายถึง "... ในเวลาที่ฉันเขียนคลาสคอนเทนเนอร์" จากนั้นคำตอบคือ: "คุณสามารถแก้ไขไฟล์ใน lockstep, duh" ต้องการแอตทริบิวต์ใหม่หรือไม่ เพิ่มแอ็ตทริบิวต์ frigging ให้กับคลาสคอนเทนเนอร์ มีรหัสมากขึ้นเมื่อใช้คลาสนั้นและไม่จำเป็นต้องใช้แอตทริบิวต์นั้นใช่ไหม พิจารณาแยกสิ่งต่าง ๆ ออกเป็นสองคลาสแยกกัน (ใช้มิกซ์อินเพื่อคงความเป็น DRY) ดังนั้นให้เลือกตัวเลือกหากเหมาะสม

หากคุณกลัวที่จะเขียนคลาส container ซ้ำ ๆ : ใช้ metaprogramming อย่างรอบคอบหรือใช้collections.namedtupleถ้าคุณไม่จำเป็นต้องกลายพันธุ์สมาชิกหลังจากการสร้าง (เพื่อน FP ของคุณจะยินดี)


7

คุณสามารถใช้คลาส Bunchของ Alex Martelli ได้ตลอดเวลา ในกรณีของคุณ:

class DataObj:
    "Catch-all data object"
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def processData(inputs):
    data = DataObj(a=1, b="sym", c=[2,5,2,1])

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

และใช่การทำสิ่งต่าง ๆ ในลักษณะนี้เป็นความคิดที่ดีในบางครั้ง


1

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

มีความเป็นไปได้ที่processDataอาจจะดีกว่าเป็นวิธีการ ( process_dataเพื่อทำตามข้อตกลงการตั้งชื่องูหลาม) เนื่องจากมันทำหน้าที่ในชั้นเรียน จากตัวอย่างนี้ดูเหมือนว่ามันอาจจะดีกว่าในฐานะโครงสร้างข้อมูล (ซึ่ง a dictอาจพอเพียง)

จากตัวอย่างจริงคุณอาจพิจารณานำคำถามไปให้CodeReviewซึ่งพวกเขาสามารถช่วยในการปรับโครงสร้างรหัสใหม่

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