หลักการความรับผิดชอบเดี่ยว - ฉันจะหลีกเลี่ยงการแตกรหัสได้อย่างไร


56

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

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

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

เรามีตัวสร้างคลาสที่ใช้พารามิเตอร์อินเทอร์เฟซมากกว่า 20 ตัวดังนั้นการลงทะเบียน IoC และการแก้ปัญหาของเราจึงกลายเป็นสัตว์ประหลาดในตัวของมันเอง

ฉันต้องการทราบว่ามี 'refactor อยู่ห่างจาก SRP' หรือไม่ที่เราสามารถใช้เพื่อช่วยแก้ไขปัญหาเหล่านี้ได้ ฉันได้อ่านแล้วว่ามันไม่ได้เป็นการละเมิด SOLID ถ้าฉันสร้างคลาสที่ว่างเปล่าแบบหยาบจำนวนมากที่ 'ห่อ' คลาสที่เกี่ยวข้องอย่างใกล้ชิดเพื่อให้เข้าถึงจุดรวมการทำงานของจุดเดียว (เช่นการเลียนแบบน้อย ใช้งานคลาสมากเกินไป SRP)

นอกจากนั้นฉันไม่สามารถคิดถึงวิธีแก้ปัญหาที่จะช่วยให้เราสามารถดำเนินการต่อไปในการพัฒนาของเราในทางปฏิบัติในขณะที่ทำให้ทุกคนมีความสุข

ข้อเสนอแนะใด ๆ


18
นั่นเป็นเพียงความคิดเห็นของฉัน แต่ฉันคิดว่ามีกฎอีกข้อหนึ่งที่ถูกลืมได้ง่ายมากภายใต้กองของตัวย่อต่างๆ - "หลักการสามัญสำนึก" เมื่อ 'การแก้ปัญหา' สร้างปัญหามากขึ้นว่ามันแก้ปัญหาจริงๆแล้วมีอะไรบางอย่างผิดปกติ ปัญหาของฉันคือถ้าปัญหาซับซ้อน แต่อยู่ในชั้นเรียนที่จัดการกับความสลับซับซ้อนและยังคงดีบั๊กง่าย - ฉันทิ้งไว้คนเดียว โดยทั่วไปความคิด 'wrapper' ของคุณดูเหมือนจะเป็นสิ่งที่ดีสำหรับฉัน แต่ฉันจะทิ้งคำตอบให้กับคนที่มีความรู้มากขึ้น
Patryk Ćwiek

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

62
คลาสที่มีพารามิเตอร์คอนสตรัคเตอร์ 20 ตัวนั้นไม่ได้ฟัง SRP มากนักสำหรับฉัน!
MattDavey

1
คุณเขียน "... การลงทะเบียน IoC และการแก้ปัญหา ... "; ดูเหมือนว่าคุณ (หรือหัวหน้าทีมของคุณ) คิดว่า "IoC" และ "การพึ่งพาการฉีด" (DI) เป็นสิ่งเดียวกันซึ่งไม่เป็นความจริง DI เป็นวิธีการที่จะบรรลุ IoC แต่ไม่แน่นอนเพียงอย่างเดียว คุณควรวิเคราะห์อย่างรอบคอบว่าทำไมคุณถึงต้องการทำ IoC หากเป็นเพราะคุณต้องการเขียนการทดสอบหน่วยคุณสามารถลองใช้รูปแบบ locator service หรือเพียงแค่อินเตอร์เฟสคลาส ( ISomething) IMHO ผู้อนุมัติเหล่านี้ง่ายต่อการจัดการมากกว่าการฉีดแบบพึ่งพาและทำให้ได้รหัสที่อ่านง่ายขึ้น

2
คำตอบที่ให้ไว้ที่นี่จะเป็นสุญญากาศ เราต้องเห็นรหัสเพื่อให้คำตอบเฉพาะ 20 พารามิเตอร์ในตัวสร้าง? ดีคุณอาจหายไปวัตถุ ... หรือพวกเขาทั้งหมดอาจถูกต้อง หรือพวกเขาอาจอยู่ในไฟล์ config หรือพวกเขาอาจอยู่ในชั้น DI หรือ ... อาการที่แน่นอนน่าสงสัย แต่เหมือนสิ่งส่วนใหญ่ใน CS "มันขึ้นอยู่กับ" ...
Steven A. Lowe

คำตอบ:


84

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

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

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

แก้ไข

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

แก้ไข 2

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

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

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


2
ทางเลือกในการรับคนที่คิดเกี่ยวกับการออกแบบการเรียน: ปล่อยให้พวกเขาเขียนซีอาร์ซีการ์ด (Class ชื่อความรับผิดชอบทำงานร่วมกัน) หากชั้นเรียนมีผู้ทำงานร่วมกันหรือความรับผิดชอบมากเกินไปก็เป็นไปได้ว่า SRP-ish ไม่เพียงพอ กล่าวอีกนัยหนึ่งข้อความทั้งหมดต้องพอดีกับบัตรดัชนีมิฉะนั้นมันจะทำมากเกินไป
Spoike

18
ฉันรู้ว่าเครื่องพ่นมันทำหน้าที่อะไร แต่คุณทำปลากับเสาอย่างไร?
R. Martinho Fernandes

13
+1 SOLID หมายถึงจุดจบไม่ใช่จุดจบในตัวมันเอง
B เซเว่น

1
+1: ฉันได้ถกเถียงกันมาก่อนหน้านี้ว่า "The Law of Demeter" ผิดชื่อมันควรจะเป็น "The Guide line of Demeter" สิ่งเหล่านี้ควรใช้ได้กับคุณคุณไม่ควรทำงานให้พวกเขา
ไบนารี Worrier

2
@EmmadKareem: มันเป็นความจริงที่ว่าวัตถุ DAO ควรจะมีคุณสมบัติหลายอย่าง แต่อีกครั้งมีหลายสิ่งที่คุณสามารถจัดกลุ่มเข้าด้วยกันในบางสิ่งที่ง่ายเหมือนCustomerคลาสและมีรหัสที่สามารถบำรุงรักษาได้มากกว่า ดูตัวอย่างได้ที่นี่: codemonkeyism.com/…
Spoike

32

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

หากคำตอบของคำถามแรกคือในทุกกรณี "ใช่" แต่คำถามที่สองคือ "ไม่ได้ปิด" ดังนั้นคุณต้องดูอีกครั้งว่าคุณใช้ SRP อย่างไร

ตัวอย่างเช่นหากเพิ่มหนึ่งฟิลด์ในตารางหมายความว่าคุณต้องเปลี่ยน DTO และคลาส validator และคลาส persistence และวัตถุโมเดลมุมมองเป็นต้นคุณได้สร้างปัญหาขึ้นมาแล้ว บางทีคุณควรคิดใหม่เกี่ยวกับวิธีการใช้ SRP

บางทีคุณอาจพูดว่าการเพิ่มเขตข้อมูลเป็นเหตุผลในการเปลี่ยนวัตถุลูกค้า แต่การเปลี่ยนชั้นความคงอยู่ (พูดจากไฟล์ XML ไปยังฐานข้อมูล) เป็นอีกเหตุผลหนึ่งที่เปลี่ยนวัตถุลูกค้า ดังนั้นคุณตัดสินใจที่จะสร้างออบเจกต์ CustomerPersistence เช่นกัน แต่ถ้าคุณทำเช่นนั้นการเพิ่มเขตข้อมูล STILL ต้องมีการเปลี่ยนแปลงวัตถุ CustomerPersisitence แล้วประเด็นคืออะไร? คุณยังคงได้รับวัตถุที่มีเหตุผลสองประการในการเปลี่ยนแปลง - มันไม่ใช่แค่ลูกค้าอีกต่อไป

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

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


+1 สำหรับ "การเปลี่ยนแปลงทุกอย่างมีผลกับชั้นหนึ่งหรือไม่"
dj18

ปัญหาที่เกี่ยวข้องที่ฉันไม่ได้กล่าวถึงคือถ้างานที่เชื่อมโยงกับเอนทิตีตรรกะหนึ่งได้แยกส่วนในคลาสที่แตกต่างกันมันอาจจำเป็นสำหรับรหัสที่จะเก็บการอ้างอิงไปยังวัตถุที่แตกต่างหลายรายการซึ่งเชื่อมโยงกับเอนทิตีเดียวกัน พิจารณาตัวอย่างเช่นเตาเผาที่มีฟังก์ชั่น "SetHeaterOutput" และ "MeasureTemperature" หากเตาเผาถูกแสดงโดย HeaterControl และ TemperatureSensor ที่เป็นอิสระวัตถุก็จะไม่มีสิ่งใดที่จะป้องกันไม่ให้วัตถุ TemperatureFeedbackSystem ถือการอ้างอิงไปยังเครื่องทำความร้อนของเตาเผาหนึ่งเครื่องและเซ็นเซอร์วัดอุณหภูมิของเตาเผาที่แตกต่างกัน
supercat

1
ถ้าแทนที่จะรวมฟังก์ชั่นเหล่านั้นลงในอินเทอร์เฟซ IKiln ซึ่งถูกนำไปใช้โดยวัตถุ Kiln ดังนั้น TemperatureFeedbackSystem จะต้องเก็บอ้างอิง IKiln เพียงรายการเดียวเท่านั้น หากจำเป็นต้องใช้เตาเผาที่มีเซ็นเซอร์อุณหภูมิหลังการขายที่เป็นอิสระใคร ๆ ก็สามารถใช้วัตถุ CompositeKiln ซึ่งคอนสตรัคเตอร์ยอมรับ IHeaterControl และ ITemperatureSensor
supercat

23

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

public class MyAwesomeClass {
    public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
      // Assign it all
    }
}

... ถ้าเช่นนั้นมันซับซ้อนเกินไปและคุณไม่ได้ติดตามSRPจริง ๆใช่ไหม? ฉันพนันได้เลยว่าถ้าคุณจดบันทึกสิ่งที่MyAwesomeClassเกิดขึ้นบนบัตร CRCมันจะไม่เหมาะกับบัตรดัชนีหรือคุณต้องเขียนด้วยตัวอักษรที่อ่านไม่ออกจริงๆ

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

TDD จะให้ตัวบ่งชี้ที่ดีเกี่ยวกับจำนวนชั้นเรียน โผงผาง; หากวิธีการทดสอบมีรหัสการตั้งค่าที่ใช้ตลอดไปในการเขียน (แม้ว่าคุณจะทำการทดสอบซ้ำ) คุณMyAwesomeClassอาจมีสิ่งที่ต้องทำมากเกินไป

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

  1. ระบุการกระทำทั้งหมด (หรือความรับผิดชอบ) ที่ชั้นเรียนของคุณทำด้วยการพึ่งพา
  2. จัดกลุ่มการกระทำตามการพึ่งพาที่เกี่ยวข้องอย่างใกล้ชิด
  3. Redelegate! I refactor แต่ละการกระทำที่ระบุให้แก่คลาสใหม่หรือ (สำคัญกว่า)

ตัวอย่างที่เป็นนามธรรมเกี่ยวกับความรับผิดชอบในการปรับโครงสร้าง

ให้Cเป็นระดับที่มีหลายอ้างอิงD1, D2, D3, D4ที่คุณต้อง refactor ใช้น้อย เมื่อเราระบุว่าวิธีใดที่Cเรียกใช้การพึ่งพาเราสามารถทำรายการง่าย ๆ

  • D1- performA(D2),performB()
  • D2 - performD(D1)
  • D3 - performE()
  • D4 - performF(D3)

เมื่อดูรายการเราจะเห็นว่าD1และD2มีความสัมพันธ์ซึ่งกันและกันเนื่องจากชั้นเรียนต้องการให้พวกเขาอยู่ด้วยกัน นอกจากนี้เรายังจะเห็นว่าความต้องการD4 D3ดังนั้นเราจึงมีสองกลุ่ม:

  • Group 1- D1<->D2
  • Group 2- D4->D3

การจัดกลุ่มเป็นตัวบ่งชี้ว่าชั้นเรียนมีความรับผิดชอบสองประการ

  1. Group 1- หนึ่งสำหรับการจัดการการโทรสองวัตถุที่ต้องการซึ่งกันและกัน บางทีคุณอาจให้ชั้นเรียนของคุณCขจัดความต้องการในการจัดการทั้งการพึ่งพาและปล่อยให้หนึ่งในนั้นจัดการการโทรเหล่านั้นแทน ในการจัดกลุ่มนี้ก็เป็นที่ชัดเจนว่าจะมีการอ้างอิงถึงD1D2
  2. Group 2- ความรับผิดชอบอื่น ๆ ต้องการวัตถุหนึ่งเพื่อเรียกอีกอย่างหนึ่ง D4จัดการไม่ได้D3แทนชั้นเรียนของคุณ? จากนั้นเราอาจจะสามารถกำจัดออกD3จากชั้นเรียนCโดยให้D4ทำการโทรแทน

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


แก้ไข:

ท่ามกลางความคิดเห็น@Emmad Karemพูดว่า:

"ถ้าคลาสของคุณมี 20 พารามิเตอร์ในตัวสร้างมันไม่ได้ดูเหมือนว่าทีมของคุณจะรู้ว่า SRP คืออะไรถ้าคุณมีคลาสที่ทำเพียงสิ่งเดียวมันจะมีการพึ่งพา 20 ครั้งได้อย่างไร" - ฉันคิดว่าถ้าคุณ มีคลาสลูกค้ามันไม่แปลกที่มีพารามิเตอร์ 20 ตัวในตัวสร้าง

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

public class Address {
    private String street1;
    //...

    private Zipcode zipcode;

    // easy to extend
    public bool isValid() {
        return zipcode.isValid();
    }
}

public class Zipcode {
    private string zipcode;
    public bool isValid() {
        // return regex match that zipcode contains numbers
    }
}

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


+1: คำตอบยอดเยี่ยม! การจัดกลุ่มเป็น IMO เป็นกลไกที่ทรงพลังมากเพราะคุณสามารถใช้การจัดกลุ่มซ้ำ การพูดอย่างคร่าวๆด้วยเลเยอร์ abstraction คุณสามารถจัดระเบียบรายการ 2 ^ n
Giorgio

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

3

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

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

ฉันกำลังทำงานบนระบบ Legacy ในตอนนี้และเราเริ่มลงเส้นทางที่คล้ายกันในตอนแรก สิ่งที่ได้ผลสำหรับเราคือการตัดสินใจร่วมกันเพื่อทำให้ดีที่สุดทั้งโลก - SOLID และ KISS (Keep It Simple Stupid) เราได้พูดคุยกันถึงการเปลี่ยนแปลงที่สำคัญในโครงสร้างรหัสและใช้สามัญสำนึกในการใช้หลักการพัฒนาที่หลากหลาย พวกเขายอดเยี่ยมเป็นแนวทางไม่ใช่ "กฎหมายของการพัฒนา S / W" ทีมไม่ได้เป็นเพียงเกี่ยวกับหัวหน้าทีม แต่เกี่ยวกับนักพัฒนาทั้งหมดในทีม สิ่งที่ได้ผลสำหรับฉันคือการทำให้ทุกคนอยู่ในห้องและสร้างแนวทางที่ใช้ร่วมกันทั้งทีมของคุณตกลงที่จะติดตาม

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


3

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

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

ด้านบนของ SRP มีรูปแบบการพัฒนาอื่น ๆ อีกมากมาย ในกรณีของคุณดูเหมือนว่า YAGNI จะขาดไปอย่างแน่นอน


3

คำตอบมากมายที่นี่ดีมาก แต่เน้นด้านเทคนิคของปัญหานี้ ฉันเพียงแค่เพิ่มว่าดูเหมือนว่าความพยายามของนักพัฒนาในการติดตามเสียง SRP เหมือนพวกเขาละเมิด SRP จริง

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

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

ขณะนี้มีปัญหาที่สองคือ "ผู้สนับสนุนที่รุนแรงของการพัฒนา SOLID" ไม่แน่ใจว่าคุณมีความสัมพันธ์ที่ดีกับนักพัฒนาซอฟต์แวร์นี้หรือไม่ดังนั้นความพยายามใด ๆ ที่จะโน้มน้าวให้เขา / เธอมีปัญหาใน codebase จะนิ่งงัน คุณจะต้องซ่อมแซมความสัมพันธ์เพื่อให้คุณสามารถอภิปรายปัญหาได้อย่างแท้จริง สิ่งที่ฉันอยากจะแนะนำคือเบียร์

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

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


-1

ฉันเห็นด้วยกับการตัดสินใจของทีมของคุณ [update = 2012.05.31] SRP โดยทั่วไปถือว่าเป็นความคิดที่ดี แต่ฉันเห็นด้วยอย่างยิ่งกับ @ Spoike -s คอมเม้นต์ว่าคอนสตรัคเตอร์ที่มีอาร์กิวเมนต์ของอินเตอร์เฟส 20 รายการอยู่ไกลมาก [/ update]:

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

  • หน่วยทดสอบได้ง่ายขึ้น / tdd (ทดสอบ srp-class ทีละตัว)
  • แต่ค่าใช้จ่ายของ
    • รหัสเริ่มต้น difficuilt มากขึ้นและการรวมและ
    • การแก้จุดบกพร่อง difficuilt เพิ่มเติม
    • การแตกแฟรกเมนต์ (= การกระจายของรหัสในหลาย ๆ ไฟล์ / ไดเรกทอรี)

ฉันเกรงว่าคุณจะไม่สามารถลดการโค้ดลงได้โดยไม่ต้องเสียสละ srp

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

   class MySrpClass {
      MySrpClass(Interface1 parm1, Interface2 param2, .... Interface20 param2) {
      }
   } 

   class MySyntaxSugarClass : MySrpClass {
      MySyntaxSugarClass() {
         super(new MyInterface1Implementation(), new MyImpl2(), ....)
      }
   }

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