หลีกเลี่ยง getters และ setters โดยแสดงข้อมูลผู้ใช้


10

พื้นหลัง

ฉันกำลังอ่าน "หนังสือรหัสสะอาด" และใน paralel ฉันกำลังทำงานกับวัตถุแบบคาลินิเทนิกกะตะเช่นบัญชีธนาคารและฉันติดอยู่กับกฎนั้น:

กฎข้อที่ 9 ของวัตถุแบบคาลิสฟีนิกคือเราไม่ใช้ทะลุหรือเซ็ตเตอร์

ดูเหมือนจะสนุกมากและฉันเห็นด้วยกับหลักการนี้ ยิ่งไปกว่านั้นที่หน้า 98-99 ของ Clean Code ผู้เขียนอธิบายว่า getters / setters ทำลายสิ่งที่เป็นนามธรรมและเราไม่ต้องถามวัตถุของเรา แต่เราต้องบอกวัตถุของเรา

มันทำให้รู้สึกที่สมบูรณ์แบบในใจของฉันและฉันเห็นด้วยกับหลักการนี้อย่างเต็มที่ ปัญหาเกิดขึ้นในทางปฏิบัติ

บริบท

ตัวอย่างเช่นฉันมีแอปพลิเคชันที่ฉันจะต้องแสดงรายชื่อผู้ใช้บางส่วนและเพื่อแสดงรายละเอียดผู้ใช้

ผู้ใช้ของฉันประกอบด้วย:

-> Name
   --> Firstname --> String
   --> Lastname --> String
-> PostalAddress
   --> Street --> String
   --> PostalCode --> String

ปัญหา

ฉันจะทำอย่างไรหรือฉันจะทำอย่างไรเพื่อหลีกเลี่ยง getters เมื่อฉันเพียงต้องการแสดงข้อมูลอย่างง่าย ๆ ( และฉันต้องยืนยันว่าฉันไม่ต้องการการดำเนินการเพิ่มเติมในฟิลด์นั้น ) เพื่อแสดงค่าชื่อในแบบง่าย ๆ ( สุ่ม) รองรับเอาต์พุตหรือไม่

เกิดอะไรขึ้นในใจฉัน

ทางออกหนึ่งคือ:

user.getName().getFirstName().getStringValue()

ซึ่งเป็นสิ่งที่น่ากลัวอย่างสิ้นเชิงทำลายกฎของวัตถุที่มีความสงบและกฎของ Demeter

อีกคนหนึ่งจะเป็นสิ่งที่ชอบ:

String firstName = user.provideFirstnameForOutput();
// That would have called in the user object =>
String firstName = name.provideFirstnameForOutput();
// That would have called in the name object =>
String firstName = firstname.provideFirstnameForOutput();

แต่ฉันรู้สึกไม่สบายใจกับวิธีแก้ปัญหานี้ซึ่งดูเหมือนจะเป็น "access order ที่สูงกว่า" เท่านั้นเช่นการข้ามทะลุมาตรฐาน / setter ด้วยวิธีการที่มีจุดประสงค์เพื่อให้ตรงกับกฎหมาย Demeter ...

ความคิดใด ๆ

คำตอบ:


17

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

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

เพื่อหลีกเลี่ยง getters และ setters คุณสามารถออกแบบโซลูชันที่คล้ายกับreactive programmingโดยการนำระบบการรายงานไปใช้ผ่านเอนทิตีที่สังเกตได้ แต่สิ่งนี้มีความซับซ้อนอย่างมากในสถาปัตยกรรมและไม่เหมาะสมกับ CRUD ในระดับแอปพลิเคชันของคุณ

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

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

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


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

@ Margin สวยมากใช่
Andy

ทุกเสียงซ้อนนี้สิ่งที่ดีจริงๆและเรียบขอบคุณสำหรับคำตอบ;)
mfrachet

2

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

String lastName = user.formatDescription("$(Lastname)");
String fullName = user.formatDescription("$(Lastname), $(Firstname)");
String abbreviated = user.formatDescription("$(FnAbb). $(LnAbb).");

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

String accountName = user.formatDescription("$(firstname).$(lastname)");

นอกจากนี้คุณยังสามารถกำหนดรูปแบบที่ซับซ้อนที่ใช้กันทั่วไปซึ่งอาจเป็นหนึ่งครั้งดังนั้นคุณสามารถพูดได้เช่น

String fullAddress = user.formatDescription(User.kAddressFormat);

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

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


5
อย่างไรก็ตามนี่ดูเหมือนว่าการละเมิด SRP สำหรับฉัน มันเป็นความรับผิดชอบของUserวัตถุที่จะแยกวิเคราะห์และแปล / แปลภาษาเทมเพลตหรือไม่?
Jörg W Mittag

@ JörgWMittagไม่จำเป็น ดังที่ฉันได้กล่าวไปแล้วหลักการของ KISS นั้นสำคัญกว่า และฉันก็บอกว่าไม่มีที่ใดในUserห้องเรียนที่จะต้องใช้ฟังก์ชั่นนี้เอง หากฉันมีเพียงสองคลาสที่ต้องการฟังก์ชั่นดังกล่าวคุณสามารถวางเดิมพันให้ฉันเพื่อแยกกลไกการเปลี่ยนเทมเพลตออกเป็นคลาสของตัวเอง นี่เป็นตัวอย่างของวิธีที่คุณสามารถยกสิ่งที่เป็นนามธรรมUserให้อยู่ในระดับที่เป็นจริงมากกว่าแค่ที่เก็บข้อมูล และผมเชื่อว่าเป็นสิ่งที่หลีกเลี่ยง accessors คือทั้งหมดที่เกี่ยวกับ: ทำ OOP แทนการจัดการพวงของstructs
cmaster - คืนสถานะโมนิก้า

คุณรู้หรือไม่ว่า KISS นั้นมีวิสัยทัศน์และวัตถุประสงค์ของ SRP โดยสิ้นเชิง? จูบไม่ได้บอกอะไรคุณเกี่ยวกับสิ่งที่ต้องทำ และ "Simple" สำหรับคุณคืออะไรอาจไม่ "ง่าย" สำหรับฉัน ฉันเห็นความแตกต่างที่แท้จริงของคุณภาพถ้าโต้เถียงกับ KISS หรือ SRP
oopexpert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.