JavaRush /จาวาบล็อก /Random-TH /คอฟฟี่เบรค #85 บทเรียน Java สามบทเรียนที่ฉันได้เรียนรู้อย...

คอฟฟี่เบรค #85 บทเรียน Java สามบทเรียนที่ฉันได้เรียนรู้อย่างยากลำบาก วิธีใช้หลักการ SOLID ในโค้ด

เผยแพร่ในกลุ่ม

บทเรียน Java สามบทเรียนที่ฉันได้เรียนรู้อย่างยากลำบาก

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

1. แลมบ์ดาอาจทำให้เกิดปัญหาได้

Lambdas มักจะมีโค้ดยาวเกิน 4 บรรทัดและมีขนาดใหญ่เกินกว่าที่คาดไว้ นี่เป็นภาระของหน่วยความจำในการทำงาน คุณจำเป็นต้องเปลี่ยนตัวแปรจาก lambda หรือไม่? คุณไม่สามารถทำอย่างนั้นได้ ทำไม หากแลมบ์ดาสามารถเข้าถึงตัวแปรไซต์การโทร ปัญหาเธรดอาจเกิดขึ้นได้ ดังนั้นคุณไม่สามารถเปลี่ยนตัวแปรจากแลมบ์ดาได้ แต่เส้นทางแห่งความสุขในแลมบ์ดาก็ใช้ได้ดี หลังจากรันไทม์ล้มเหลว คุณจะได้รับคำตอบนี้:
at [CLASS].lambda$null$2([CLASS].java:85)
at [CLASS]$$Lambda$64/730559617.accept(Unknown Source)
การติดตามสแต็กแลมบ์ดาเป็นเรื่องยาก ชื่อเหล่านี้สร้างความสับสนและยากต่อการติดตามและแก้ไขข้อบกพร่อง แลมบ์ดามากขึ้น = มีการติดตามสแต็กมากขึ้น วิธีที่ดีที่สุดในการดีบักแลมบ์ดาคืออะไร ใช้ผลลัพธ์ระดับกลาง
map(elem -> {
 int result = elem.getResult();
 return result;
});
อีกวิธีที่ดีคือการใช้เทคนิคการดีบัก IntelliJ ขั้นสูง ใช้ TAB เพื่อเลือกโค้ดที่คุณต้องการแก้ไขและรวมเข้ากับผลลัพธ์ระดับกลาง “เมื่อเราหยุดที่บรรทัดที่มี lambda ถ้าเรากด F7 (ก้าวเข้าไป) IntelliJ จะเน้นส่วนที่จำเป็นต้องแก้ไขจุดบกพร่อง เราสามารถเปลี่ยนบล็อกเป็นการแก้ไขข้อบกพร่องโดยใช้ Tab และเมื่อเราตัดสินใจได้แล้ว ให้กด F7 อีกครั้ง” จะเข้าถึงตัวแปรไซต์การโทรจากแลมบ์ดาได้อย่างไร คุณสามารถเข้าถึงได้เฉพาะตัวแปรสุดท้ายหรือตัวแปรสุดท้ายเท่านั้น คุณต้องสร้าง wrapper รอบๆ ตัวแปรตำแหน่งการโทร ไม่ว่าจะใช้AtomicTypeหรือใช้ประเภทของคุณเอง คุณสามารถเปลี่ยน wrapper ตัวแปรที่สร้างขึ้นด้วย lambda วิธีแก้ปัญหาการติดตามสแต็ก? ใช้ฟังก์ชันที่มีชื่อ ด้วยวิธีนี้ คุณสามารถค้นหาโค้ดที่รับผิดชอบ ตรวจสอบตรรกะ และแก้ไขปัญหาได้อย่างรวดเร็ว ใช้ฟังก์ชันที่มีชื่อเพื่อตัดการติดตามสแต็กที่เป็นความลับ แลมบ์ดาตัวเดียวกันซ้ำแล้วซ้ำอีกหรือไม่? วางไว้ในฟังก์ชันที่มีชื่อ คุณจะมีจุดอ้างอิงเพียงจุดเดียว แลมบ์ดาแต่ละตัวได้รับฟังก์ชันที่สร้างขึ้น ซึ่งทำให้ยากต่อการติดตาม
lambda$yourNamedFunction
lambda$0
ฟังก์ชันที่มีชื่อช่วยแก้ปัญหาอื่น แลมบ์ดาตัวใหญ่ ฟังก์ชันที่มีชื่อจะแยกแลมบ์ดาขนาดใหญ่ สร้างโค้ดขนาดเล็ก และสร้างฟังก์ชันที่เสียบได้
.map(this::namedFunc1).filter(this::namedFilter1).map(this::namedFunc2)

2. ปัญหาเกี่ยวกับรายการ

คุณต้องทำงานกับรายการ ( Lists ) คุณต้องมีHashMapสำหรับข้อมูล สำหรับบทบาท คุณจะต้องมีTreeMap รายการดำเนินต่อไป และไม่มีทางที่คุณจะหลีกเลี่ยงการทำงานกับคอลเลกชันได้ จะทำรายการได้อย่างไร? คุณต้องการรายการประเภทใด? มันควรจะไม่เปลี่ยนรูปหรือไม่แน่นอน? คำตอบทั้งหมดเหล่านี้ส่งผลต่ออนาคตของโค้ดของคุณ เลือกรายการที่ถูกต้องล่วงหน้าเพื่อที่คุณจะได้ไม่เสียใจในภายหลัง Arrays::asListสร้างรายการ "จากต้นทางถึงปลายทาง" คุณไม่สามารถทำอะไรกับรายการนี้ได้? คุณไม่สามารถปรับขนาดได้ เขาไม่เปลี่ยนแปลง คุณสามารถทำอะไรที่นี่? ระบุองค์ประกอบ การเรียงลำดับ หรือการดำเนินการอื่นๆ ที่ไม่ส่งผลต่อขนาด ใช้Arrays::asListอย่างระมัดระวังเนื่องจากขนาดของมันไม่เปลี่ยนรูปแต่เนื้อหาไม่เปลี่ยนรูป new ArrayList()สร้างรายการ "ไม่แน่นอน" ใหม่ รายการที่สร้างขึ้นรองรับการดำเนินการใดบ้าง เพียงเท่านี้และนี่คือเหตุผลที่ต้องระวัง สร้างรายการที่ไม่แน่นอนจากรายการที่ไม่เปลี่ยนรูปโดยใช้new ArrayList( ) List::ofสร้างคอลเลกชันที่ “ไม่เปลี่ยนรูป” ขนาดและเนื้อหาไม่เปลี่ยนแปลงภายใต้เงื่อนไขบางประการ หากเนื้อหาเป็นข้อมูลพื้นฐาน เช่นintรายการนั้นจะไม่เปลี่ยนรูป ลองดูตัวอย่างต่อไปนี้
@Test
public void testListOfBuilders() {
  System.out.println("### TESTING listOF with mutable content ###");

  StringBuilder one = new StringBuilder();
  one.append("a");

  StringBuilder two = new StringBuilder();
  two.append("a");

  List<StringBuilder> asList = List.of(one, two);

  asList.get(0).append("123");

  System.out.println(asList.get(0).toString());
}
### รายการทดสอบ OF พร้อมเนื้อหาที่ไม่แน่นอน ### a123
คุณต้องสร้างวัตถุที่ไม่เปลี่ยนรูปและแทรกลงในList:: of อย่างไรก็ตามList::ofไม่ได้ให้การรับประกันใดๆ ของการไม่เปลี่ยนรูป List::ofให้ความไม่เปลี่ยนรูป ความน่าเชื่อถือ และความสามารถในการอ่าน รู้ว่าเมื่อใดควรใช้โครงสร้างที่ไม่เปลี่ยนรูป และเมื่อใดควรใช้โครงสร้างที่ไม่เปลี่ยนรูป รายการอาร์กิวเมนต์ที่ไม่ควรเปลี่ยนแปลงควรอยู่ในรายการที่ไม่เปลี่ยนรูป รายการที่ไม่แน่นอนสามารถเป็นรายการที่ไม่แน่นอนได้ ทำความเข้าใจว่าคุณต้องการคอลเลกชันใดเพื่อสร้างโค้ดที่เชื่อถือได้

3. คำอธิบายประกอบทำให้คุณช้าลง

คุณใช้คำอธิบายประกอบหรือไม่? คุณเข้าใจพวกเขาไหม? คุณรู้ไหมว่าพวกเขาทำอะไร? หากคุณคิดว่า คำอธิบาย ประกอบที่บันทึกไว้เหมาะสำหรับทุกวิธี แสดงว่าคุณคิดผิด ฉันใช้Loggedเพื่อบันทึกอาร์กิวเมนต์ของวิธีการ ฉันแปลกใจที่มันไม่ได้ผล
@Transaction
@Method("GET")
@PathElement("time")
@PathElement("date")
@Autowired
@Secure("ROLE_ADMIN")
public void manage(@Qualifier('time')int time) {
...
}
เกิดอะไรขึ้นกับรหัสนี้? มีรายละเอียดการกำหนดค่ามากมายที่นี่ คุณจะพบสิ่งนี้หลายครั้ง การกำหนดค่าผสมกับโค้ดปกติ ไม่เลวในตัวเอง แต่มันดึงดูดสายตาของคุณ จำเป็นต้องมีคำอธิบายประกอบเพื่อลดโค้ดสำเร็จรูป คุณไม่จำเป็นต้องเขียนตรรกะการบันทึกสำหรับแต่ละปลายทาง ไม่ต้องตั้งค่าธุรกรรมใช้@Transactional คำอธิบายประกอบลดรูปแบบโดยการแยกโค้ด ไม่มีผู้ชนะที่ชัดเจนที่นี่เนื่องจากทั้งคู่อยู่ในเกม ฉันยังคงใช้ XML และคำอธิบายประกอบ เมื่อคุณพบรูปแบบที่ซ้ำกัน วิธีที่ดีที่สุดคือย้ายตรรกะไปไว้ในคำอธิบายประกอบ ตัวอย่างเช่น การบันทึกเป็นตัวเลือกคำอธิบายประกอบที่ดี คุณธรรม: อย่าใช้คำอธิบายประกอบมากเกินไปและอย่าลืม XML

โบนัส: คุณอาจประสบปัญหากับตัวเลือกเสริม

คุณจะใช้orElseจากOptional พฤติกรรมที่ไม่พึง ประสงค์เกิดขึ้นเมื่อคุณไม่ผ่าน ค่าคงที่ orElse คุณควรตระหนักถึงสิ่งนี้เพื่อป้องกันปัญหาในอนาคต ลองดูตัวอย่างบางส่วน เมื่อgetValue(x)ส่งคืนค่าgetValue(y) จะถูกดำเนิน การ วิธีการในorElseจะถูกดำเนินการหากgetValue(x) ส่งคืนค่า Optionalที่ไม่ว่างเปล่า
getValue(x).orElse(getValue(y)
                  .orElseThrow(() -> new NotFoundException("value not present")));

public Optional<Value> getValue(Source s)
{
  System.out.println("Source: " + s.getName());

  // returns value from s source
}

// when getValue(x) is present system will output
Source: x
Source: y
ใช้orElseGet _ มันจะไม่รันโค้ดสำหรับOptionals ที่ไม่ว่าง เปล่า
getValue(x).orElseGet(() -> getValue(y)
                  .orElseThrow(() -> new NotFoundException("value not present")));

public Optional<Value> getValue(Source s)
{
  System.out.println("Source: " + s.getName());

  // returns value from s source
}

// when getValue(x) is present system will output
Source: x

บทสรุป

การเรียนรู้ Java เป็นเรื่องยาก คุณไม่สามารถเรียนรู้ Java ได้ภายใน 24 ชั่วโมง ฝึกฝนทักษะของคุณ ใช้เวลา เรียนรู้ และเก่งในงานของคุณ

วิธีใช้หลักการ SOLID ในโค้ด

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

หลักการที่มั่นคง

แล้วคุณจะเขียนโค้ด SOLID ได้อย่างไร? จริงๆแล้วมันง่าย คุณเพียงแค่ต้องปฏิบัติตามกฎห้าข้อเหล่านี้:
  • หลักการความรับผิดชอบเดียว
  • หลักการเปิด-ปิด
  • หลักการทดแทน Liskov
  • หลักการแยกส่วนต่อประสาน
  • หลักการผกผันการพึ่งพา
ไม่ต้องกังวล! หลักการเหล่านี้ง่ายกว่าที่คิดมาก!

หลักการความรับผิดชอบเดียว

ในหนังสือของเขา โรเบิร์ต ซี. มาร์ตินบรรยายหลักธรรมนี้ดังนี้ “ชั้นเรียนควรมีเหตุผลเดียวเท่านั้นในการเปลี่ยนแปลง” ลองดูสองตัวอย่างด้วยกัน

1. สิ่งที่ไม่ควรทำ

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

2. จะทำอย่างไร

ลองนึกภาพคลาสที่ควรแสดงการแจ้งเตือนให้กับผู้ใช้ใหม่FirstUseNotification จะประกอบด้วย 3 ฟังก์ชั่น คือ
  • ตรวจสอบว่ามีการแสดงการแจ้งเตือนแล้วหรือไม่
  • แสดงการแจ้งเตือน
  • ทำเครื่องหมายการแจ้งเตือนตามที่แสดงไว้แล้ว
ชั้นเรียนนี้มีเหตุผลหลายประการที่ต้องเปลี่ยนแปลงหรือไม่ เลขที่ คลาสนี้มีฟังก์ชันที่ชัดเจนอย่างหนึ่ง - แสดงการแจ้งเตือนสำหรับผู้ใช้ใหม่ ซึ่งหมายความว่าชั้นเรียนมีเหตุผลหนึ่งข้อที่ต้องเปลี่ยนแปลง กล่าวคือหากเป้าหมายนี้เปลี่ยนไป ดังนั้นชั้นเรียนนี้ไม่ละเมิดหลักการความรับผิดชอบเดียว แน่นอนว่ามีบางสิ่งที่อาจเปลี่ยนแปลง: วิธีทำเครื่องหมายการแจ้งเตือนว่าอ่านแล้วอาจเปลี่ยนแปลง หรือวิธีแสดงการแจ้งเตือน อย่างไรก็ตาม เนื่องจากจุดประสงค์ของชั้นเรียนนั้นชัดเจนและเป็นพื้นฐาน นี่ก็เป็นเรื่องปกติ

หลักการเปิด-ปิด

หลักการเปิด-ปิดกำหนดโดย Bertrand Meyer ว่า “วัตถุซอฟต์แวร์ (คลาส โมดูล ฟังก์ชัน ฯลฯ) ควรเปิดเพื่อขยาย แต่ปิดเพื่อแก้ไข” หลักการนี้จริงๆ แล้วง่ายมาก คุณต้องเขียนโค้ดของคุณเพื่อให้สามารถเพิ่มคุณสมบัติใหม่ได้โดยไม่ต้องเปลี่ยนซอร์สโค้ด ซึ่งจะช่วยป้องกันสถานการณ์ที่คุณต้องเปลี่ยนคลาสที่ขึ้นอยู่กับคลาสที่คุณแก้ไข อย่างไรก็ตามหลักการนี้นำไปปฏิบัติได้ยากกว่ามาก เมเยอร์แนะนำให้ใช้มรดก แต่มันนำไปสู่ความสัมพันธ์ที่แน่นแฟ้น เราจะหารือเรื่องนี้ในหลักการของการแยกส่วนต่อประสานและหลักการของการผกผันการพึ่งพา ดังนั้น Martin จึงเกิดแนวทางที่ดีกว่า: ใช้ความหลากหลาย แทนที่จะใช้การสืบทอดแบบดั้งเดิม วิธีการนี้ใช้คลาสฐานเชิงนามธรรม ด้วยวิธีนี้ ข้อมูลจำเพาะของการสืบทอดสามารถนำกลับมาใช้ใหม่ได้ในขณะที่ไม่จำเป็นต้องดำเนินการ สามารถเขียนอินเทอร์เฟซได้เพียงครั้งเดียวแล้วปิดเพื่อทำการเปลี่ยนแปลง ฟังก์ชั่นใหม่จะต้องใช้อินเทอร์เฟซนี้และขยายออกไป

หลักการทดแทน Liskov

หลักการนี้คิดค้นโดย Barbara Liskov ผู้ชนะรางวัลทัวริงจากการมีส่วนร่วมในภาษาการเขียนโปรแกรมและวิธีการซอฟต์แวร์ ในบทความของเธอ เธอได้กำหนดหลักการของเธอไว้ดังนี้: “ออบเจ็กต์ในโปรแกรมควรถูกแทนที่ด้วยอินสแตนซ์ของชนิดย่อยได้ โดยไม่กระทบต่อการทำงานของโปรแกรมที่ถูกต้อง” ลองดูหลักการนี้ในฐานะโปรแกรมเมอร์ ลองนึกภาพเรามีสี่เหลี่ยมจัตุรัส มันอาจเป็นสี่เหลี่ยมจัตุรัส ซึ่งฟังดูสมเหตุสมผลเพราะสี่เหลี่ยมจัตุรัสเป็นรูปทรงพิเศษของสี่เหลี่ยมมุมฉาก นี่คือจุดที่หลักการทดแทน Liskov เข้ามาช่วยเหลือ เมื่อใดก็ตามที่คุณคาดว่าจะเห็นสี่เหลี่ยมในโค้ดของคุณ ก็เป็นไปได้ที่สี่เหลี่ยมจะปรากฏเช่นกัน ทีนี้ลองนึกภาพ สี่เหลี่ยมผืนผ้าของคุณมี วิธี SetWidthและSetHeight ซึ่งหมายความว่าสแควร์ก็ต้องการวิธีการเหล่านี้เช่นกัน น่าเสียดายที่สิ่งนี้ไม่สมเหตุสมผลเลย ซึ่งหมายความว่าหลักการทดแทน Liskov ถูกละเมิดที่นี่

หลักการแยกส่วนต่อประสาน

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

หลักการผกผันการพึ่งพา

หลักการสุดท้ายนั้นเรียบง่าย: โมดูลระดับสูงควรสามารถนำมาใช้ซ้ำได้ และไม่ควรได้รับผลกระทบจากการเปลี่ยนแปลงโมดูลระดับต่ำ
  • A. โมดูลระดับสูงไม่ควรขึ้นอยู่กับโมดูลระดับต่ำ ทั้งสองต้องขึ้นอยู่กับนามธรรม (เช่นอินเทอร์เฟซ)
  • B. นามธรรมไม่ควรขึ้นอยู่กับรายละเอียด รายละเอียด (การใช้งานคอนกรีต) จะต้องขึ้นอยู่กับนามธรรม
สิ่งนี้สามารถทำได้โดยการใช้นามธรรมที่แยกโมดูลระดับสูงและต่ำ ชื่อของหลักการบ่งบอกว่าทิศทางของการพึ่งพาเปลี่ยนแปลงไป แต่ก็ไม่เป็นเช่นนั้น มันจะแยกการพึ่งพาโดยการแนะนำสิ่งที่เป็นนามธรรมระหว่างสิ่งเหล่านั้นเท่านั้น เป็นผลให้คุณจะได้รับการขึ้นต่อกันสองรายการ:
  • โมดูลระดับสูง ขึ้นอยู่กับนามธรรม
  • โมดูลระดับต่ำขึ้นอยู่กับสิ่งที่เป็นนามธรรมเดียวกัน
สิ่งนี้อาจดูยาก แต่ในความเป็นจริงมันจะเกิดขึ้นโดยอัตโนมัติหากคุณใช้หลักการเปิด/ปิดและหลักการแทนที่ Liskov อย่างถูกต้อง นั่นคือทั้งหมด! ตอนนี้คุณได้เรียนรู้หลักการสำคัญห้าประการที่เป็นรากฐานของ SOLID แล้ว ด้วยหลักการทั้งห้าข้อนี้ คุณสามารถทำให้โค้ดของคุณน่าทึ่งได้!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION