
- รูปแบบต่อต้านทางสถาปัตยกรรม — รูปแบบต่อต้านเหล่านี้เกิดขึ้นเมื่อโครงสร้างของระบบได้รับการออกแบบ (โดยทั่วไปแล้วโดยสถาปนิก)
- รูปแบบการต่อต้านการจัดการ/องค์กร — สิ่งเหล่านี้เป็นการต่อต้านรูปแบบในการจัดการโครงการ ซึ่งมักพบโดยผู้จัดการหลายคน (หรือกลุ่มผู้จัดการ)
- รูปแบบต่อต้านการพัฒนา — รูปแบบต่อต้านเหล่านี้เกิดขึ้นเมื่อระบบถูกใช้งานโดยโปรแกรมเมอร์ทั่วไป
1. การวิเคราะห์อัมพาต
การวิเคราะห์อัมพาตถือเป็นรูปแบบการต่อต้านการบริหารแบบคลาสสิก มันเกี่ยวข้องกับการวิเคราะห์สถานการณ์มากเกินไปในระหว่างการวางแผน เพื่อไม่ให้มีการตัดสินใจหรือดำเนินการใด ๆ โดยพื้นฐานแล้วจะทำให้กระบวนการพัฒนาเป็นอัมพาต สิ่งนี้มักจะเกิดขึ้นเมื่อเป้าหมายคือการบรรลุความสมบูรณ์แบบและพิจารณาทุกอย่างอย่างสมบูรณ์ในช่วงการวิเคราะห์ การต่อต้านรูปแบบนี้มีลักษณะเด่นคือการเดินเป็นวงกลม (วงจรปิดแบบรันออฟเดอะมิล) การแก้ไขและสร้างแบบจำลองโดยละเอียด ซึ่งจะรบกวนขั้นตอนการทำงาน ตัวอย่างเช่น คุณกำลังพยายามทำนายสิ่งต่างๆ ในระดับหนึ่ง แต่จะทำอย่างไรถ้าจู่ๆ ผู้ใช้ต้องการสร้างรายชื่อพนักงานตามตัวอักษรตัวที่สี่และห้าของชื่อ รวมถึงรายชื่อโครงการที่พวกเขาใช้เวลาทำงานมากที่สุด ระหว่างวันปีใหม่และวันสตรีสากลตลอด 4 ปีที่ผ่านมา? ในสาระสำคัญก็คือ ' วิเคราะห์มากเกินไป ต่อไปนี้คือเคล็ดลับบางประการสำหรับการต่อสู้กับอัมพาตจากการวิเคราะห์:- คุณต้องกำหนดเป้าหมายระยะยาวเป็นสัญญาณสำหรับการตัดสินใจ เพื่อให้การตัดสินใจแต่ละครั้งของคุณทำให้คุณเข้าใกล้เป้าหมายมากขึ้นแทนที่จะทำให้คุณหยุดนิ่ง
- อย่าจดจ่ออยู่กับเรื่องเล็ก ๆ น้อย ๆ (ทำไมต้องตัดสินใจเกี่ยวกับรายละเอียดเล็กน้อยราวกับว่าเป็นการตัดสินใจที่สำคัญที่สุดในชีวิตของคุณ)
- กำหนดเส้นตายสำหรับการตัดสินใจ
- อย่าพยายามทำงานให้เสร็จอย่างสมบูรณ์ - ดีกว่าที่จะทำมันให้ดี
2. วัตถุพระเจ้า
วัตถุพระเจ้าเป็นรูปแบบการต่อต้านที่อธิบายถึงความเข้มข้นมากเกินไปของฟังก์ชั่นทุกประเภทและข้อมูลที่แตกต่างกันจำนวนมาก (วัตถุที่แอปพลิเคชันหมุนรอบ) ยกตัวอย่างเล็กน้อย:
public class SomeUserGodObject {
private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";
private static final String FIND_ALL_CUSTOMERS = "SELECT id, u.email, u.phone, u.first_name_en, u.middle_name_en, u.last_name_en, u.created_date" +
" WHERE u.id IN (SELECT up.user_id FROM user_permissions up WHERE up.permission_id = ?)";
private static final String FIND_BY_EMAIL = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_dateFROM users WHERE email = ?";
private static final String LIMIT_OFFSET = " LIMIT ? OFFSET ?";
private static final String ORDER = " ORDER BY ISNULL(last_name_en), last_name_en, ISNULL(first_name_en), first_name_en, ISNULL(last_name_ru), " +
"last_name_ru, ISNULL(first_name_ru), first_name_ru";
private static final String CREATE_USER_EN = "INSERT INTO users(id, phone, email, first_name_en, middle_name_en, last_name_en, created_date) " +
"VALUES (?, ?, ?, ?, ?, ?, ?)";
private static final String FIND_ID_BY_LANG_CODE = "SELECT id FROM languages WHERE lang_code = ?";
........
private final JdbcTemplate jdbcTemplate;
private Map<String, String> firstName;
private Map<String, String> middleName;
private Map<String, String> lastName;
private List<Long> permission;
........
@Override
public List<User> findAllEnCustomers(Long permissionId) {
return jdbcTemplate.query( FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
}
@Override
public List<User> findAllEn() {
return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
}
@Override
public Optional<List<User>> findAllEnByEmail(String email) {
var query = FIND_ALL_USERS_EN + FIND_BY_EMAIL + ORDER;
return Optional.ofNullable(jdbcTemplate.query(query, userRowMapper(), email));
}
.............
private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
switch (type) {
case USERS:
return findAllEnUsers(permissionId);
case CUSTOMERS:
return findAllEnCustomers(permissionId);
default:
return findAllEn();
}
}
..............…
private RowMapper<User> userRowMapperEn() {
return (rs, rowNum) ->
User.builder()
.id(rs.getLong("id"))
.email(rs.getString("email"))
.accessFailed(rs.getInt("access_counter"))
.createdDate(rs.getObject("created_date", LocalDateTime.class))
.firstName(rs.getString("first_name_en"))
.middleName(rs.getString("middle_name_en"))
.lastName(rs.getString("last_name_en"))
.phone(rs.getString("phone"))
.build();
}
}
ที่นี่เราเห็นชั้นเรียนขนาดใหญ่ที่ทำทุกอย่าง ประกอบด้วยแบบสอบถามฐานข้อมูลและข้อมูลบางส่วน นอกจากนี้เรายังเห็นเมธอดส่วนหน้าของ findAllWithoutPageEn ซึ่งรวมถึงตรรกะทางธุรกิจด้วย วัตถุระดับเทพเช่นนี้กลายเป็นสิ่งใหญ่โตและน่าอึดอัดใจที่จะดูแลรักษาอย่างเหมาะสม เราต้องยุ่งกับมันในโค้ดทุกชิ้น ส่วนประกอบของระบบจำนวนมากพึ่งพามันและเชื่อมโยงอย่างแน่นหนา การรักษารหัสดังกล่าวจะยากขึ้นเรื่อย ๆ ในกรณีเช่นนี้ ควรแยกโค้ดออกเป็นคลาสต่างๆ โดยแต่ละคลาสจะมีจุดประสงค์เดียวเท่านั้น ในตัวอย่างนี้ เราสามารถแบ่งวัตถุพระเจ้าออกเป็นคลาส Dao:
public class UserDaoImpl {
private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";
........
private final JdbcTemplate jdbcTemplate;
........
@Override
public List<User> findAllEnCustomers(Long permissionId) {
return jdbcTemplate.query(FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
}
@Override
public List<User> findAllEn() {
return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
}
........
}
คลาสที่มีข้อมูลและวิธีการเข้าถึงข้อมูล:
public class UserInfo {
private Map<String, String> firstName;
…..
public Map<String, String> getFirstName() {
return firstName;
}
public void setFirstName(Map<String, String> firstName) {
this.firstName = firstName;
}
....
และจะเป็นการเหมาะสมกว่าที่จะย้ายเมธอดด้วยตรรกะทางธุรกิจไปยังบริการ:
private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
switch (type) {
case USERS:
return findAllEnUsers(permissionId);
case CUSTOMERS:
return findAllEnCustomers(permissionId);
default:
return findAllEn();
}
}
3. ซิงเกิลตัน
ซิงเกิลตันเป็นรูปแบบที่ง่ายที่สุด ทำให้มั่นใจได้ว่าในแอ็พพลิเคชันแบบเธรดเดียวจะมีอินสแตนซ์เดียวของคลาส และจัดเตรียมจุดเชื่อมต่อส่วนกลางให้กับออบเจกต์นี้ แต่มันเป็นรูปแบบหรือต่อต้านรูปแบบ? มาดูข้อเสียของรูปแบบนี้กัน:-
สถานะสากล เมื่อเราเข้าถึงอินสแตนซ์ของคลาส เราไม่ทราบสถานะปัจจุบันของคลาสนี้ เราไม่รู้ว่าใครเปลี่ยนมันหรือเมื่อไหร่ รัฐอาจไม่ได้เป็นอย่างที่เราคาดหวัง กล่าวอีกนัยหนึ่ง ความถูกต้องของการทำงานกับซิงเกิลตันขึ้นอยู่กับลำดับการเข้าถึง ซึ่งหมายความว่าระบบย่อยต้องพึ่งพาซึ่งกันและกัน และเป็นผลให้การออกแบบมีความซับซ้อนมากขึ้น
-
singleton ฝ่าฝืนหลักการ SOLID ซึ่งเป็นหลักความรับผิดชอบเดียว: นอกเหนือจากหน้าที่โดยตรงแล้ว singleton class ยังควบคุมจำนวนของอินสแตนซ์อีกด้วย
-
การพึ่งพาของคลาสธรรมดาในซิงเกิลตันไม่สามารถมองเห็นได้ในอินเทอร์เฟซของคลาส เนื่องจากโดยปกติแล้วอินสแตนซ์ซิงเกิลตันจะไม่ถูกส่งผ่านเป็นอาร์กิวเมนต์ของเมธอด แต่จะได้รับโดยตรงผ่าน getInstance() แทน คุณต้องเข้าสู่การใช้งานแต่ละเมธอดเพื่อระบุการพึ่งพาของคลาสในซิงเกิลตัน — เพียงแค่ดูที่คลาสของสาธารณะ สัญญาไม่เพียงพอ
การมีซิงเกิลตันช่วยลดความสามารถในการทดสอบของแอปพลิเคชันโดยรวมและคลาสที่ใช้ซิงเกิลตันโดยเฉพาะ ก่อนอื่น คุณไม่สามารถแทนที่ singleton ด้วยวัตถุจำลองได้ ประการที่สอง หากซิงเกิลตันมีส่วนต่อประสานสำหรับเปลี่ยนสถานะ การทดสอบจะขึ้นอยู่กับอีกอันหนึ่ง
กล่าวอีกนัยหนึ่ง singleton เพิ่มการมีเพศสัมพันธ์ และทุกสิ่งที่กล่าวถึงข้างต้นไม่มีอะไรมากไปกว่าผลของการมีเพศสัมพันธ์ที่เพิ่มขึ้น
และถ้าคุณคิดเกี่ยวกับมัน คุณสามารถหลีกเลี่ยงการใช้ซิงเกิลตัน ตัวอย่างเช่น ค่อนข้างเป็นไปได้ (และจำเป็นจริงๆ) ที่จะใช้โรงงานประเภทต่างๆ เพื่อควบคุมจำนวนอินสแตนซ์ของวัตถุ
อันตรายที่สุดคือความพยายามในการสร้างสถาปัตยกรรมแอปพลิเคชันทั้งหมดโดยใช้ซิงเกิลตัน มีทางเลือกที่ยอดเยี่ยมมากมายสำหรับแนวทางนี้ ตัวอย่างที่สำคัญที่สุดคือ Spring ซึ่งก็คือคอนเทนเนอร์ IoC ซึ่งเป็นวิธีแก้ปัญหาตามธรรมชาติในการควบคุมการสร้างบริการ เนื่องจากจริงๆ แล้วเป็น "โรงงานบนสเตียรอยด์"
การโต้วาทีที่ไม่มีวันสิ้นสุดและไม่สามารถประนีประนอมกันได้จำนวนมากกำลังเดือดดาลในเรื่องนี้ ขึ้นอยู่กับคุณที่จะตัดสินใจว่าซิงเกิลตันเป็นรูปแบบหรือต่อต้านรูปแบบ
เราจะไม่รั้งรอมัน เราจะไปยังรูปแบบการออกแบบล่าสุดสำหรับวันนี้แทน นั่นคือ poltergeist
4. โพลเตอร์ไกสต์
โพลเตอร์ไกสต์เป็นรูปแบบการต่อต้านที่เกี่ยวข้องกับคลาสที่ไม่มีจุดหมายซึ่งใช้เพื่อเรียกเมธอดของคลาสอื่นหรือเพียงแค่เพิ่มเลเยอร์ที่เป็นนามธรรมโดยไม่จำเป็น รูปแบบการต่อต้านนี้แสดงตัวว่าเป็นวัตถุอายุสั้น ไร้สถานะ วัตถุเหล่านี้มักจะใช้เพื่อเริ่มต้นวัตถุอื่นๆ ที่ถาวรกว่า
public class UserManager {
private UserService service;
public UserManager(UserService userService) {
service = userService;
}
User createUser(User user) {
return service.create(user);
}
Long findAllUsers(){
return service.findAll().size();
}
String findEmailById(Long id) {
return service.findById(id).getEmail();}
User findUserByEmail(String email) {
return service.findByEmail(email);
}
User deleteUserById(Long id) {
return service.delete(id);
}
}
ทำไมเราต้องการวัตถุที่เป็นเพียงตัวกลางและมอบหมายงานให้คนอื่น เรากำจัดมันและถ่ายโอนฟังก์ชันการทำงานเล็กๆ น้อยๆ ที่มีให้กับวัตถุที่มีอายุยืนยาว ต่อไป เราไปยังรูปแบบที่เราสนใจมากที่สุด (ในฐานะนักพัฒนาทั่วไป) เช่นรูปแบบต่อต้านการพัฒนา .
5. ฮาร์ดโค้ด
ดังนั้นเราจึงมาถึงคำที่น่ากลัวนี้: การเข้ารหัสอย่างหนัก สาระสำคัญของรูปแบบการป้องกันนี้คือรหัสนั้นเชื่อมโยงอย่างมากกับการกำหนดค่าฮาร์ดแวร์และ/หรือสภาพแวดล้อมของระบบ สิ่งนี้ทำให้การย้ายรหัสไปยังการกำหนดค่าอื่นซับซ้อนอย่างมาก รูปแบบต่อต้านนี้มีความเกี่ยวข้องอย่างใกล้ชิดกับตัวเลขมหัศจรรย์ (รูปแบบต่อต้านเหล่านี้มักจะเกี่ยวพันกัน) ตัวอย่าง:
public Connection buildConnection() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8&characterSetResults=UTF-8&serverTimezone=UTC", "user01", "12345qwert");
return connection;
}
เจ็บใช่ไหม ที่นี่เราฮาร์ดโค้ดการตั้งค่าการเชื่อมต่อของเรา ดังนั้นรหัสจะทำงานได้อย่างถูกต้องกับ MySQL เท่านั้น ในการเปลี่ยนฐานข้อมูล เราจะต้องดำดิ่งลงไปในโค้ดและเปลี่ยนแปลงทุกอย่างด้วยตนเอง ทางออกที่ดีคือการกำหนดค่าในไฟล์แยกต่างหาก:
spring:
datasource:
jdbc-url:jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: user01
password: 12345qwert
อีกทางเลือกหนึ่งคือการใช้ค่าคงที่
6. สมอเรือ
ในบริบทของการต่อต้านรูปแบบสมอเรือหมายถึงการเก็บชิ้นส่วนของระบบที่ไม่ใช้แล้วหลังจากดำเนินการปรับให้เหมาะสมหรือปรับโครงสร้างใหม่ นอกจากนี้ โค้ดบางส่วนยังสามารถเก็บไว้ "เพื่อใช้ในอนาคต" ได้ในกรณีที่คุณต้องการใช้อย่างกะทันหัน โดยพื้นฐานแล้ว สิ่งนี้จะเปลี่ยนรหัสของคุณให้เป็นถังขยะ ตัวอย่าง:
public User update(Long id, User request) {
User user = mergeUser(findById(id), request);
return userDAO.update(user);
}
private User mergeUser(User findUser, User requestUser) {
return new User(
findUser.getId(),
requestUser.getEmail() != null ? requestUser.getEmail() : findUser.getEmail(),
requestUser.getFirstName() != null ? requestUser.getFirstName() : findUser.getFirstNameRu(),
requestUser.getMiddleName() != null ? requestUser.getMiddleName() : findUser.getMiddleNameRu(),
requestUser.getLastName() != null ? requestUser.getLastName() : findUser.getLastNameEn(),
requestUser.getPhone() != null ? requestUser.getPhone() : findUser.getPhone());
}
เรามีเมธอดการอัพเดทที่ใช้วิธีการแยกเพื่อรวมข้อมูลผู้ใช้จากฐานข้อมูลด้วยข้อมูลผู้ใช้ที่ส่งผ่านไปยังเมธอด (หากผู้ใช้ที่ส่งผ่านไปยังเมธอดการอัพเดทมีฟิลด์ว่าง ค่าฟิลด์เก่าจะถูกนำมาจากฐานข้อมูล) . จากนั้น สมมติว่ามีข้อกำหนดใหม่ที่บันทึกจะต้องไม่ถูกรวมเข้ากับข้อมูลเก่า แต่แม้ว่าจะมีฟิลด์ว่างอยู่ ฟิลด์เหล่านี้จะถูกใช้เพื่อเขียนทับข้อมูลเก่า:
public User update(Long id, User request) {
return userDAO.update(user);
}
ซึ่งหมายความว่าคุณจะไม่ใช้การผสานผู้ใช้อีกต่อไป แต่น่าเสียดายที่จะลบออก — จะเกิดอะไรขึ้นหากวิธีนี้ (หรือแนวคิดของวิธีนี้) อาจมีประโยชน์ในสักวันหนึ่ง โค้ดดังกล่าวมีแต่จะทำให้ระบบซับซ้อนและทำให้เกิดความสับสนโดยที่ไม่มีประโยชน์ในทางปฏิบัติ เราต้องไม่ลืมว่ารหัสดังกล่าวที่มี "ชิ้นส่วนที่ตายแล้ว" จะส่งต่อให้เพื่อนร่วมงานได้ยากเมื่อคุณออกจากโครงการอื่น วิธีที่ดีที่สุดในการจัดการกับสมอเรือคือการปรับโครงสร้างโค้ดใหม่ เช่น ลบส่วนต่างๆ ของโค้ด (ฉันรู้ ปวดใจ) นอกจากนี้ เมื่อเตรียมกำหนดการพัฒนา จำเป็นต้องคำนึงถึงจุดยึดดังกล่าวด้วย (เพื่อจัดสรรเวลาสำหรับการจัดระเบียบ)
7. ส้วมซึมของวัตถุ
เพื่ออธิบายรูปแบบการต่อต้านนี้ ก่อนอื่นคุณต้องทำความคุ้นเคยกับรูปแบบกลุ่มอ็อบเจกต์ อ็อบเจกต์พูล (กลุ่มทรัพยากร) คือรูปแบบการออกแบบเชิงสร้างสรรค์ชุดของออบเจกต์ที่เริ่มต้นและพร้อมใช้งาน เมื่อแอปพลิเคชันต้องการวัตถุ วัตถุนั้นจะถูกนำมาจากพูลนี้แทนที่จะสร้างใหม่ เมื่อไม่ต้องการวัตถุอีกต่อไป สิ่งนั้นจะไม่ถูกทำลาย มันจะถูกส่งกลับไปที่สระแทน รูปแบบนี้มักจะใช้กับวัตถุที่มีน้ำหนักมากซึ่งใช้เวลานานในการสร้างทุกครั้งที่จำเป็น เช่น เมื่อเชื่อมต่อกับฐานข้อมูล มาดูตัวอย่างเล็กๆ ง่ายๆ กัน นี่คือคลาสที่แสดงถึงรูปแบบนี้:
class ReusablePool {
private static ReusablePool pool;
private List<Resource> list = new LinkedList<>();
private ReusablePool() {
for (int i = 0; i < 3; i++)
list.add(new Resource());
}
public static ReusablePool getInstance() {
if (pool == null) {
pool = new ReusablePool();
}
return pool;
}
public Resource acquireResource() {
if (list.size() == 0) {
return new Resource();
} else {
Resource r = list.get(0);
list.remove(r);
return r;
}
}
public void releaseResource(Resource r) {
list.add(r);
}
}
คลาสนี้ถูกนำเสนอในรูปแบบของsingleton pattern/anti-pattern ข้างต้น นั่นคือสามารถมีวัตถุประเภทนี้ได้เพียงอันเดียว มันใช้Resource
วัตถุ บางอย่าง ตามค่าเริ่มต้น ตัวสร้างจะเติมพูลด้วย 4 อินสแตนซ์ เมื่อคุณได้รับอ็อบเจกต์ มันจะถูกลบออกจากพูล (หากไม่มีออบเจกต์ที่มีอยู่ ออบเจกต์หนึ่งจะถูกสร้างขึ้นและส่งคืนทันที) และในตอนท้ายเรามีวิธีการใส่วัตถุกลับ วัตถุทรัพยากรมีลักษณะดังนี้:
public class Resource {
private Map<String, String> patterns;
public Resource() {
patterns = new HashMap<>();
patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
}
public Map<String, String> getPatterns() {
return patterns;
}
public void setPatterns(Map<String, String> patterns) {
this.patterns = patterns;
}
}
ที่นี่เรามีวัตถุขนาดเล็กที่มีแผนที่ที่มีชื่อรูปแบบการออกแบบเป็นคีย์และลิงก์ Wikipedia ที่สอดคล้องกันเป็นค่า ตลอดจนวิธีการเข้าถึงแผนที่ ลองดูที่หลัก:
class SomeMain {
public static void main(String[] args) {
ReusablePool pool = ReusablePool.getInstance();
Resource firstResource = pool.acquireResource();
Map<String, String> firstPatterns = firstResource.getPatterns();
// use our map somehow...
pool.releaseResource(firstResource);
Resource secondResource = pool.acquireResource();
Map<String, String> secondPatterns = firstResource.getPatterns();
// use our map somehow...
pool.releaseResource(secondResource);
Resource thirdResource = pool.acquireResource();
Map<String, String> thirdPatterns = firstResource.getPatterns();
// use our map somehow...
pool.releaseResource(thirdResource);
}
}
ทุกอย่างที่นี่ชัดเจนเพียงพอ: เราได้รับวัตถุพูล รับวัตถุที่มีทรัพยากรจากพูล รับแผนที่จากวัตถุทรัพยากร ทำบางอย่างกับมัน และนำทั้งหมดนี้มาแทนที่ในพูลเพื่อใช้ซ้ำต่อไป Voila นี่คือรูปแบบการออกแบบกลุ่มวัตถุ แต่เรากำลังพูดถึงรูปแบบต่อต้านใช่ไหม? ลองพิจารณากรณีต่อไปนี้ในวิธีการหลัก:
Resource fourthResource = pool.acquireResource();
Map<String, String> fourthPatterns = firstResource.getPatterns();
// use our map somehow...
fourthPatterns.clear();
firstPatterns.put("first","blablabla");
firstPatterns.put("second","blablabla");
firstPatterns.put("third","blablabla");
firstPatterns.put("fourth","blablabla");
pool.releaseResource(fourthResource);
อีกครั้ง เราได้รับวัตถุทรัพยากร เราได้รับแผนผังของรูปแบบ และเราทำบางอย่างกับแผนที่ แต่ก่อนที่จะบันทึกแผนที่กลับไปที่กลุ่มวัตถุ แผนที่จะถูกล้างและเติมข้อมูลเสียหาย ทำให้วัตถุทรัพยากรไม่เหมาะสมสำหรับการใช้ซ้ำ หนึ่งในรายละเอียดหลักของกลุ่มออบเจกต์คือเมื่อออบเจ็กต์ถูกส่งคืน จะต้องคืนค่าเป็นสถานะที่เหมาะสมสำหรับการใช้ซ้ำต่อไป หากอ็อบเจกต์ที่ส่งคืนไปยังพูลยังคงอยู่ในสถานะที่ไม่ถูกต้องหรือไม่ได้กำหนด การออกแบบของเราจะเรียกว่าอ็อบเจกต์เซสพูล การจัดเก็บวัตถุที่ไม่เหมาะสำหรับการนำมาใช้ซ้ำนั้นสมเหตุสมผลหรือไม่? ในสถานการณ์นี้ เราสามารถทำให้แผนที่ภายในไม่เปลี่ยนรูปแบบได้ในตัวสร้าง:
public Resource() {
patterns = new HashMap<>();
patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
patterns = Collections.unmodifiableMap(patterns);
}
ความพยายามและความปรารถนาที่จะเปลี่ยนแปลงเนื้อหาของแผนที่จะจางหายไป ต้องขอบคุณ UnsupportedOperationException ที่พวกเขาจะสร้างขึ้น Anti-patternsเป็นกับดักที่นักพัฒนาพบบ่อยครั้งเนื่องจากการไม่มีเวลาอย่างเฉียบพลัน ความประมาท ขาดประสบการณ์ หรือแรงกดดันจากผู้จัดการโครงการ การเร่งรีบซึ่งเป็นเรื่องปกติอาจนำไปสู่ปัญหาใหญ่สำหรับแอปพลิเคชันในอนาคต ดังนั้นคุณจำเป็นต้องรู้เกี่ยวกับข้อผิดพลาดเหล่านี้และหลีกเลี่ยงล่วงหน้า นี่เป็นการสรุปส่วนแรกของบทความ ยังมีต่อ...
GO TO FULL VERSION