ฉันทำให้ชั้นเรียนของฉันละเอียดเกินไปหรือไม่? หลักการความรับผิดชอบเดี่ยวควรถูกนำไปใช้อย่างไร


9

ฉันเขียนโค้ดจำนวนมากที่เกี่ยวข้องกับสามขั้นตอนพื้นฐาน

  1. รับข้อมูลจากที่อื่น
  2. แปลงข้อมูลนั้น
  3. วางข้อมูลนั้นไว้ที่ใดที่หนึ่ง

ปกติแล้วฉันจะใช้คลาสสามประเภท - โดยได้แรงบันดาลใจจากรูปแบบการออกแบบที่เกี่ยวข้อง

  1. โรงงาน - เพื่อสร้างวัตถุจากทรัพยากรบางอย่าง
  2. ผู้ไกล่เกลี่ย - เพื่อใช้โรงงานทำการเปลี่ยนแปลงจากนั้นใช้ผู้บังคับบัญชา
  3. ผู้บังคับบัญชา - เพื่อใส่ข้อมูลนั้นไปที่อื่น

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

ที่ฉันต้องดิ้นรนคือเมื่อฉันมาทดสอบฉันลงเอยด้วยการทดสอบที่รัดกุม ตัวอย่างเช่น;

  • โรงงาน - อ่านไฟล์จากดิสก์
  • Commander - เขียนไฟล์ลงดิสก์

ฉันไม่สามารถทำการทดสอบได้หากไม่มีอันอื่น ฉันสามารถเขียนรหัส 'ทดสอบ' เพิ่มเติมเพื่อทำดิสก์อ่าน / เขียนได้ แต่จากนั้นฉันก็ทำซ้ำตัวเอง

เมื่อดูที่. Net คลาสFileจะใช้วิธีการที่แตกต่างกันโดยจะรวมความรับผิดชอบของโรงงานและผู้บังคับบัญชาเข้าด้วยกัน มันมีฟังก์ชั่นสำหรับการสร้างลบอยู่และอ่านทั้งหมดในที่เดียว

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

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



6
Looking at .Net, the File class takes a different approach, it combines the responsibilities (of my) factory and commander together. It has functions for Create, Delete, Exists, and Read all in one place.- โปรดทราบว่าคุณกำลังทำให้ "ความรับผิดชอบ" กับ "สิ่งที่ต้องทำ" ความรับผิดชอบเป็นเหมือน "ประเด็นที่น่าเป็นห่วง" ความรับผิดชอบของคลาสไฟล์กำลังดำเนินการกับไฟล์
Robert Harvey

1
ดูเหมือนว่าคุณจะอยู่ในสภาพดี สิ่งที่คุณต้องมีก็คือผู้ไกล่เกลี่ยทดสอบ (หรืออีกรายการหนึ่งสำหรับการแปลงทุกประเภทถ้าคุณชอบแบบนั้น) ผู้ไกล่เกลี่ยทดสอบอาจอ่านไฟล์เพื่อตรวจสอบความถูกต้องโดยใช้คลาสไฟล์ของ. net ไม่มีปัญหากับเรื่องนี้จากมุมมองของ SOLID
Martin Maat

1
ตามที่กล่าวถึงโดย @Robert Harvey SRP มีชื่อเส็งเคร็งเพราะมันไม่ได้เกี่ยวกับความรับผิดชอบ มันเกี่ยวกับ "การห่อหุ้มและการทำให้เป็นนามธรรมในพื้นที่ที่มีความกังวล / ยุ่งยากยาก ๆ ที่อาจเปลี่ยนแปลง" ฉันเดา STDACMC ยาวเกินไป :-) ที่กล่าวว่าฉันคิดว่าการแบ่งของคุณออกเป็นสามส่วนนั้นสมเหตุสมผล
user949300

1
จุดสำคัญในFileไลบรารีของคุณจาก C # คือสำหรับทุกสิ่งที่เรารู้ว่าFileคลาสนั้นอาจเป็นซุ้มทำการวางไฟล์ทั้งหมดไว้ในที่เดียว - เข้าไปในชั้นเรียน แต่อาจใช้การอ่าน / เขียนที่คล้ายกัน จริงๆแล้วมีตรรกะที่ซับซ้อนมากขึ้นสำหรับการจัดการไฟล์ คลาสดังกล่าว (the File) จะยังคงยึด SRP ไว้เพราะกระบวนการทำงานกับระบบไฟล์จะถูกแยกออกจากเลเยอร์อื่น ไม่ได้บอกว่าเป็นกรณี แต่อาจเป็นไปได้ :)
Andy

คำตอบ:


5

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

การแยกความรับผิดชอบคำสั่งการค้นหา

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

คุณได้แสดงความกังวลเกี่ยวกับการทดสอบแล้ว ฉันไม่คิดว่าการติดตาม CQRS ไม่ทำให้เกิดการเขียนรหัสที่ทดสอบได้ คุณอาจเพียงแค่ติดตาม CQRS ด้วยวิธีที่ทำให้รหัสของคุณไม่สามารถทดสอบได้

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

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

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

หากคุณใช้ให้ระวังคำเตือนนี้:

โดยเฉพาะ CQRS ควรใช้เฉพาะบางส่วนของระบบ (BoundedContext ใน DDD lingo) และไม่ใช่ระบบโดยรวม ในการคิดแบบนี้บริบทที่ถูกผูกมัดแต่ละข้อต้องมีการตัดสินใจของตัวเองว่าควรจะทำแบบจำลองอย่างไร

Martin Flowler: CQRS


ไม่เคยเห็น CQRS ที่น่าสนใจมาก่อน รหัสทดสอบได้นี่เป็นเรื่องเกี่ยวกับการพยายามหาวิธีที่ดีกว่า ฉันใช้ mocks และการฉีดพึ่งพาเมื่อฉันทำได้ (ซึ่งฉันคิดว่าเป็นสิ่งที่คุณอ้างถึง)
James Wood

ครั้งแรกที่อ่านเกี่ยวกับเรื่องนี้ฉันได้ระบุสิ่งที่คล้ายกันผ่านแอปพลิเคชันของฉัน: จัดการกับการค้นหาที่ยืดหยุ่นฟิลด์ที่กรองได้ / คัดแยกได้หลายประเภท (Java / JPA) เป็นอาการปวดหัวและนำไปสู่โค้ดสำเร็จรูปจำนวนหนึ่งเว้นแต่ว่าคุณสร้างเครื่องมือค้นหาพื้นฐาน จะจัดการสิ่งนี้ให้คุณ (ฉันใช้ rsql-jpa) แม้ว่าฉันจะมีโมเดลเดียวกัน (พูดเหมือนกันทั้ง JPA Entities สำหรับทั้งสอง) การค้นหาจะถูกดึงไปยังบริการทั่วไปโดยเฉพาะและเลเยอร์โมเดลไม่ต้องจัดการอีกต่อไป
Walfrat

3

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

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

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

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

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

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


2

โดยทั่วไปคุณมีความคิดที่ถูกต้อง

รับข้อมูลจากที่อื่น แปลงข้อมูลนั้น วางข้อมูลนั้นไว้ที่ใดที่หนึ่ง

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

interface Reader[T] {
    def read(): T
}

interface Transformer[T, U] {
    def transform(t: T): U
}

interface Writer[T] {
    def write(t: T): void
}

จากนั้นโปรแกรมสามารถแสดงเป็น:

def program[T, U](reader: Reader[T], 
                  transformer: Transformer[T, U], 
                  writer: Writer[U]): void =
    writer.write(transformer.transform(reader.read()))

สิ่งนี้นำไปสู่การเพิ่มจำนวนชั้นเรียน

ฉันไม่คิดว่านี่เป็นปัญหา IMO มีขนาดเล็กจำนวนมากชั้นเรียนที่สามารถทดสอบได้ดีกว่าชั้นเรียนขนาดใหญ่ที่มีความเหนียวน้อย

ที่ฉันต้องดิ้นรนคือเมื่อฉันมาทดสอบฉันลงเอยด้วยการทดสอบที่รัดกุม ฉันไม่สามารถทำการทดสอบได้หากไม่มีอันอื่น

แต่ละชิ้นควรทดสอบได้อย่างอิสระ ตามโมเดลด้านบนคุณสามารถแสดงการอ่าน / เขียนไปยังไฟล์ได้ดังนี้:

class FileReader(fileName: String) implements Reader[String] {
    override read(): String = // read file into string
}

class FileWriter(fileName: String) implements Writer[String] {
    override write(str: String) = // write str to file
}

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

class JsonParser implements Transformer[String, Json] {
    override transform(str: String): Json = // parse as json
}

จากนั้นคุณสามารถแปลงเป็นวัตถุที่เหมาะสม:

class FooParser implements Transformer[Json, Foo] {
    override transform(json: Json): Foo = // ...
}

แต่ละเหล่านี้สามารถทดสอบได้อย่างอิสระ คุณยังสามารถทดสอบหน่วยprogramข้างต้นโดยเยาะเย้ยreader, และtransformerwriter


นั่นเป็นสิ่งที่ฉันอยู่ตอนนี้ ฉันสามารถทดสอบแต่ละฟังก์ชั่นเป็นรายบุคคลอย่างไรก็ตามโดยการทดสอบพวกเขาพวกเขากลายเป็นคู่ เช่นสำหรับการทดสอบ FileWriter แล้วมีอย่างอื่นต้องอ่านสิ่งที่เขียนวิธีแก้ปัญหาที่ชัดเจนคือการใช้ FileReader Fwiw ผู้ไกล่เกลี่ยมักจะทำอย่างอื่นเช่นใช้ตรรกะทางธุรกิจหรืออาจเป็นตัวแทนจากฟังก์ชั่นหลักฟังก์ชั่นหลัก
James Wood

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

โปรดดูstackoverflow.com/questions/1087351/ด้วยอาจช่วยให้การทดสอบของคุณดีขึ้น
ซามูเอล

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

@EmersonCardoso; ฉันได้ทำให้สถานการณ์ในคำถามของฉันค่อนข้างง่ายขึ้น ในขณะที่ผู้ไกล่เกลี่ยของฉันบางคนค่อนข้างง่าย แต่บางคนก็ซับซ้อนกว่าและมักจะใช้โรงงาน / ผู้บัญชาการหลายคน ฉันพยายามหลีกเลี่ยงรายละเอียดของสถานการณ์เดียวฉันสนใจสถาปัตยกรรมการออกแบบระดับสูงที่สามารถนำไปใช้กับหลายสถานการณ์ได้
James Wood

2

ท้ายที่สุดฉันจะทดสอบอย่างแน่นหนา ตัวอย่างเช่น;

  • โรงงาน - อ่านไฟล์จากดิสก์
  • Commander - เขียนไฟล์ลงดิสก์

ดังนั้นการมุ่งเน้นที่นี่เป็นในสิ่งที่พวกเขาร่วมกันมีเพศสัมพันธ์ คุณส่งผ่านวัตถุระหว่างทั้งสอง (เช่น a File) แล้วมันเป็นไฟล์ที่พวกเขากำลังเชื่อมต่อกับไม่ใช่กันและกัน

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

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

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

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


ในตัวอย่างนั้นสิ่งที่เชื่อมโยงเข้าด้วยกันคือทรัพยากร - เช่นดิสก์ระบบ มิฉะนั้นจะไม่มีการโต้ตอบระหว่างสองคลาส
James Wood

1

รับข้อมูลจากที่อื่น แปลงข้อมูลนั้น วางข้อมูลนั้นไว้ที่ใดที่หนึ่ง

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

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

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

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

class FinanceTransaction
{
    private $id;
    private $storage;

    public function __construct(UUID $id, DataStorage $storage)
    {
        $this->id = $id;
        $this->storage = $storage;
    }

    public function perform(
        Order $order,
        Customer $customer,
        Merchant $merchant
    )
    {
        if ($order->isExpired()) {
            throw new Exception('Order expired');
        }

        if ($customer->canNotPurchase($order)) {
            throw new Exception('It is not legal to purchase this kind of stuff by this customer');
        }

        $this->storage->save($this->id, $order, $customer, $merchant);
    }
}

(new FinanceTransaction())
    ->perform(
        new Order(
            new Product(
                $_POST['product_id']
            ),
            new Card(
                new CardNumber(
                    $_POST['card_number'],
                    $_POST['cvv'],
                    $_POST['expires_at']
                )
            )
        ),
        new Customer(
            new Name(
                $_POST['customer_name']
            ),
            new Age(
                $_POST['age']
            )
        ),
        new Merchant(
            new MerchantId($_POST['merchant_id'])
        )
    )
;

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


1

ที่ฉันต้องดิ้นรนคือเมื่อฉันมาทดสอบฉันลงเอยด้วยการทดสอบที่รัดกุม ตัวอย่างเช่น;

  • โรงงาน - อ่านไฟล์จากดิสก์
  • Commander - เขียนไฟล์ลงดิสก์

ระวัง abstractions ที่รั่วไหลเมื่อทำงานกับระบบไฟล์ - ฉันเห็นมันถูกละเลยบ่อยเกินไปและมีอาการที่คุณอธิบาย

หากคลาสดำเนินการกับข้อมูลที่มาจาก / เข้าไปในไฟล์เหล่านี้ระบบไฟล์จะกลายเป็นรายละเอียดการนำไปใช้ (I / O) และควรแยกออกจากกัน คลาสเหล่านี้ (factory / commander / mediator) ไม่ควรรับรู้ถึงระบบไฟล์ยกเว้นว่างานเพียงอย่างเดียวคือการจัดเก็บ / อ่านข้อมูลที่ให้ไว้ คลาสที่จัดการกับระบบไฟล์ควร encapsulate พารามิเตอร์เฉพาะบริบทเช่นพา ธ (อาจถูกส่งผ่านคอนสตรัคเตอร์) ดังนั้นอินเทอร์เฟซไม่ได้เปิดเผยลักษณะของมัน (คำว่า "ไฟล์" ในชื่ออินเตอร์เฟสเป็นกลิ่นส่วนใหญ่)


"คลาสเหล่านี้ (โรงงาน / ผู้บังคับบัญชา / ผู้ไกล่เกลี่ย) ไม่ควรรับรู้ถึงระบบไฟล์เว้นแต่งานเดียวของพวกเขาคือการจัดเก็บ / อ่านข้อมูลที่ให้ไว้" ในตัวอย่างนี้คือสิ่งที่พวกเขาทำ
James Wood

0

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

หากต้องการทำขั้นตอนต่อไปคุณควรสร้างอินเทอร์เฟซสำหรับคลาส Factory, Mediator และ Commander จากนั้นคุณสามารถใช้เวอร์ชันคลาสที่เยาะเย้ยออกมาเมื่อเขียนการทดสอบหน่วยของคุณสำหรับการใช้งานที่เป็นรูปธรรมของผู้อื่น ด้วย mocks คุณสามารถตรวจสอบว่าวิธีการที่เรียกว่าในลำดับที่ถูกต้องและด้วยพารามิเตอร์ที่ถูกต้องและรหัสภายใต้การทดสอบการทำงานอย่างถูกต้องกับค่าตอบแทนที่แตกต่างกัน

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


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