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

หลีกเลี่ยงคลาส Junk-Drawer ใน Ruby

เนื่องจาก Ruby เป็นภาษาเชิงวัตถุ เราจึงมักจะจำลองโลกเป็นชุดของวัตถุ เราบอกว่าจำนวนเต็มสองจำนวน (x และ y) เป็นจุด และเส้นมีสองตัว

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

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

คลาส Junk-drawer เป็นเรื่องธรรมดามากจนเรามักจะยอมรับว่าเป็นสิ่งที่หลีกเลี่ยงไม่ได้และพยายามใช้ขั้นตอน "การปรับโครงสร้างใหม่" เพื่อพัฒนา แต่ถ้าเราสามารถหลีกเลี่ยงปัญหาได้ตั้งแต่แรกล่ะ

กฎข้อแรกของการพัฒนาเว็บคือเราไม่พูดถึง คลาสผู้ใช้

หัวใจของแอป Rails เวอร์ชันโปรดักชั่นที่ต่อสู้อย่างหนักหน่วงคือความโหดร้ายของคลาสที่ชื่อว่า User .

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

class User
  attr_accessor :username, :password, :email, :address

  def authenticate!(password)
    ...
  end
end

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

class User
    ...

    attr_accessor :payment_processor_token, :subscription_plan_id, ...etc

    def charge_cc
      ...
    end
end

ยอดเยี่ยม! ตอนนี้เรากำลังทำเงินจริง! CEO ตัดสินใจว่าพวกเขาต้องการส่งออก VCard ด้วยข้อมูลติดต่อของผู้ใช้ เพื่อให้ทีมขายสามารถนำเข้าไปยัง SalesForce ได้อย่างง่ายดาย ถึงเวลาที่จะจุดไฟเป็นกลุ่ม:

class User
    ...

    def export_vcard
      ...
    end
end

เราทำอะไรไปบ้าง

เราเริ่มต้นด้วยคลาส User ซึ่งมีวัตถุประสงค์เพื่อจัดการการพิสูจน์ตัวตนเท่านั้น การเพิ่มวิธีการและคุณลักษณะเพิ่มเติมทำให้เรากลายเป็นผู้ใช้/สมาชิก/ติดต่อ Frankenstein ไฮบริด

ทำไมเราจะทำเช่นนี้? เราไม่เคารพตัวเองในฐานะนักพัฒนาหรือไม่? พ่อแม่รักเราไม่พอหรือ? หรือเราตั้งตัวเองให้ล้มเหลวโดยเชื่อว่าข้อมูลนี้คือ .จริงๆ สิ่งของ สิ่งของ

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

ความจริงเกี่ยวกับข้อมูลก็คือข้อมูลนั่นเอง

ข้อมูลเป็นเพียงข้อมูล วันนี้คุณอาจต้องปฏิบัติต่อมันเหมือนเป็นแฟน พรุ่งนี้อาจจะเป็นแฟนเก่า

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

ฟังดูง่าย แต่วิธีนี้ทำให้แยกข้อกังวลออกเป็นโมดูลต่างๆ ได้ง่ายมาก

นี่คือการ์ตูนเกี่ยวกับวิธีที่เราอาจสร้างระบบผู้ใช้ใน Elixir:

my_user = %{username: "foo", password: ..., phone: ..., payment_token: ...}
my_user = Authentication.authenticate(my_user)
my_user = Subscription.charge(my_user)
my_user = Contact.export_vcard(my_user)

เนื่องจากข้อมูลแยกจากโค้ด จึงไม่มีโมดูลใดที่สามารถตีความข้อมูลนั้นได้

นำกลับมาที่ Ruby

เนื่องจากวิธีนี้ใช้ได้ผลดีใน Elixir ทำไมไม่ลองใช้วิธีนี้ใน Ruby ล่ะ? ไม่มีอะไรหยุดเราไม่ให้สร้าง User ลงใน wrapper ง่ายๆ สำหรับข้อมูล และดึงตรรกะทางธุรกิจทั้งหมดลงในโมดูล

class User
  attr_accessor :username, :password, :email, :address
end

module Authentication
  def self.authenticate!(user)
    ..
  end
end

module Subscription
  def self.charge(user)
    ..
  end
end

module Contact
  def self.export_vcard(user)
    ..
  end
end

คุณยังสามารถสร้าง User จัดคลาสเป็น struct หรือเพิ่มโค้ดเพื่อให้ (ชนิด) ไม่เปลี่ยนรูป

แล้วไงล่ะ

ดูเหมือนการเปลี่ยนแปลงเล็กน้อยหรือไม่? ประเด็นคืออะไร?

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

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

แนวทางอื่นๆ

มีวิธีอื่นในการป้องกันปัญหาลิ้นชักขยะ ตามเนื้อผ้า ในการเขียนโปรแกรมเชิงวัตถุ คุณจะพยายามทำเช่นนั้นผ่านการสืบทอดและการปรับโครงสร้างใหม่อย่างระมัดระวัง ในปี 2012 Sandi Metz ได้ตีพิมพ์ Practical Object Oriented Design in Ruby ซึ่งชักชวนให้หลายคนเริ่มเขียนวัตถุโดยใช้การฉีดพึ่งพา เมื่อไม่นานมานี้ ความนิยมในการเขียนโปรแกรมเชิงฟังก์ชันทำให้ Rubyists ทดลองกับ "วัตถุข้อมูล" ที่ไม่เปลี่ยนรูป

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

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