Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

แก้รหัสสปาเก็ตตี้ด้วยการเปลี่ยนมุมมองง่ายๆ

ระเบียบยักษ์ของ if คำพูดคอยจ้องหน้าคุณอยู่เสมอ คุณรู้สึกว่าคุณ ควร สามารถลดความซับซ้อนได้ ยกเว้น Business Logic ที่คอยขัดขวาง

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

ฉันได้ยินว่าคุณขว้างแล็ปท็อปผ่านหน้าต่างจากตรงนี้ไป

คุณ สามารถ เขียน if . จำนวนมาก คำสั่งเพื่อจัดการกับกฎเหล่านี้:

class Quote
  attr_accessor :line_items
 
  ... 
 
  def add_line_item(line_item)
    if line_item.kind_of?(Ad)
      self.line_items << line_item
    elsif line_item.kind_of?(Website)
      if @line_items.select {|item| item.kind_of?(Ad) }.length >= 5
        # TODO: Put the fractions of a cent into a bank account
        # I have set up
        line_item.price *= 0.8
      end
      existing_website = self.line_items.detect { |item| item.kind_of?(Website) }
      if existing_website
        existing_website.price += line_item.price
      else
        self.line_items << line_item
      end
    end
  end
end

แต่ฉันคิดว่าเราเห็นด้วยว่ามันแย่มาก คุณจะแก้ให้หายยุ่งได้อย่างไร

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

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

ย้อนกลับวิธีการของคุณ!

วิธีหนึ่งที่ฉันโปรดปรานในการปรับโครงสร้างโค้ดใหม่คือ ลองย้อนกลับผู้โทรและผู้รับสาย นี่คือตัวอย่าง การใช้โค้ดด้านบน:

app/models/quote.rb
class Quote
  ...
  def add_line_item(line_item)
    line_item.add_to_quote(self)
  end
end
app/models/line_item.rb
class Ad < LineItem
  ...
  def add_to_quote(quote)
    quote.line_items << self
  end 
end
app/models/website.rb
class Website < LineItem
  def add_to_quote(quote)
    if quote.line_items.select {|item| item.kind_of?(Ad) }.length >= 5
      # TODO: Put the fractions of a cent into a bank account
      # I have set up
      self.price *= 0.8
    end
    existing_website = quote.line_items.detect { |item| item.kind_of?(Website) }
    if existing_website
      existing_website.price += self.price
    else
	  quote.line_items << self
    end
  end
end

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

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

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

ตำแหน่งที่คุณอาจไม่ต้องการใช้รูปแบบนี้

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

  • มันสามารถทำลายการห่อหุ้มได้ คุณอาจต้องเปิดเผยแอตทริบิวต์ใน Quote วัตถุที่คุณไม่ต้องการเปิดเผยต่อสาธารณะ

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

  • อาจละเมิดหลักการความรับผิดชอบเดียวใน Ad เพราะตอนนี้ Ad มีหน้าที่รู้วิธีเพิ่มตัวเองใน Quote .

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

เหตุใดจึงเป็นหนึ่งในรายการโปรดของฉัน

แม้จะมีปัญหาเหล่านี้ แต่ก็เป็นหนึ่งในการปรับโครงสร้างใหม่ที่ฉันชื่นชอบ รหัสที่ฉันเขียนหลังจากใช้รูปแบบนี้มีแนวโน้มที่จะชัดเจนและมั่นใจมากขึ้น

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

ลองใช้โค้ดของคุณเอง

เช่นเดียวกับรูปแบบอื่นๆ ที่ฉันชื่นชอบ ตอนแรกฉันเจอ Reversing Method ในรูปแบบแนวทางปฏิบัติที่ดีที่สุดของ Smalltalk และเป็นเครื่องมือที่มีค่านับตั้งแต่นั้นมา

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