มันเป็นที่ยอมรับในการคัดลอกและวางยาว แต่รหัสตรงไปตรงมาแทนที่จะห่อไว้ในชั้นเรียนหรือฟังก์ชั่น?


29

สมมติว่าฉันมีส่วนของรหัสเพื่อเชื่อมต่อกับอินเทอร์เน็ตและแสดงผลลัพธ์การเชื่อมต่อเช่น:

HttpRequest* httpRequest=new HttpRequest();
httpRequest->setUrl("(some domain .com)");
httpRequest->setRequestType(HttpRequest::Type::POST);
httpRequest->setRequestData("(something like name=?&age=30&...)");
httpRequest->setResponseCallback([=](HttpClient* client, HttpResponse* response){
    string responseString=response->getResponseDataString();
        if(response->getErrorCode()!=200){
            if(response->getErrorCode()==404){
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setFontColor(255,255,255);
                alert->setPosition(Screen.MIDDLE);
                alert->show("Connection Error","Not Found");
            }else if((some other different cases)){
                (some other alert)
            }else
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setPosition(Screen.MIDDLE);
                alert->setFontColor(255,255,255);
                alert->show("Connection Error","unknown error");
            }
        }else{
            (other handle methods depend on different URL)
        }
}

รหัสมีความยาวและเป็นที่ใช้กันทั่วไป แต่รหัสข้างต้นไม่ต้องการสิ่งพิเศษใด ๆ เช่นฟังก์ชั่นที่กำหนดเองและคลาส (HttpRequest และ Alert มีให้โดยกรอบการทำงานโดยค่าเริ่มต้น) และแม้ว่าส่วนรหัสยาวมันเป็น ตรงไปตรงมาและไม่ซับซ้อน (ยาวเพียงเพราะมีการรวมกลุ่มของการตั้งค่าเช่น url ขนาดตัวอักษร ... ) และส่วนของรหัสมีความแตกต่างเล็กน้อยระหว่างคลาส (เช่น: url, ร้องขอข้อมูล, กรณีจัดการข้อผิดพลาด, หมายเลขอ้างอิงปกติ กรณี ... )

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


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

8
และ BTW การผสมระบบเครือข่ายและการแสดงข้อผิดพลาดในที่เดียวก็เป็นเรื่องที่ใหญ่โต
sleske

11
ไม่ไม่เคย. ยอมรับไม่ได้อย่างสิ้นเชิง หากคุณอยู่ในโครงการของฉันคุณจะไม่อยู่ในโครงการของฉันอีกต่อไปและคุณจะเข้าร่วมโปรแกรมการฝึกอบรมหรือ PIP
nhgrif

10
ผู้ควบคุมที่บอกว่า "กล่องแจ้งเตือนนี้ที่อยู่ตรงกลางหน้าจอของฉันมาพร้อมกับความหวาดกลัวอย่างแท้จริงฉันกำลังดู cat jif และป๊อปอัปกำลังบล็อกมุมมองของฉันทุกครั้งที่ปรากฏขึ้นโปรดเลื่อนไปทางขวาบน ." 3 สัปดาห์ต่อมา "เจ้าทำอะไรเหรอ?! ฉันไม่สามารถปิด cat jif ของฉันได้อีกต่อไปเพราะป๊อปอัปของคุณครอบคลุม X ในด้านขวาบน, แก้ไขได้"
MonkeyZeus

11
ฉันคิดว่าทุกคนที่นี่ดูเหมือนจะคิดว่านี่เป็นความคิดที่ไม่ดี แต่เพื่อเปลี่ยนคำถามทำไมคุณไม่ใส่รหัสนี้ในชั้นเรียนหรือฟังก์ชั่นแยกต่างหาก
Karl Gjertsen

คำตอบ:


87

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

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

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

ดูเพิ่มเติมที่https://softwareengineering.stackexchange.com/a/103235/63172


19
... และใครจะรู้ว่า 30 บรรทัดเหล่านี้เหมือนกันกับที่คุณดูก่อนหน้านี้หรือถ้าใครบางคนเปลี่ยนเป็นพอร์ตหรือที่อยู่ IP อื่นในสำเนาของเขา / เธอไม่ว่าด้วยเหตุผลใด ข้อสันนิษฐานโดยนัยว่า " กลุ่มใด ๆ ที่ค่อนข้าง 30 บรรทัดเริ่มต้นด้วยHttpRequestเหมือนกันทั้งหมด " เป็นเรื่องง่ายที่จะเข้าใจผิด
null

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

54

เลขที่

ในความเป็นจริงแม้แต่รหัส "ง่าย ๆ " ของคุณก็ควรแบ่งออกเป็นส่วนย่อย ๆ อย่างน้อยสองคน

หนึ่งเพื่อทำการเชื่อมต่อและจัดการการตอบสนองปกติ 200 ตัวอย่างเช่นถ้าคุณเปลี่ยนจาก POST เป็น PUT ในบางกรณี ถ้าคุณทำ zillions ของการเชื่อมต่อเหล่านี้และต้องมีหลายเธรดหรือเชื่อมต่อกัน? การมีรหัสในที่แห่งเดียวพร้อมอาร์กิวเมนต์สำหรับเมธอดจะทำให้สิ่งนี้ง่ายขึ้นมาก

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



คุณสามารถอ้างอิง SRP: การมีบล็อคโค้ดที่มีเพียงจุดประสงค์เดียวทำให้ง่ายต่อการเข้าใจและบำรุงรักษามากขึ้น ..
Roland Tepp

นี่เป็นกรณีหนึ่งที่ DRY และ SRP จัดตำแหน่งจริง บางครั้งพวกเขาทำไม่ได้
949300

18

เป็นที่ยอมรับในการคัดลอกและวาง ...

เลขที่

สำหรับฉันข้อโต้แย้งในการตัดสินใจคือสิ่งนี้:

... มันใช้กันทั่วไป ...

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

มันตรงไปตรงมาและไม่ซับซ้อน ...

ดังนั้นควรปรับโครงสร้างให้เข้ากับฟังก์ชั่นได้ง่ายขึ้น

... มีกลุ่มของการตั้งค่าเช่น url ขนาดตัวอักษร ...

และผู้ใช้ชอบที่จะเปลี่ยนแปลงอะไร แบบอักษรขนาดแบบอักษรสี ฯลฯ เป็นต้น

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

... ส่วนรหัสมีความแตกต่างเล็กน้อยระหว่างคลาส (เช่น: url, ข้อมูลคำขอ, ตัวจัดการรหัสข้อผิดพลาด, ตัวจัดการแบบปกติ ... )

Variation => พารามิเตอร์ฟังก์ชัน


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

8

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

ในการแจ้งเตือนของคุณคุณจะต้องตัดสินใจออกแบบ การตัดสินใจออกแบบที่คล้ายกันน่าจะทำสำหรับการแจ้งเตือนทั้งหมด ดังนั้นจึงเป็นไปได้ว่าคุณควรมีวิธีการบางอย่าง "ShowAlertInAStyleSuitableForMyApplication" หรืออาจจะสั้นกว่านี้เล็กน้อยและควรจะเรียกว่า

คุณจะมีคำขอ http จำนวนมากพร้อมการจัดการข้อผิดพลาดที่คล้ายกัน คุณไม่ควรทำซ้ำการจัดการข้อผิดพลาดซ้ำแล้วซ้ำอีก แต่แยกข้อผิดพลาดทั่วไปออก โดยเฉพาะอย่างยิ่งหากการจัดการข้อผิดพลาดของคุณมีรายละเอียดเพิ่มขึ้นอีกเล็กน้อย (ข้อผิดพลาดเกี่ยวกับการหมดเวลา 401 และอื่น ๆ )


6

การทำสำเนาก็โอเคในบางสถานการณ์ แต่ไม่ใช่ในอันนี้ วิธีการนั้นซับซ้อนเกินไป มีขีด จำกัด ต่ำกว่าเมื่อทำซ้ำง่ายกว่า "แฟคตอริ่ง"

ตัวอย่างเช่น:

def add(a, b)
    return a + b
end

มันโง่เพียงแค่ทำ + b

แต่เมื่อคุณมีความซับซ้อนเพิ่มขึ้นเล็กน้อยคุณก็จะข้ามเส้น

foo.a + foo.b

ควรกลายเป็น

foo.total
def foo
    ...
    def total
        return self.a + self.b
    end
end

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

ในที่สุดการขอเว็บฉันต้องการโทรหาสิ่งที่ต้องการ:

Web.Post(URI, Params, ResponseHandler);

บรรทัดนั้นคือสิ่งที่ฉันจะมีทั่วรหัสของฉัน จากนั้นเมื่อฉันต้องการเปลี่ยนแปลง "วิธีรับสิ่งของ" ฉันทำได้อย่างรวดเร็วด้วยความพยายามน้อยกว่ามาก

นอกจากนี้ยังช่วยให้รหัสแห้งและจะช่วยให้มีSRP


0

ในโครงการที่มีขนาด / ความซับซ้อนใด ๆ ฉันต้องการที่จะสามารถค้นหารหัสเมื่อฉันต้องการมันเพื่อวัตถุประสงค์ดังต่อไปนี้:

  1. แก้ไขเมื่อมันพัง
  2. เปลี่ยนฟังก์ชั่น
  3. ใช้ซ้ำได้

จะไม่น่ารักไม่ว่าจะเป็นการเข้าร่วมโครงการที่กำลังดำเนินการอยู่หรือทำงานต่อในโครงการเป็นเวลาหลายปีและเมื่อมีการร้องขอใหม่เพื่อ "เชื่อมต่ออินเทอร์เน็ตและแสดงผลการเชื่อมต่อ" ก็ง่ายต่อการหาสถานที่เนื่องจากมี การออกแบบที่ดีแทนที่จะอาศัยการค้นหาทั่วทั้งรหัสสำหรับ httprequest หรือไม่ อาจหาได้ง่ายกว่าด้วย Google อย่างไรก็ตาม

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


0

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

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

ฉันเขียนล่าม SQL ทั้งหมดเพื่อที่ฉันจะได้เปลี่ยนจาก MySQL เป็นMySQLiใน PHP ได้ดีขึ้นเพราะฉันต้องเปลี่ยนล่ามของฉันและการใช้งานทุกอย่างก็ทำได้แม้ว่ามันจะเป็นตัวอย่างที่รุนแรง


-1

ในการตัดสินใจว่าควรทำสำเนาหรือย้ายรหัสไปยังฟังก์ชันที่เรียกว่าสองครั้งหรือไม่ให้ลองตรวจสอบว่ามีความเป็นไปได้มากกว่าหรือไม่:

  1. จะต้องเปลี่ยนทั้งการใช้รหัสในวิธีเดียวกัน

  2. จะต้องเปลี่ยนอย่างน้อยหนึ่งการใช้รหัสเพื่อให้แตกต่างกัน

ในกรณีแรกมันจะดีกว่าถ้ามีฟังก์ชั่นหนึ่งจัดการกับทั้งสองวิธี ในกรณีหลังน่าจะดีกว่าถ้ามีรหัสแยกสำหรับการใช้งานสองแบบ

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

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