มีประโยชน์กับ mini-refactor code โดยหวังว่าจะปรับปรุงคุณภาพหรือเป็นเพียง“ การเคลื่อนย้ายโค้ดไปรอบ ๆ ” โดยไม่มีประโยชน์มาก?


12

ตัวอย่าง

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

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

รหัสเดิม:

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

if ($verid)
    $sql1 = "SELECT * FROM product_spec WHERE id = " . clean_input($verid);
else
    $sql1 = "SELECT * FROM product_spec WHERE product_id = " . clean_input($productid) ;
$result1 = query($sql1);
$row1 = fetch_array($result1);
/* html markup follows */

refactoring:

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

//some implementation details omitted 
$this->repository = new SpecRepository($mysql);
if ($verid)
    $row1 = $this->repository->getSpecByVersion($verid);
else 
    $row1 = $this->repository->getSpecByProductId($productid);
/* html markup follows to be refactored or left alone till another time*/

//added new class:
class SpecRepository extends MySqlRepository
{

    function getSpecByVersion(int $verid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE id = ?
        ", $verid)->getSingleArray();
    }

    function getSpecByProductId(int $productid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE product_id = ?
        ", $productid)->getSingleArray();
    }
}

ฉันควรทำสิ่งนี้หรือไม่

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

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

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

ทำไมฉันถึง refactor ตั้งแต่แรก?

ในกรณีของฉันฉันกำลังเพิ่มฟังก์ชั่นใหม่สำหรับสายผลิตภัณฑ์ใหม่ดังนั้นฉันต้องทำตามโครงสร้างรหัสที่มีอยู่สำหรับสายผลิตภัณฑ์ที่คล้ายกันหรือเขียนเอง


OT แต่สิ่งที่ต้องพิจารณาคือSelect * from ..สามารถถือเป็นรูปแบบการต่อต้าน ดูstackoverflow.com/q/3639861/31326
Peter M

1
@PeterM: เว้นแต่คุณจะเขียน ORM ซึ่งในกรณีนี้select *จะกลายเป็น "แนวปฏิบัติที่ดีที่สุด"
Robert Harvey

@RobertHarvey ในกรณีนี้ฉันกำลังถามว่าทำไมคุณถึงเขียน ORM ของคุณเอง: D
Peter M

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

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

คำตอบ:


13

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

ฉันจะบอกว่าข้อดีที่คุณได้รับจากที่นี่คือรหัสที่เข้าใจได้ง่ายกว่าอย่างน้อยในมุมมองของฉันเพราะคนที่ไม่รู้จักรหัสฐานของคุณ


โดยทั่วไป:

การปรับโครงสร้างใหม่ช่วยให้นักพัฒนาซอฟต์แวร์รายอื่นเข้าใจรหัสของคุณได้ง่ายขึ้นหรือไม่

การเปลี่ยนโครงสร้างทำให้การปรับปรุงโค้ดง่ายขึ้นหรือไม่

การเปลี่ยนโครงสร้างทำให้การบำรุงรักษารหัสทำได้ง่ายขึ้นหรือไม่

การเปลี่ยนโครงสร้างทำให้การดีบักง่ายขึ้นหรือไม่

หากคำตอบของสิ่งเหล่านี้คือ "ใช่" มันก็คุ้มค่าและมันเป็นมากกว่าแค่ "การย้ายรหัสไปมา?

หากคำตอบของสิ่งเหล่านี้คือ "ไม่" บางทีก็อาจไม่จำเป็นต้องทำการปรับสภาพใหม่

ขนาดสุดท้ายของฐานรหัสอาจมีขนาดใหญ่กว่าในแง่ของ LoC (แม้ว่า refactorings บางตัวสามารถลดขนาดฐานรหัสได้โดยการปรับปรุงโค้ดซ้ำซ้อน) แต่นั่นไม่ควรเป็นปัจจัยหากมีกำไรอื่น ๆ


9

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

อย่างไรก็ตามคุณได้รับประโยชน์แล้ว

"ในกรณีของฉันฉันกำลังเพิ่มฟังก์ชั่นใหม่สำหรับสายผลิตภัณฑ์ใหม่"

คุณไม่สามารถเพิ่มฟังก์ชั่นการทำงานของ monolith ได้อย่างง่ายดาย

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


ใช่ แต่ฉันยังสามารถเพิ่ม monolith ต่อไปสำหรับฟังก์ชั่นใหม่ :) คือฉันจะเพิ่มif/elseblock ใหม่และทำตามรูปแบบดั้งเดิมของรหัสเพื่อเพิ่มคำสั่ง SQL จากนั้นฉันจะใส่มาร์กอัพ HTML ใหม่ลงในไฟล์ monolith เดียวกัน และเพื่อเปลี่ยนคอลัมน์ฉันจะค้นหา if / else บล็อกที่รับผิดชอบการวางสาย SQL และแก้ไข SQL นั้นเพื่อเปลี่ยนแปลงคอลัมน์โดยไม่ทำให้ไฟล์แตก
Dennis

ด้วยวิธี refactored ฉันน่าจะไปที่ if / else block ใน monolith ก่อนเพื่อดูว่าฉันต้องไปที่ไฟล์ที่เก็บแยกต่างหากเพื่อเปลี่ยน SQL หรือถ้าฉันทำ monolith นั้นเมื่อไม่นานมานี้ฉันรู้ว่าจะเข้าไปในไฟล์ repository แทนโดยข้าม monolith หรือถ้าฉันค้นหา codebase ของฉันสำหรับเฉพาะ SQL ค้นหาจะวางฉันลงในแฟ้มที่เก็บใหม่นี้ยังผ่านเสา
เดนนิส

2

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

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

ฉันให้คุณค่าการปรับโครงสร้างด้วยเหตุผลต่อไปนี้:

  • ฉันเข้าใจโค้ดดีกว่า
  • ฉันลบรหัสที่ซ้ำกันดังนั้นถ้าฉันมีข้อผิดพลาดในที่เดียวฉันไม่ต้องตามล่าทุกที่ที่รหัสถูกคัดลอกวาง
  • ฉันสร้างคลาสที่มีบทบาทที่กำหนดไว้อย่างดี ตัวอย่างเช่นฉันสามารถใช้ที่เก็บเดียวกันซ้ำเพื่อเพิ่มความชุ่มชื้นให้กับหน้า HTML, ฟีด XML หรืองานพื้นหลัง สิ่งนี้จะไม่เกิดขึ้นหากรหัสการเข้าถึงฐานข้อมูลอยู่ในคลาส HTML เท่านั้น
  • ฉันเปลี่ยนชื่อตัวแปรวิธีการคลาสโมดูลโฟลเดอร์ถ้าไม่ตรงกับความเป็นจริงในปัจจุบัน ฉันcommentรหัสผ่านชื่อตัวแปรชื่อวิธีการ
  • ฉันแก้ไขข้อบกพร่องที่ซับซ้อนด้วยการผสมผสานระหว่างการทดสอบหน่วยและการปรับโครงสร้างใหม่
  • ฉันมีความยืดหยุ่นในการแทนที่โมดูลทั้งหมดแพ็กเกจ หาก ORM ปัจจุบันล้าสมัยบั๊กกี้หรือไม่ได้รับการดูแลรักษานั้นจะง่ายกว่าถ้ามันจะไม่กระจายไปทั่วโครงการ
  • ฉันค้นพบความสามารถใหม่ ๆ หรือการปรับปรุงที่ทำให้เกิดข้อเสนอสำหรับลูกค้า
  • ฉันสร้างส่วนประกอบที่ใช้ซ้ำได้ นี่เป็นหนึ่งในผลประโยชน์ที่ยิ่งใหญ่ที่สุดเพราะงานที่ทำในโครงการนั้นอาจถูกนำมาใช้ซ้ำสำหรับลูกค้า / โครงการอื่น
  • ฉันมักจะเริ่มต้นด้วยวิธีการแก้ปัญหาที่โง่เง่า, hardcoded, แปลกประหลาดและฉัน refactor มันในภายหลังเมื่อฉันเรียนรู้เพิ่มเติม ช่วงเวลาต่อมาอาจเป็นวันนี้หรืออาจหลังจากหลายวัน ฉันไม่ต้องการใช้เวลามากเกินไปในarchitectการแก้ปัญหา สิ่งนี้จะเกิดขึ้นขณะเขียน - เขียนรหัสซ้ำไปมา

ในการปรับโครงสร้างโลกที่สมบูรณ์แบบควรได้รับการตรวจสอบโดยการทดสอบหน่วยการทดสอบการรวมระบบเป็นต้น หากไม่มีให้ลองเพิ่ม IDE บางตัวอาจช่วยได้มาก ฉันมักจะใช้ปลั๊กอินที่มีค่าใช้จ่าย ฉันต้องการใช้เวลาเพียงเล็กน้อยกับ refactorings และมีประสิทธิภาพมากเกี่ยวกับเรื่องนี้

ฉันยังแนะนำข้อบกพร่อง ฉันสามารถดูได้ว่าเหตุใด QA จึงถามare you guys doing refactoring? because everything stopped working!นี่เป็นความเสี่ยงที่ทีมต้องยอมรับและเราต้องโปร่งใสเสมอ

ในช่วงหลายปีที่ผ่านมาฉันพบว่าการปรับปรุงอย่างต่อเนื่องช่วยเพิ่มผลผลิตของฉัน เพิ่มฟีเจอร์ใหม่ได้ง่ายกว่ามาก การสร้างใหม่ขนาดใหญ่นั้นไม่ต้องการการเขียนซ้ำทั้งหมดบ่อยครั้งที่เกิดขึ้นเมื่อฐานรหัสไม่ได้ปรับให้เข้ากับวิวัฒนาการของผลิตภัณฑ์ นอกจากนี้ยังสนุก


นี่ใกล้เคียงกับการลงคะแนนเสียงมาก - ไม่มี 'ฉัน' ใน 'โปรแกรมเมอร์คอมพิวเตอร์' โดย 'ฉัน' คุณฉัน 'ฉัน' (ลงคะแนน) หรือคุณหมายถึงนักพัฒนาที่ทำงานกับรหัสตอนนี้และในอนาคตหรือไม่
mattnz

ฉันสังเกตเห็นฉันด้วย แต่ฉันก็ใช้ถ้อยคำ ฉันทำงานเป็นผู้พัฒนาเดี่ยวเป็นเวลาหลายปีและเมื่อฉันเขียนคำตอบนี้ฉันขอย้ำส่วนใหญ่จากประสบการณ์นั้น ฉันสามารถแทนที่Iด้วยtoแม้ว่า
Adrian Iftode

1

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

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

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

สิ่งต่าง ๆ เช่น 'การพัฒนาคุณสมบัติใหม่ที่เร็วขึ้น' ไม่ได้ลดลงเพราะเป็นเพียงการลดต้นทุน คุณต้องสร้างยอดขาย


1

ตามความเห็นของฉัน refactoring เป็นการลงทุนที่ดี

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

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

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

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