
- การต่อต้านรูปแบบทางสถาปัตยกรรม - การต่อต้านรูปแบบทางสถาปัตยกรรมที่เกิดขึ้นเมื่อออกแบบโครงสร้างของระบบ (โดยปกติจะเป็นโดยสถาปนิก)
- Management Anti Pattern - รูปแบบต่อต้านในสาขาการจัดการซึ่งมักพบโดยผู้จัดการหลายคน (หรือกลุ่มผู้จัดการ)
- รูปแบบการต่อต้านการพัฒนา - การต่อต้านรูปแบบคือปัญหาการพัฒนาที่เกิดขึ้นเมื่อโปรแกรมเมอร์ธรรมดาเขียนระบบ
1. อัมพาตเชิงวิเคราะห์
การวิเคราะห์อัมพาตถือเป็นรูปแบบการต่อต้านองค์กรแบบคลาสสิก มันเกี่ยวข้องกับการวิเคราะห์สถานการณ์มากเกินไปเมื่อวางแผนเพื่อไม่ให้มีการตัดสินใจหรือดำเนินการใด ๆ ซึ่งจะทำให้การพัฒนาเป็นอัมพาต สิ่งนี้มักเกิดขึ้นเมื่อเป้าหมายคือการบรรลุความสมบูรณ์แบบและเสร็จสิ้นช่วงการวิเคราะห์ รูปแบบการต่อต้านนี้มีลักษณะเฉพาะคือการเดินเป็นวงกลม (แบบวงปิด) แก้ไขและการสร้างแบบจำลองที่มีรายละเอียด ซึ่งจะรบกวนขั้นตอนการทำงาน ตัวอย่างเช่น คุณกำลังพยายามคาดเดาสิ่งต่างๆ เช่น จะเกิดอะไรขึ้นหากจู่ๆ ผู้ใช้ต้องการสร้างรายชื่อพนักงานตามตัวอักษรที่สี่และห้าของชื่อ รวมถึงโครงการที่พวกเขาทุ่มเทชั่วโมงทำงานมากที่สุดระหว่างปีใหม่และ วันที่ 8 มีนาคม ในรอบ 4 ปีที่ผ่านมา ? โดยพื้นฐานแล้ว นี่เป็นการวิเคราะห์ที่มากเกินไป ตัวอย่างในชีวิตจริงที่ดีคือการที่การวิเคราะห์อัมพาตทำให้ Kodak ล้มละลายได้อย่างไร ต่อไปนี้เป็นเคล็ดลับสั้นๆ สองสามข้อในการต่อสู้กับอัมพาตของการวิเคราะห์:- คุณต้องกำหนดเป้าหมายระยะยาวเป็นสัญญาณในการตัดสินใจ เพื่อให้ทุกการตัดสินใจที่คุณทำจะทำให้คุณเข้าใกล้เป้าหมายมากขึ้น และไม่บังคับให้คุณต้องกำหนดเวลา
- อย่ามุ่งความสนใจไปที่เรื่องมโนสาเร่ (ทำไมต้องตัดสินใจในเรื่องเล็กน้อยราวกับว่ามันเป็นครั้งสุดท้ายในชีวิตของคุณ?)
- กำหนดเส้นตายในการตัดสินใจ
- อย่าพยายามทำงานให้สมบูรณ์แบบ: ทำมันให้ดีจะดีกว่า
2. วัตถุพระเจ้า
Divine Objectเป็นรูปแบบต่อต้านที่อธิบายถึงความเข้มข้นที่มากเกินไปของฟังก์ชันที่แตกต่างกันมากเกินไป โดยจัดเก็บข้อมูลที่หลากหลายจำนวนมาก (วัตถุที่แอปพลิเคชันหมุนวนอยู่) ลองยกตัวอย่างเล็กๆ น้อยๆ: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 ข้อใดข้อหนึ่ง - Single Responsibility Principle - คลาส Singleton นอกเหนือจากการปฏิบัติหน้าที่ในทันทีแล้ว ยังควบคุมจำนวนอินสแตนซ์ด้วย
-
การพึ่งพาของคลาสปกติในซิงเกิลตันไม่สามารถมองเห็นได้ในส่วนต่อประสานคลาส เนื่องจากโดยปกติแล้วอินสแตนซ์ของซิงเกิลตันจะไม่ถูกส่งผ่านในพารามิเตอร์ของวิธีการ แต่ได้รับโดยตรงผ่าน
getInstance()
เพื่อระบุการพึ่งพาของคลาสในซิงเกิลตัน คุณต้องเจาะลึกถึงการใช้งานแต่ละวิธี - เพียงแค่ดูสาธารณะ สัญญาของวัตถุนั้นไม่เพียงพอการมีซิงเกิลตันจะช่วยลดความสามารถในการทดสอบของแอปพลิเคชันโดยทั่วไปและคลาสที่ใช้ซิงเกิลตันโดยเฉพาะ ประการแรก คุณไม่สามารถวาง Mock object แทนที่ singleton ได้ และประการที่สอง หาก singleton มีอินเทอร์เฟซสำหรับเปลี่ยนสถานะ การทดสอบจะขึ้นอยู่กับกันและกัน
กล่าวอีกนัยหนึ่ง ซิงเกิลตันจะเพิ่มการเชื่อมต่อ และทั้งหมดที่กล่าวมาข้างต้นก็ไม่มีอะไรมากไปกว่าผลที่ตามมาของการเชื่อมต่อที่เพิ่มขึ้น
และถ้าคุณลองคิดดู การใช้ซิงเกิลตันก็สามารถหลีกเลี่ยงได้ ตัวอย่างเช่น ในการควบคุมจำนวนอินสแตนซ์ของวัตถุ ค่อนข้างเป็นไปได้ (และจำเป็น) ที่จะใช้โรงงานประเภทต่างๆ
อันตรายที่ยิ่งใหญ่ที่สุดคือความพยายามสร้างสถาปัตยกรรมแอปพลิเคชันทั้งหมดโดยใช้ซิงเกิลตัน มีทางเลือกดีๆ มากมายสำหรับแนวทางนี้ ตัวอย่างที่สำคัญที่สุดคือ Spring นั่นคือคอนเทนเนอร์ IoC: มีปัญหาในการควบคุมการสร้างบริการได้รับการแก้ไขอย่างเป็นธรรมชาติ เนื่องจากในความเป็นจริงแล้ว พวกเขาคือ "โรงงานที่ใช้สเตียรอยด์"
ขณะนี้มีโฮลิวาร์มากมายในหัวข้อนี้ ดังนั้นมันขึ้นอยู่กับคุณที่จะตัดสินใจว่าซิงเกิลตันเป็นแพทเทิร์นหรือแอนตี้แพทเทิร์น
และเราจะไม่จมอยู่กับมันและไปยังรูปแบบการออกแบบสุดท้ายสำหรับวันนี้ - โพลเตอร์ไกสต์
4. โพลเตอร์ไกสต์
Poltergeistเป็นคลาสต่อต้านรูปแบบที่ไม่มีประโยชน์ซึ่งใช้ในการเรียกเมธอดของคลาสอื่นหรือเพียงแค่เพิ่มเลเยอร์นามธรรมที่ไม่จำเป็น สารต้านรูปแบบนี้แสดงออกมาในรูปของวัตถุอายุสั้นที่ไม่มีสถานะ วัตถุเหล่านี้มักจะใช้เพื่อเตรียมใช้งานวัตถุอื่นที่มีความคงทนมากกว่า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);
}
เป็นผลให้mergeUser
ไม่มีการใช้อีกต่อไป และน่าเสียดายที่จะลบมันออกไป: จะเกิดอะไรขึ้นถ้ามัน (หรือแนวคิดของมัน) ยังมีประโยชน์อยู่? รหัสดังกล่าวทำให้ระบบซับซ้อนและสร้างความสับสนเท่านั้น โดยพื้นฐานแล้วไม่ได้ให้คุณค่าเชิงปฏิบัติใดๆ เลย เราต้องไม่ลืมว่าโค้ดที่มี "ชิ้นส่วนที่ตายแล้ว" ดังกล่าวจะยากต่อการถ่ายโอนไปยังเพื่อนร่วมงานเมื่อคุณออกจากโปรเจ็กต์อื่น วิธีที่ดีที่สุดในการจัดการกับจุดยึดเรือคือการปรับโครงสร้างโค้ดใหม่ กล่าวคือ การลบส่วนต่างๆ ของโค้ดเหล่านี้ (อนิจจา) นอกจากนี้เมื่อวางแผนการพัฒนาคุณต้องคำนึงถึงการเกิดขึ้นของพุกดังกล่าว (ให้เวลาในการทำความสะอาดหางแร่)
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/antipattern ที่อธิบายไว้ข้างต้น นั่นคือสามารถมีได้เพียงอ็อบเจ็กต์ประเภทนี้เท่านั้น มันทำงานกับอ็อบเจ็กต์บางตัวResource
โดยค่าเริ่มต้นในตัวสร้างพูลจะเต็มไปด้วย 4 ชุด เมื่อนำวัตถุดังกล่าวออกไปก็จะถูกลบออกจากสระ (หากไม่มีก็สร้างและแจกทันที) และสุดท้ายก็มีวิธีการนำวัตถุกลับคืนมา วัตถุResource
มีลักษณะดังนี้:
public class Resource {
private Map<String, String> patterns;
public Resource() {
patterns = new HashMap<>();
patterns.put("заместитель", "https://studfile.net/preview/3676297/page:3/");
patterns.put("мост", "https://studfile.net/preview/3676297/page:4/");
patterns.put("фасад", "https://studfile.net/preview/3676297/page:5/");
patterns.put("строитель", "https://studfile.net/preview/3676297/page:6/#16");
}
public Map<String, String> getPatterns() {
return patterns;
}
public void setPatterns(Map<String, String> patterns) {
this.patterns = patterns;
}
}
ที่นี่เรามีวัตถุขนาดเล็กที่ประกอบด้วยแผนที่ซึ่งมีชื่อของรูปแบบเป็นกุญแจและลิงก์ไปยังรูปแบบเหล่านั้นเป็นค่า ตลอดจนวิธีการเข้าถึงแผนที่ ลองดูmain
:
class SomeMain {
public static void main(String[] args) {
ReusablePool pool = ReusablePool.getInstance();
Resource firstResource = pool.acquireResource();
Map<String, String> firstPatterns = firstResource.getPatterns();
// ......Howим-то образом используем нашу мапу.....
pool.releaseResource(firstResource);
Resource secondResource = pool.acquireResource();
Map<String, String> secondPatterns = firstResource.getPatterns();
// ......Howим-то образом используем нашу мапу.....
pool.releaseResource(secondResource);
Resource thirdResource = pool.acquireResource();
Map<String, String> thirdPatterns = firstResource.getPatterns();
// ......Howим-то образом используем нашу мапу.....
pool.releaseResource(thirdResource);
}
}
ทุกอย่างที่นี่ก็ชัดเจนเช่นกัน: เรานำวัตถุพูล ดึงวัตถุที่มีทรัพยากรออกมา นำแผนที่จากนั้น ทำอะไรบางอย่างกับมัน และนำมันทั้งหมดกลับเข้าไปในพูลเพื่อนำกลับมาใช้ใหม่อีกครั้ง Voila: ที่นี่คุณมีรูปแบบพูลของวัตถุ แต่เรากำลังพูดถึงการต่อต้านรูปแบบใช่ไหม? ลองดูกรณีนี้main
:
Resource fourthResource = pool.acquireResource();
Map<String, String> fourthPatterns = firstResource.getPatterns();
// ......Howим-то образом используем нашу мапу.....
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("заместитель", "https://studfile.net/preview/3676297/page:3/");
patterns.put("мост", "https://studfile.net/preview/3676297/page:4/");
patterns.put("фасад", "https://studfile.net/preview/3676297/page:5/");
patterns.put("строитель", "https://studfile.net/preview/3676297/page:6/#16");
patterns = Collections.unmodifiableMap(patterns);
}
(ความพยายามและความปรารถนาที่จะเปลี่ยนแปลงเนื้อหาจะตกไปพร้อมกับ UnsupportedOperationException) AntiPatternsเป็นกับดักที่นักพัฒนามักตกหลุมพรางเนื่องจากการไม่มีเวลาอย่างเฉียบพลัน การไม่ตั้งใจ ไม่มีประสบการณ์ หรือการถูกเตะจากผู้จัดการ การไม่มีเวลาและความเร่งรีบตามปกติอาจส่งผลให้เกิดปัญหาใหญ่ในการใช้งานในอนาคต ดังนั้นจึงต้องทราบและหลีกเลี่ยงข้อผิดพลาดเหล่านี้ล่วงหน้า 
GO TO FULL VERSION