หลักการความรับผิดชอบเดี่ยว - ฉันใช้มากเกินไปหรือไม่


13

สำหรับการอ้างอิง - http://en.wikipedia.org/wiki/Single_responsibility_principle

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

  • ดูรายการบัญชีแยกประเภทที่มีอยู่ในรูปแบบตาราง
  • สร้างรายการบัญชีแยกประเภทใหม่โดยใช้ปุ่มสร้าง
  • คลิกที่รายการบัญชีแยกประเภทในตาราง (กล่าวถึงในตัวชี้แรก) และดูรายละเอียดในหน้าถัดไป คุณสามารถลบรายการบัญชีแยกประเภทในหน้านี้

(มีการดำเนินการ / การตรวจสอบเพิ่มเติมอีกสองสามครั้งในแต่ละหน้า แต่เพื่อความกระชับฉันจะ จำกัด เฉพาะสิ่งนี้

ดังนั้นฉันตัดสินใจที่จะสร้างสามชั้นที่แตกต่างกัน -

  • LedgerLandingPage
  • CreateNewLedgerEntryPage
  • ViewLedgerEntryPage

คลาสเหล่านี้มีบริการที่สามารถทำได้ในหน้าเหล่านั้นและการทดสอบซีลีเนียมใช้คลาสเหล่านี้เพื่อนำแอปพลิเคชันไปสู่สถานะที่ฉันสามารถยืนยันได้

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


2
IMHO ไม่มีประเด็นใดที่จะพยายามตอบคำถามของคุณโดยทั่วไปโดยไม่ทราบว่ามีสองสิ่ง: ชั้นเรียนเหล่านี้มีขนาดเท่าใด (โดยวิธีการนับและ LOC) และคาดว่าจะมีขนาดใหญ่ขึ้นและมีความซับซ้อนมากขึ้นในอนาคตอันใกล้
PéterTörök

2
10 เมธอดแต่ละรายการเป็น IMHO ข้อบ่งชี้ที่ชัดเจนว่าจะไม่ใส่รหัสลงในคลาสเดียวที่มี 30 วิธี
Doc Brown

6
นี่คือขอบเขตของเส้นขอบ: คลาส 100 LOC และ 10 วิธีไม่เล็กเกินไปและหนึ่งใน 300 LOC ไม่ใหญ่เกินไป อย่างไรก็ตาม 30 วิธีในการเรียนเสียงเดียวมากเกินไปสำหรับฉัน โดยรวมแล้วฉันมักจะเห็นด้วยกับ @Doc เนื่องจากมีความเสี่ยงน้อยกว่าที่จะมีชั้นเรียนเล็ก ๆ น้อยกว่าชั้นเรียนที่มีน้ำหนักเกินเดียว โดยเฉพาะอย่างยิ่งการพิจารณาว่าชั้นเรียนตามธรรมชาติมีแนวโน้มที่จะเพิ่มน้ำหนักในช่วงเวลา ...
PéterTörök

1
@ PéterTörök - ฉันเห็นด้วยเว้นแต่ว่ารหัสสามารถถูก refactored เป็นหนึ่งคลาสที่สามารถใช้งานได้อย่างสังหรณ์ใจที่ reuses รหัสแทนที่จะทำซ้ำฟังก์ชั่นเช่นฉันคาดหวังว่า OP มี
SoylentGray

1
บางทีการใช้ MVC จะทำให้สิ่งนี้หมดไป: สามมุมมอง, หนึ่งโมเดล (Ledger) และอาจเป็นสามคอนโทรลเลอร์
วินไคลน์

คำตอบ:


19

อ้างถึง @YannisRizos ที่นี่ :

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

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

LedgerLandingPage

CreateNewLedgerEntryPage

ViewLedgerEntryPage

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

หากคุณมีฟังก์ชั่นการใช้งานร่วมกันระหว่าง 3 คลาสเหล่านั้นพวกมันจะอยู่ในคลาสพื้นฐานทั่วไปLedgerคลาสนั้น (ฉันถือว่าคุณมีอย่างน้อยหนึ่งสำหรับการดำเนินการ CRUD) หรือในคลาสผู้ช่วยที่แยกต่างหาก

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


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

2
"คลาสที่เล็กกว่าดีกว่าที่จะรักษาไว้ในเกือบทุกกรณีที่ฉันนึกได้" ฉันไม่คิดว่าแม้แต่รหัสสะอาดจะไปไกลขนาดนั้น คุณจะเถียงว่าในรูปแบบ MVC ควรมีคอนโทรลเลอร์หนึ่งตัวต่อหน้า (หรือกรณีใช้ตามที่คุณวางไว้) ตัวอย่างเช่น? ใน Refactoring ฟาวเลอร์มีโค้ดสองกลิ่นด้วยกัน: หนึ่งหมายถึงมีเหตุผลหลายประการในการเปลี่ยนคลาส (SRP) และอีกหนึ่งหมายถึงต้องแก้ไขคลาสจำนวนมากสำหรับการเปลี่ยนแปลงครั้งเดียว
สาธารณรัฐประชาธิปไตยประชาชนลาว

@pdr, สถานะ OP ไม่มีฟังก์ชั่นทั่วไประหว่างคลาสเหล่านี้ดังนั้น (สำหรับฉัน) มันเป็นเรื่องยากที่จะจินตนาการถึงสถานการณ์เมื่อคุณต้องการสัมผัสมากกว่าหนึ่งเพื่อทำการเปลี่ยนแปลงเดียว
PéterTörök

@pdr: หากคุณมีคลาสมากเกินไปที่จะแก้ไขสำหรับการเปลี่ยนแปลงเพียงครั้งเดียวส่วนใหญ่จะเป็นสัญญาณที่คุณไม่ได้ปรับการใช้งานทั่วไปลงในที่แยกต่างหาก แต่ในตัวอย่างข้างต้นสิ่งนี้อาจนำไปสู่คลาสที่สี่และไม่ใช่แค่คลาสเดียว
Doc Brown

2
@pdr: BTW คุณอ่าน "รหัสสะอาด" จริงหรือไม่? บ็อบมาร์ตินไปไกลมากในการแบ่งรหัสเป็นหน่วยที่เล็กลง
Doc Brown

11

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

@Karpie เขียนไว้ในคำตอบก่อนหน้านี้ :

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

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

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

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


1
เพื่อความเข้าใจของฉัน SRP ไม่ใช่รูปแบบการออกแบบจริงๆ
Doc Brown

@DocBrown แน่นอนไม่ได้เป็นรูปแบบการออกแบบ แต่การอภิปรายเกี่ยวกับคำถามที่มีความเกี่ยวข้องอย่างมาก ... ดีจับแม้ว่าผมได้ปรับปรุงคำตอบ
Yannis

4

ตรวจสอบชั้นเรียนเหล่านี้:

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

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

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

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


มีสิ่งต่าง ๆ เช่นSRP มากเกินไป : หากคุณต้องการคลาสโหลเพื่ออ่านเนื้อหาของไฟล์มันค่อนข้างปลอดภัยที่จะสมมติว่าคุณกำลังทำอยู่

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


ฉันเดาว่าฉันไม่สามารถอธิบายความรับผิดชอบของชั้นเรียนเหล่านี้ได้ดีกว่าที่คุณทำ
Tarun

2

คลาสเหล่านั้นมีเหตุผลเดียวที่จะเปลี่ยนหรือไม่?

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

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

ประเด็นทั้งหมดคือ:

  • จับตาดูไดรเวอร์เพื่อการเปลี่ยนแปลง
  • เลือกการออกแบบที่ยืดหยุ่นซึ่งสามารถนำกลับมาใช้ใหม่ได้
  • พร้อมที่จะ refactor รหัส

หากคุณมีความคิดนี้คุณจะสร้างรหัสโดยไม่ต้องกลัวการเก็งกำไร / ออกแบบทางวิศวกรรม

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


1

มีสามเหตุผลในการเรียกใช้ SRP เป็นเหตุผลในการแบ่งคลาส:

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

มีสามเหตุผลในการปฏิเสธที่จะแยกชั้น:

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

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

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


ดีที่จะเห็นมุมมองที่แตกต่าง
Tarun

0

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


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

0

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

มีบางกรณีที่จะซ่อนสถานะในชั้นเรียนสำหรับการแก้ไขบัญชีแยกประเภท

ไม่ว่าคุณจะใช้ SRP ในทางที่ผิดหรือไม่ก็ตามคุณกำลังทำอะไรที่เป็นพื้นฐานมากกว่า: คุณกำลังใช้สิ่งที่เป็นนามธรรม มันเป็นปัญหาทั่วไปที่เกิดจากความเชื่อในตำนาน OO เป็นกระบวนทัศน์การพัฒนาอย่างมีสติ

การเขียนโปรแกรมเป็นรูปธรรม 90%: การใช้ข้อมูลนามธรรมควรจะหายากและออกแบบอย่างระมัดระวัง ในทางตรงกันข้ามฟังก์ชั่นนามธรรม, ควรจะเป็นเรื่องธรรมดามากขึ้นเนื่องจากรายละเอียดภาษาและตัวเลือกของอัลกอริทึมจะต้องแยกออกจากความหมายของการดำเนินงาน


-1

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


2
สำหรับฉันคลาส '... manager' มักแสดงให้เห็นว่าคุณไม่ต้องกังวลที่จะทำลายมันลงไปอีก การจัดการชั้นเรียนคืออะไร? การนำเสนอความเพียร?
sebastiangeiger

-1 การใส่ 3 กรณีการใช้งานหรือมากกว่าลงใน 1 คลาสเป็นสิ่งที่ SRP พยายามหลีกเลี่ยง - และด้วยเหตุผลที่ดี
Doc Brown

@sebastiangeiger แล้ว OP สามคลาสทำอะไรกัน การนำเสนอหรือวิริยะ?
sevenseacat

@Karpie ฉันหวังว่าจะนำเสนอโดยสุจริต ได้รับแล้วตัวอย่างไม่ดีจริงๆ แต่ฉันคาดหวังว่า '... หน้า' ในเว็บไซต์กำลังทำสิ่งที่คล้ายกับมุมมอง
sebastiangeiger

2
ในความเป็นจริงคุณต้องการเพียงหนึ่งคลาสสำหรับแอปพลิเคชันทั้งหมดโดยมีความรับผิดชอบเพียงอย่างเดียวในการทำให้ผู้ใช้มีความสุข;
24945 PéterTörök
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.