CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.
Bài đăng trên blog CFSDN này sẽ đưa nó vào hoạt động! Những mẫu thiết kế nào thường được sử dụng trong công việc đều được tác giả sưu tầm và biên soạn. Nếu các bạn quan tâm đến bài viết này thì nhớ like nhé.
Lời nói đầu
Xin chào mọi người, mình là cậu bé sưu tập ốc sên.
Khi chúng ta thường viết mã, trong hầu hết các trường hợp, chúng ta viết mã theo kiểu đường dẫn, về cơ bản có thể triển khai logic nghiệp vụ. Làm thế nào để tìm thấy niềm vui khi viết mã? Tôi nghĩ cách tốt nhất là sử dụng các mẫu thiết kế để tối ưu hóa mã doanh nghiệp của bạn. Hôm nay tôi muốn nói chuyện với bạn về các mẫu thiết kế tôi sử dụng trong công việc hàng ngày.
Những mẫu thiết kế nào thường được sử dụng trong công việc.
1. Chế độ chiến lược
1.1 Kịch bản kinh doanh
Giả sử có một kịch bản kinh doanh như vậy, hệ thống dữ liệu lớn sẽ đẩy các tệp và áp dụng các phương pháp phân tích cú pháp khác nhau tùy theo các loại khác nhau. Hầu hết bạn bè sẽ viết đoạn mã sau:
- nếu(kiểu=="MỘT"){
- // Phân tích theo định dạng A
-
- }khácnếu(kiểu=="B"){
- // Phân tích theo định dạng B
- }khác{
- // Phân tích theo định dạng mặc định
- }
Có thể có vấn đề gì với mã này?
- Nếu có quá nhiều nhánh, mã ở đây sẽ trở nên cồng kềnh, khó bảo trì và khó đọc.
- Nếu bạn cần truy cập một loại phân tích cú pháp mới, bạn chỉ có thể sửa đổi mã gốc.
Nói một cách chuyên nghiệp hơn, đoạn mã trên vi phạm nguyên tắc đóng mở và nguyên tắc duy nhất của lập trình hướng đối tượng.
- Nguyên tắc đóng mở (mở để mở rộng, nhưng đóng để sửa đổi): thêm hoặc xóa logic nhất định yêu cầu sửa đổi mã gốc
- Nguyên tắc thống nhất (quy định rằng một lớp chỉ được có một lý do để thay đổi): Việc sửa đổi bất kỳ loại mã logic nhánh nào đều yêu cầu thay đổi mã của lớp hiện tại.
Nếu mã của bạn giống như Jiang Zi: có nhiều nhánh if...else và các nhánh có điều kiện khác và mỗi nhánh có điều kiện có thể được gói gọn và thay thế, chúng ta có thể sử dụng mẫu chiến lược để tối ưu hóa.
1.2 Định nghĩa mẫu chiến lược
Mẫu chiến lược xác định một nhóm thuật toán và đóng gói chúng một cách riêng biệt để có thể thay thế chúng bằng nhau. Mẫu này cho phép các thay đổi thuật toán độc lập với khách hàng sử dụng thuật toán. Định nghĩa của mẫu chiến lược này có hơi trừu tượng không?
Giả sử bạn đang hẹn hò với những cô gái có kiểu tính cách khác nhau và bạn cần sử dụng các chiến lược khác nhau thì đối với một số người, đi xem phim sẽ tốt hơn, đối với những người khác, đi ăn vặt sẽ hiệu quả hơn và đối với một số người, đi mua sắm là tốt nhất. Tất nhiên, mục đích là lấy lòng cô gái, xem phim, ăn vặt và đi mua sắm là những chiến lược khác nhau.
Mẫu Chiến lược nhắm đến một tập hợp các thuật toán và đóng gói mỗi thuật toán thành một lớp độc lập với giao diện chung, khiến chúng có thể hoán đổi cho nhau.
1.3 Sử dụng chế độ chiến lược
Làm thế nào để sử dụng chế độ chiến lược Jiangzi thực hiện:
Một giao diện hoặc lớp trừu tượng có hai phương thức (một kiểu khớp phương thức, một phương thức triển khai logic có thể thay thế).
Việc triển khai khác nhau của các chiến lược khác nhau (nghĩa là các lớp triển khai của các chiến lược khác nhau).
Sử dụng mô hình chiến lược.
1.3.1 Một giao diện, hai phương thức.
- công cộnggiao diệnIFileStrategy{
-
- // Nó thuộc về kiểu phân tích tập tin nào
- FileTypeResolveEnumgainFileType();
-
- // Thuật toán công khai được đóng gói (phương pháp phân tích cú pháp cụ thể)
- voidresolve(Objectobjectparam);
- }
1.3.2 Thực hiện khác biệt các chiến lược khác nhau.
Triển khai cụ thể chiến lược loại A.
- @Thành phần
- công cộngclassAFileResolveimplementsIFileStrategy{
-
- @Ghi đè
- công cộngFileTypeResolveEnumgainFileType(){
- trở lạiFileTypeResolveEnum.File_A_RESOLVE;
- }
-
- @Ghi đè
- công cộngvoidresolve(Objectobjectparam){
- logger. thông tin("Một tệp phân tích kiểu, tham số: {}",objectparam);
- // Một kiểu phân tích logic cụ thể
- }
- }
Triển khai cụ thể chiến lược loại B.
- @Thành phần
- công cộngclassBFileResolveimplementsIFileStrategy{
-
- @Ghi đè
- công cộngFileTypeResolveEnumgainFileType(){
- trở lạiFileTypeResolveEnum.File_B_RESOLVE;
- }
-
-
- @Ghi đè
- công cộngvoidresolve(Objectobjectparam){
- logger. thông tin("Tệp phân tích cú pháp loại B, tham số: {}",objectparam);
- // logic cụ thể của phân tích loại B
- }
- }
Mặc định loại chiến lược thực hiện cụ thể.
- @Thành phần
- công cộngclassDefaultFileResolveimplementsIFileStrategy{
-
- @Ghi đè
- công cộngFileTypeResolveEnumgainFileType(){
- trở lạiFileTypeResolveEnum.File_DEFAULT_RESOLVE;
- }
-
- @Ghi đè
- công cộngvoidresolve(Objectobjectparam){
- logger. thông tin("Tệp phân tích kiểu mặc định, tham số: {}",objectparam);
- // Kiểu mặc định phân tích cú pháp logic cụ thể
- }
- }
1.3.3 Sử dụng chế độ chiến lược.
Cách sử dụng? Chúng tôi sử dụng vòng đời Spring và sử dụng giao diện ApplicationContextAware để khởi tạo chiến lược tương ứng vào bản đồ. Sau đó cung cấp phương thức ResolveFile cho thế giới bên ngoài.
- /**
- *@author tài khoản public: Cậu bé nhặt ốc
- */
- @Thành phần
- công cộngclassStrategyUseServiceimplementsApplicationContextAware{
-
-
- Bản đồ riêng tư
iFileStrategyMap=newConcurrentHashMap<>();
-
- công cộngvoidresolveFile(FileTypeResolveEnumfileTypeResolveEnum,ObjectobjectParam){
- IFileStrategyiFileStrategy=iFileStrategyMap.get(fileTypeResolveEnum);
- nếu(iFileStrategy!=vô giá trị){
- iFileStrategy.resolve(objectParam);
- }
- }
-
- // Đưa các chiến lược khác nhau vào bản đồ
- @Ghi đè
- công cộngvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
- Bản đồ
tmepMap=applicationContext.getBeansOfType(IFileStrategy.class);
- Bản đồ tmep.giá trị().forEach(strategyService->iFileStrategyMap.put(strategyService.gainFileType(),strategyService));
- }
- }
2. Mô hình chuỗi trách nhiệm
2.1 Kịch bản kinh doanh
Hãy xem xét một tình huống kinh doanh phổ biến, đó là đặt hàng. Logic cơ bản của giao diện đặt hàng thường bao gồm xác minh tham số không trống, xác minh bảo mật, xác minh danh sách đen, chặn quy tắc, v.v. Nhiều đối tác sẽ sử dụng ngoại lệ để đạt được:
- công cộnglớp họcĐặt hàng{
-
- công cộngvoidcheckNullParam(Tham số đối tượng){
- //Xác minh tham số không null
- ném ngoại lệ Runtime mới();
- }
- công cộngvoidcheckBảo mật(){
- //kiểm tra bảo mật
- ném ngoại lệ Runtime mới();
- }
- công cộngvoidcheckBackList() {
- //Xác minh danh sách đen
- ném ngoại lệ Runtime mới();
- }
- công cộngvoidcheckRule() {
- // Chặn quy tắc
- ném ngoại lệ Runtime mới();
- }
-
- công cộngtĩnhvoidmain(Chuỗi[]đối số){
- Đặt hàngđặt hàng=mớiĐặt hàng();
- thử{
- đặt hàng.checkNullParam();
- đặt hàng.kiểm tra bảo mật();
- đặt hàng.checkBackList();
- order2.checkRule();
- Hệ thống.ngoài.println("đơn hàng thành công");
- }bắt(RuntimeException){
- Hệ thống.ngoài.println("lệnh không thành công");
- }
- }
- }
Mã này sử dụng các ngoại lệ để đưa ra các phán đoán điều kiện logic. Nếu logic tiếp theo ngày càng phức tạp hơn, một số vấn đề sẽ phát sinh: ví dụ: các ngoại lệ chỉ có thể trả về thông tin ngoại lệ và không thể trả về nhiều trường hơn. lớp học.
Hơn nữa, hướng dẫn phát triển của Alibaba quy định cấm sử dụng các ngoại lệ để đưa ra phán đoán hợp lý.
[Bắt buộc] Không sử dụng ngoại lệ để kiểm soát quá trình hoặc kiểm soát điều kiện. Lưu ý: Mục đích ban đầu của thiết kế ngoại lệ là giải quyết các tình huống bất ngờ khác nhau trong quá trình vận hành chương trình và hiệu quả xử lý ngoại lệ thấp hơn nhiều so với phương pháp phán đoán có điều kiện.
Làm thế nào để tối ưu hóa mã này? Bạn có thể xem xét mô hình chuỗi trách nhiệm.
2.2 Định nghĩa mô hình chuỗi trách nhiệm
Sử dụng chuỗi mẫu trách nhiệm khi bạn muốn cho nhiều đối tượng cơ hội xử lý một yêu cầu.
Mẫu Chuỗi trách nhiệm tạo ra một chuỗi các đối tượng người nhận cho một yêu cầu. Có nhiều nút đối tượng trên chuỗi thực thi và mỗi nút đối tượng có cơ hội (khớp điều kiện) để xử lý giao dịch yêu cầu. Nếu một nút đối tượng được xử lý, nó có thể được chuyển đến nút tiếp theo để tiếp tục xử lý hoặc quay lại để hoàn tất quá trình xử lý. theo nhu cầu thực tế của doanh nghiệp. Mẫu này tách riêng người gửi và người nhận yêu cầu dựa trên loại yêu cầu.
Chuỗi mẫu trách nhiệm thực sự là một mẫu để xử lý các yêu cầu, mang đến cho nhiều bộ xử lý (nút đối tượng) cơ hội xử lý yêu cầu cho đến khi một trong số chúng được xử lý thành công. Chuỗi mẫu trách nhiệm xâu chuỗi nhiều bộ xử lý thành một chuỗi và sau đó chuyển yêu cầu dọc theo chuỗi:
Mô hình chuỗi trách nhiệm
Để đưa ra một sự tương tự:
Giả sử buổi tối bạn đi học lớp tự chọn và ngồi ở hàng cuối cùng để có thể đi lại một chút. Khi đến lớp, bạn thấy có vài cô gái xinh đẹp đang ngồi trước mặt nên bạn tìm một mảnh giấy và viết: "Xin chào, bạn có thể làm bạn gái của tôi được không? Nếu bạn không muốn thì xin hãy chuyển đi. " Những tờ giấy lần lượt được chuyền đi, rồi đến tay cô gái ngồi ở hàng đầu tiên. Cô ấy đưa tờ giấy cho giáo viên. Tôi nghe nói thầy đã ngoài 40 tuổi và chưa lập gia đình...
2.3 Sử dụng mô hình chuỗi trách nhiệm
Làm thế nào để sử dụng mô hình chuỗi trách nhiệm?
- một giao diện hoặc lớp trừu tượng
- Xử lý khác biệt từng đối tượng
- Khởi tạo chuỗi đối tượng (mảng) (được kết nối)
2.3.1 Một giao diện hoặc lớp trừu tượng.
Giao diện hoặc lớp trừu tượng này yêu cầu:
- Có một thuộc tính trỏ đến đối tượng tiếp theo chịu trách nhiệm
- Một phương thức thiết lập để đặt đối tượng tiếp theo
- Các phương pháp triển khai khác biệt cho các đối tượng lớp con (chẳng hạn như phương thức doFilter trong đoạn mã sau)
- /**
- *Theo dõi tài khoản công khai: Cậu bé nhặt ốc
- */
- công cộnglớp trừu tượng
-
- //Đối tượng tiếp theo trong chuỗi trách nhiệm
- privateAbstractHandlernextHandler;
-
- /**
- *Đối tượng tiếp theo trong chuỗi trách nhiệm
- */
- công cộngvoidsetNextHandler(Trừu tượngHandlernextHandler){
- this.nextHandler=nextHandler;
- }
-
- /**
- *Logic chặn tham số cụ thể được triển khai bởi các lớp con
- */
- công cộngvoidfilter(Yêu cầuyêu cầu, Phản hồiphản hồi){
- doFilter(yêu cầu, phản hồi);
- nếu(getNextHandler()!=vô giá trị){
- getNextHandler().filter(yêu cầu, phản hồi);
- }
- }
-
- công cộngTóm tắtHandlergetNextHandler(){
- trở lạiTrình xử lý tiếp theo;
- }
-
- abstractvoiddoFilter(Yêu cầu bộ lọcYêu cầu, Phản hồiphản hồi);
-
- }
2.3.2 Xử lý vi phân từng đối tượng.
Trong chuỗi trách nhiệm, mỗi đối tượng được xử lý khác nhau. Ví dụ: trong kịch bản kinh doanh của phần này, có các đối tượng xác minh tham số, đối tượng xác minh bảo mật, đối tượng xác minh danh sách đen và đối tượng chặn quy tắc.
- /**
- *Đối tượng xác minh tham số
- **/
- @Thành phần
- @Đặt hàng(1)//Xếp thứ tự thứ nhất, được xác minh đầu tiên
- công cộngclassCheckParamFilterObjectextendsAbstractHandler{
-
- @Ghi đè
- công cộngvoiddoFilter(Yêu cầuyêu cầu, Phản hồiphản hồi){
- Hệ thống.ngoài.println("Kiểm tra tham số không trống");
- }
- }
-
- /**
- *Đối tượng kiểm tra an ninh
- */
- @Thành phần
- @Đặt hàng(2)//Thứ tự xác minh xếp thứ 2
- công cộngclassCheckSecurityFilterObjectextendsAbstractHandler{
-
- @Ghi đè
- công cộngvoiddoFilter(Yêu cầuyêu cầu, Phản hồiphản hồi){
- //gọiBảo mậtkiểm tra
- Hệ thống.ngoài.println("Xác minh cuộc gọi an toàn");
- }
- }
- /**
- *Đối tượng xác minh danh sách đen
- */
- @Thành phần
- @Đặt hàng(3)//Thứ tự xác minh xếp thứ 3
- công cộngclassCheckBlackFilterObjectextendsAbstractHandler{
-
- @Ghi đè
- công cộngvoiddoFilter(Yêu cầuyêu cầu, Phản hồiphản hồi){
- //gọi danh sách đenkiểm tra
- Hệ thống.ngoài.println("Xác minh danh sách đen");
- }
- }
-
- /**
- * Quy tắc chặn đối tượng
- */
- @Thành phần
- @Đặt hàng(4)//Thứ tự xác minh xếp thứ 4
- công cộngclassCheckRuleFilterObjectextendsAbstractHandler{
-
- @Ghi đè
- công cộngvoiddoFilter(Yêu cầuyêu cầu, Phản hồiphản hồi){
- //kiểm traluật lệ
- Hệ thống.ngoài.println("kiểm tra quy tắc");
- }
- }
2.3.3 Xâu chuỗi đối tượng (khởi tạo) && sử dụng.
- @Thành phần("ChainPatternDemo")
- công cộnglớpChainPatternDemo{
-
- // Tự động đưa các đối tượng vào từng chuỗi trách nhiệm
- @Autowired
- danh sách xử lý trừu tượng riêng tư;
-
- privateAbstractHandlerabstractHandler;
-
- // Được thực thi tự động sau khi chèn lò xo, các đối tượng của chuỗi trách nhiệm được kết nối
- @PostXây dựng
- công cộngvoidinitializeChainFilter()
-
- vì(số nguyêntôi = 0;tôi
kích cỡ
();tôi++){
- nếu(i==0){
- abstractHandler=abstractHandleList.get(0);
- }khác{
- AbstractHandlercurrentHander=abstractHandleList.get(i-1);
- AbstractHandlernextHander=abstractHandleList.get(i);
- currentHander. setNextHandler(nextHander);
- }
- }
- }
-
- //Gọi trực tiếp phương thức này để sử dụng
- công cộngPhản ứngthực hiện(Yêu cầuyêu cầu, Phản hồiphản hồi){
- abstractHandler.filter(yêu cầu, phản hồi);
- trở lạiphản ứng;
- }
-
- công cộngTóm tắtHandlergetAbstractHandler(){
- trở lạiTrình xử lý trừu tượng;
- }
-
- công cộngvoidsetAbstractHandler(AbstractHandlerabstractHandler){
- this.abstractHandler=abstractHandler;
- }
- }
Kết quả chạy như sau:
- Kiểm tra tham số không trống
- Xác minh cuộc gọi an toàn
- Kiểm tra danh sách đen
- kiểm traluật lệ
3. Mẫu phương thức mẫu
3.1 Kịch bản kinh doanh
Giả sử chúng ta có một tình huống kinh doanh như vậy: những người bán khác nhau trong hệ thống nội bộ gọi giao diện hệ thống của chúng ta để tương tác với các hệ thống bên thứ ba bên ngoài (phương thức http). Thực hiện theo quy trình tương tự như sau:
Một yêu cầu sẽ trải qua các quy trình sau:
- Truy vấn thông tin người bán
- Ký tin nhắn yêu cầu
- Gửi yêu cầu http đi
- Xác minh chữ ký của tin nhắn được trả lại
Tại đây, một số thương gia có thể sử dụng đại lý, trong khi một số có thể sử dụng kết nối trực tiếp. Giả sử rằng người bán A và B hiện đang được kết nối. Nhiều đối tác có thể triển khai mã giả như sau:
- // Người bán Một tay cầm xử lý
- Công tyAHandlerimplementsRequestHandler{
- Trả lời(yêu cầu){
- //Truy vấn thông tin người bán
- truy vấnMerchantInfo();
- //Thêm chữ ký
- chữ ký();
- //yêu cầu http (người bán A giả định rằng họ sử dụng proxy)
- httpYêu cầu củaProxy()
- //Xác minh chữ ký
- xác minh();
- }
- }
- // Tay cầm xử lý của người bán B
- Công ty BHandlerimplementsRequestHandler{
- Trả lời(Rreq){
- //Truy vấn thông tin người bán
- truy vấnMerchantInfo();
- //Thêm chữ ký
- chữ ký();
- //yêu cầu http (người bán B không sử dụng proxy, kết nối trực tiếp)
- httpRequestbyDirect();
- //Xác minh chữ ký
- xác minh();
- }
- }
Giả sử rằng một người bán C mới được thêm vào quyền truy cập, bạn cần triển khai một bộ mã khác như vậy. Rõ ràng, đoạn mã này được lặp lại và một số phương thức phổ biến được viết lại trong mỗi lớp con.
Làm cách nào để tối ưu hóa? Bạn có thể sử dụng mẫu phương thức mẫu.
3.2 Định nghĩa mẫu phương thức mẫu
Xác định quy trình khung của một thuật toán đang hoạt động và trì hoãn một số bước cho các lớp con để các lớp con có thể xác định lại các bước cụ thể của thuật toán mà không thay đổi cấu trúc của thuật toán. Ý tưởng cốt lõi của nó là xác định một loạt các bước cho một thao tác. Đối với một số bước không thể xác định tạm thời, các lớp con sẽ thực hiện để các lớp con khác nhau có thể xác định các bước khác nhau.
Để sử dụng một phép ẩn dụ đơn giản:
Ví dụ về mẫu: Khi đuổi theo bạn gái, trước tiên bạn phải "nắm tay", sau đó là "ôm", sau đó là "hôn", rồi mới "vỗ...uh...tay". Về việc bạn cầm nó bằng tay trái hay tay phải thì không quan trọng, nhưng trong toàn bộ quá trình, một mẫu quy trình đã được đặt ra và bạn chỉ cần làm theo mẫu đó.
3.3 Sử dụng các phương pháp mẫu
- Một lớp trừu tượng xác định quy trình khung (các phương thức trừu tượng được kết hợp với nhau)
- Xác định các bước phương thức chung và đặt chúng vào lớp trừu tượng (xóa dấu phương thức trừu tượng)
- Các bước không chắc chắn, triển khai khác biệt cho các lớp con
Hãy tiếp tục với ví dụ về quy trình kinh doanh ở trên và tối ưu hóa nó bằng phương thức mẫu:
3.3.1 Một lớp trừu tượng xác định quy trình khung.
Bởi vì quy trình mà mỗi yêu cầu đều trải qua gồm các bước sau:
- Truy vấn thông tin người bán
- Ký tin nhắn yêu cầu
- Gửi yêu cầu http đi
- Xác minh chữ ký của tin nhắn được trả lại
Vì vậy, chúng ta có thể định nghĩa một lớp trừu tượng chứa một số phương thức của quy trình yêu cầu. Các phương thức này trước tiên được định nghĩa là các phương thức trừu tượng:
- /**
- *Lớp trừu tượng xác định quy trình khung (truy vấn thông tin người bán, thêm chữ ký, yêu cầu http, xác minh chữ ký)
- */
- lớp trừu tượngDịch vụ thương gia trừu tượng{
-
- //Truy vấn thông tin người bán
- abstractqueryMerchantInfo();
- //Thêm chữ ký
- chữ ký trừu tượng();
- //yêu cầu http
- trừu tượnghttpRequest();
- //Xác minh chữ ký
- tóm tắt xác minhSinature();
-
- }
3.3.2 Đặt các bước phương thức chung đã xác định vào các lớp trừu tượng.
- lớp trừu tượngDịch vụ thương gia trừu tượng{
-
- //Quy trình phương thức mẫu
- ResphandlerTempPlate(yêu cầu){
- //Truy vấn thông tin người bán
- truy vấnMerchantInfo();
- //Thêm chữ ký
- chữ ký();
- //yêu cầu http
- httpYêu cầu();
- //Xác minh chữ ký
- xác minhSinature();
- }
- // Liệu Http có sử dụng proxy hay không (được cung cấp cho các lớp con để triển khai)
- tóm tắtbooleanisRequestByProxy();
- }
3.3.3 Đối với các bước không chắc chắn, hãy triển khai triển khai khác biệt cho các lớp con.
Bởi vì không chắc chắn có nên thực hiện quy trình proxy hay không nên nó được thực hiện bởi các lớp con.
Việc thực hiện yêu cầu của Người bán A:
- Công tyADịch vụImplextendsAbstractMerchantService{
- Trả lời(yêu cầu){
- trở lạihandlerTempPlate(yêu cầu);
- }
- //Sử dụng proxy http
- booleanisYêu cầu củaProxy(){
- trở lạiĐÚNG VẬY;
- }
Việc thực hiện yêu cầu của Người bán B:
- Công tyBDịch vụImplextendsTóm tắtThương giaDịch vụ{
- Trả lời(yêu cầu){
- trở lạihandlerTempPlate(yêu cầu);
- }
- // Công ty B không sử dụng đại lý
- booleanisYêu cầu củaProxy(){
- trở lạiSAI;
- }
4. Mẫu người quan sát
4.1 Kịch bản kinh doanh
Đăng nhập và đăng ký phải là tình huống kinh doanh phổ biến nhất. Lấy việc đăng ký làm ví dụ. Chúng ta thường gặp những tình huống tương tự, tức là sau khi người dùng đăng ký thành công, chúng ta sẽ gửi tin nhắn cho người dùng hoặc gửi email, v.v. nên chúng ta thường có đoạn mã sau:
- voidregister(Người sử dụngngười sử dụng){
- chènĐăng kýNgười dùng(người sử dụng);
- gửi tin nhắn IM();
- gửiEmail();
- }
Mã này có vấn đề gì? Nếu sản phẩm bổ sung yêu cầu: Bây giờ đối với người dùng đã đăng ký thành công, hãy gửi thông báo bằng tin nhắn văn bản cho người dùng. Vì vậy bạn phải thay đổi lại mã của phương thức đăng ký. . . Điều này có vi phạm nguyên tắc đóng mở không?
- voidregister(Người sử dụngngười sử dụng){
- chènĐăng kýNgười dùng(người sử dụng);
- gửi tin nhắn IM();
- gửi tin nhắn di động();
- gửiEmail();
- }
Hơn nữa, nếu giao diện gửi tin nhắn bị lỗi thì có ảnh hưởng đến việc đăng ký người dùng nữa không, lúc này có cần thêm phương thức không đồng bộ vào tin nhắn thông báo không? . .
Trong thực tế, chúng ta có thể sử dụng tối ưu hóa mẫu quan sát.
4.2 Định nghĩa mẫu quan sát
Mẫu Observer xác định mối quan hệ phụ thuộc một-nhiều giữa các đối tượng. Khi trạng thái của một đối tượng thay đổi, tất cả các đối tượng phụ thuộc vào nó sẽ được thông báo và quá trình cập nhật nghiệp vụ được hoàn thành.
Mẫu người quan sát thuộc về mẫu hành vi Khi trạng thái của một đối tượng (người quan sát) thay đổi, tất cả các đối tượng phụ thuộc (đối tượng người quan sát) sẽ được thông báo và thông báo phát sóng sẽ được thực hiện. Các thành viên chính của nó là người quan sát và vật được quan sát.
Có thể quan sát: Đối tượng mục tiêu sẽ thông báo cho tất cả người quan sát khi trạng thái của nó thay đổi.
Người quan sát: chấp nhận thông báo thay đổi trạng thái từ các dịch vụ được quan sát và thực thi các dịch vụ được xác định trước.
Kịch bản sử dụng: Sau khi hoàn thành một việc gì đó, kịch bản thông báo không đồng bộ. Ví dụ: đăng nhập thành công, gửi tin nhắn IM, v.v.
4.3 Sử dụng mẫu quan sát
Mẫu quan sát tương đối đơn giản để thực hiện.
- Một lớp có thể quan sát được Có thể quan sát được;
- Nhiều người quan sátNgười quan sát;
- Thực hiện khác biệt của người quan sát
- Đóng gói mẫu quan sát cổ điển: EventBus trong thực tế
4.3.1 Một lớp được quan sát Có thể quan sát và nhiều người quan sát Người quan sát.
- công cộnglớpCó thể quan sát được{
-
- danh sách riêng tư
người quan sát
- =mảngmớiDanh sách
();
- riêng tưsố nguyêntình trạng;
-
- công cộngsố nguyênlấyTrạng Thái(){
- trở lạitình trạng;
- }
-
- công cộngvoidsetState(số nguyêntình trạng){
- thông báo cho tất cả người quan sát();
- }
-
- //Thêm người quan sát
- công cộngvoidaddServer(Người quan sát){
- người quan sát.thêm vào(người quan sát);
- }
-
- // xóa người quan sát
- công cộngvoidremoveServer(Người quan sát){
- observers.remove(người quan sát);
- }
- //thông báo
- công cộngvoidnotifyAllObservers(số nguyêntình trạng){
- nếu(trạng thái!=1){
- Hệ thống.ngoài.println("Không phải trạng thái thông báo");
- trở lại;
- }
-
- vì(Người quan sát:người quan sát){
- người quan sát.doEvent();
- }
- }
- }
4.3.2 Việc thực hiện khác biệt của người quan sát.
- //Người quan sát
- giao diệnObserver{
- Sự kiện voiddo();
- }
- //Tôi đang nhắn tin
- IMMessageObserverimplementsObserver{
- voiddoSự kiện(){
- Hệ thống.ngoài.println("Gửi tin nhắn IM");
- }
- }
-
- //SMS di động
- MobileNoObserverimplementsObserver{
- voiddoSự kiện(){
- Hệ thống.ngoài.println("Gửi tin nhắn SMS");
- }
- }
- //EmailKhông
- EmailObserverthực hiệnObserver{
- voiddoSự kiện(){
- Hệ thống.ngoài.println("Gửi tin nhắn email");
- }
- }
4.3.3 EventBus thực chiến.
Vẫn còn một chút rắc rối khi tự mình tạo một bộ mã mẫu quan sát. Trên thực tế, Guava EventBus được đóng gói, nó cung cấp một tập hợp bus sự kiện dựa trên chú thích và API có thể được sử dụng linh hoạt, điều này thật tuyệt vời.
Chúng ta hãy xem mã thực tế của EventBus Đầu tiên, chúng ta có thể khai báo một lớp EventBusCenter, tương tự như vai trò của Observerable ở trên.
- công cộnglớpEventBusCenter{
-
- riêng tưtĩnhEventBuseventBus=newEventBus();
-
- privateEventBusCenter() {
- }
-
- công cộngtĩnhSự kiệnBusgetInstance()
- trở lạisự kiệnBus;
- }
- //Thêm người quan sát
- công cộngtĩnhvoidregister(Objectobj){
- eventBus.register(obj);
- }
- // xóa người quan sát
- công cộngtĩnhvoidunregister(Objectobj){
- eventBus.unregister(obj);
- }
- // Đẩy tin nhắn đến người quan sát
- công cộngtĩnhvoidpost(Objectobj){
- eventBus.post(đối tượng);
- }
- }
Sau đó khai báo EventListener của người quan sát.
- công cộnglớpEventListener{
-
- @Subscribe//Đã thêm đăng ký, đánh dấu phương thức này ở đây là phương thức xử lý sự kiện
- công cộngvoidhandle(Thông báo sự kiệnThông báo sự kiện){
- Hệ thống.ngoài.println("Gửi tin nhắn IM"+notifyEvent.getImNo());
- Hệ thống.ngoài.println("Gửi tin nhắn SMS"+notifyEvent.getMobileNo());
- Hệ thống.ngoài.println("Gửi tin nhắn email"+notifyEvent.getEmailNo());
- }
- }
-
- //Lớp sự kiện thông báo
- công cộnglớpThông báoSự kiện{
-
- privateStringmobileKhông;
-
- Chuỗi email riêng tưKhông;
-
- Stringim riêng tưKhông;
-
- công cộngNotifyEvent(StringmobileNo,StringemailNo,StringimNo){
- this.mobileNo=mobileNo;
- this.emailNo=emailNo;
- this.imNo=imNo;
- }
- }
Sử dụng thử nghiệm demo:
- công cộnglớpEventBusDemoTest{
-
- công cộngtĩnhvoidmain(Chuỗi[]đối số){
-
- EventListenereventListener=newEventListener();
- EventBusCenter.register(eventListener);
- EventBusCenter.post(newNotifyEvent("13372817283","123@qq.com","666"));
- }
- }
Kết quả chạy:
- Gửi tin nhắn IM 666
- Gửi tin nhắn SMS 13372817283
- Gửi tin nhắn email 123@qq.com
5. Mẫu nhà xưởng
5.1 Kịch bản kinh doanh
Mẫu nhà máy thường được sử dụng cùng với mẫu chiến lược. Được sử dụng để tối ưu hóa một số lượng lớn các câu lệnh điều kiện if...else...or switch...case...có điều kiện.
Hãy lấy ví dụ về mẫu chiến lược ở phần đầu tiên. Tạo các đối tượng phân tích cú pháp khác nhau tùy theo các loại phân tích tệp khác nhau.
- IFileStrategygetFileStrategy(FileTypeResolveEnumfileType){
- IFileStrategy Chiến lược tập tin;
- nếu(fileType=FileTypeResolveEnum.File_A_RESOLVE){
- fileStrategy=newAFileResolve();
- }khácnếu(fileType=FileTypeResolveEnum.File_A_RESOLV){
- fileStrategy=newBFileResolve();
- }khác{
- fileStrategy=newDefaultFileResolve();
- }
- trở lạifileChiến lược;
- }
Trên thực tế, đây là mẫu nhà máy, xác định giao diện để tạo đối tượng và cho phép các lớp con của nó quyết định lớp nhà máy nào sẽ khởi tạo. Mẫu nhà máy trì hoãn quá trình tạo cho đến lớp con.
Ví dụ về mẫu chiến lược không sử dụng mã trước đó mà sử dụng các đặc điểm của lò xo để tạo mẫu nhà máy Haha, các bạn có thể quay lại ví dụ và xem xét kỹ hơn, tôi sẽ chuyển mã xuống và đưa ra. thử lại đi.
- /**
- *@author tài khoản public: Cậu bé nhặt ốc
- */
- @Thành phần
- công cộngclassStrategyUseServiceimplementsApplicationContextAware{
-
- Bản đồ riêng tư
iFileStrategyMap=newConcurrentHashMap<>();
-
- // Đặt tất cả các đối tượng được phân tích cú pháp loại tệp vào bản đồ. Khi cần sử dụng chúng, bạn chỉ cần lấy chúng trong tầm tay. Đây là biểu hiện của mô hình nhà máy.
- @Ghi đè
- công cộngvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
- Bản đồ
tmepMap=applicationContext.getBeansOfType(IFileStrategy.class);
- Bản đồ tmep.giá trị().forEach(strategyService->iFileStrategyMap.put(strategyService.gainFileType(),strategyService));
- }
- }
5.2 Sử dụng mẫu nhà máy
Việc xác định Factory Pattern cũng tương đối đơn giản
Giao diện gốc cung cấp phương thức tạo các đối tượng khác nhau.
Các lớp con của nó triển khai giao diện gốc và xây dựng các đối tượng khác nhau.
Sử dụng mô hình nhà máy.
5.3.1 Giao diện xuất xưởng.
- giao diệnIFileResolveFactory{
- giải quyết vô hiệu();
- }
5.3.2 Các lớp con khác nhau triển khai giao diện của nhà máy.
- classAFileResolveimplementsIFileResolveFactory{
- voidresolve(){
- Hệ thống.ngoài.println("Phân tích loại tệp A");
- }
- }
-
- classBFileResolveimplementsIFileResolveFactory{
- voidresolve(){
- Hệ thống.ngoài.println("Phân tích loại tệp B");
- }
- }
-
- classDefaultFileResolveimplementsIFileResolveFactory{
- voidresolve(){
- Hệ thống.ngoài.println("Phân tích loại tệp mặc định");
- }
- }
5.3.3 Sử dụng chế độ xuất xưởng.
- //Xây dựng các đối tượng nhà máy khác nhau
- IFileResolveFactoryfileResolveFactory;
- nếu(fileType="A"){
- fileResolveFactory=newAFileResolve();
- }khácnếu(fileType="B"){
- fileResolveFactory=newBFileResolve();
- }khác{
- fileResolveFactory=newDefaultFileResolve();
- }
-
- fileResolveFactory. giải quyết();
Trong trường hợp bình thường, ở chế độ xuất xưởng, bạn sẽ không thấy đoạn mã trên. Mẫu nhà máy sẽ xuất hiện cùng với các mẫu thiết kế khác như mẫu chiến lược.
6. Mẫu đơn
6.1 Kịch bản kinh doanh
Mẫu singleton đảm bảo rằng một lớp chỉ có một phiên bản và cung cấp một điểm truy cập toàn cầu để truy cập nó. Kết nối giữa I/O và cơ sở dữ liệu thường được triển khai bằng chế độ đơn. Trình quản lý tác vụ trong Windows cũng là một mô hình đơn lẻ điển hình.
Hãy xem một ví dụ về mẫu singleton.
- /**
- *Tài khoản chính thức: Cậu bé nhặt ốc
- */
- công cộnglớpLanHanSingleton{
-
- riêng tưtĩnhLanHanSingleton trường hợp;
-
- riêng tưLanHanSingleton(){
-
- }
-
- công cộngtĩnhLanHanSingletongetInstance(){
- nếu(trường hợp==vô giá trị){
- thể hiện=newLanHanSingleton();
- }
- trở lạiví dụ;
- }
-
- }
-
Ví dụ trên là cách triển khai singleton theo phong cách lười biếng. Thật lười biếng khi chỉ tạo các phiên bản khi cần thiết. Nếu nó tồn tại, nó sẽ được trả về, nếu không, nó sẽ được tạo. Bạn cần thêm từ khóa được đồng bộ hóa, nếu không có thể xảy ra vấn đề bảo mật tuyến tính.
6.2 Phương pháp viết cổ điển của mẫu đơn
Trên thực tế, có một số phương pháp triển khai chế độ singleton, chẳng hạn như chế độ đói, khóa kiểm tra kép, lớp bên trong tĩnh, liệt kê và các phương pháp triển khai khác.
6.2.1 Chế độ người đói.
- công cộnglớpEHanSingleton{
-
- riêng tưtĩnhEHanSingletoninstance=newEHanSingleton();
-
- riêng tưEHanSingleton(){
- }
-
- công cộngtĩnhEHanSingletongetInstance(){
- trở lạiví dụ;
- }
-
- }
Chế độ đói tương đối đói và siêng năng. Phiên bản đã được tạo trong quá trình khởi tạo. Bất kể bạn có sử dụng nó sau này hay không, trước tiên bạn cần phải tạo một phiên bản mới. Không có vấn đề an toàn luồng nào với điều này, nhưng nó gây lãng phí dung lượng bộ nhớ.
6.2.2 Khóa xác minh kép.
- công cộnglớpDoubleCheckSingleton{
-
- riêng tưtĩnhTrường hợp DoubleCheckSingleton;
-
- riêng tưDoubleCheckSingleton(){}
-
- công cộngtĩnhDoubleCheckSingletongetInstance(){
- nếu(trường hợp==vô giá trị){
- đồng bộ(DoubleCheckSingleton.class){
- nếu(trường hợp==vô giá trị){
- thể hiện=newDoubleCheckSingleton();
- }
- }
- }
- trở lạiví dụ;
- }
- }
Chế độ đơn được thực hiện bằng khóa kiểm tra kép kết hợp những ưu điểm và nhược điểm của phong cách lười biếng và phong cách đói khát. Trong ví dụ mã trên, một lớp phán đoán có điều kiện if được thêm vào bên trong và bên ngoài từ khóa được đồng bộ hóa, điều này không chỉ đảm bảo an toàn cho luồng mà còn cải thiện hiệu quả thực thi so với khóa trực tiếp và cũng tiết kiệm dung lượng bộ nhớ.
6.2.3 Các lớp bên trong tĩnh.
- công cộnglớpInnerClassSingleton{
-
- riêng tưtĩnhlớpInnerClassSingletonHolder{
- riêng tưtĩnhfinalInnerClassSingletonINSTANCE=newInnerClassSingleton();
- }
-
- privateInnerClassSingleton(){}
-
- công cộngtĩnhfinalInnerClassSingletongetInstance(){
- trở lạiInnerClassSingletonHolder.INSTANCE;
- }
- }
Việc triển khai các lớp bên trong tĩnh có phần giống với khóa xác minh kép. Tuy nhiên, phương pháp này chỉ phù hợp với các tình huống miền tĩnh. Phương pháp khóa kiểm tra kép có thể được sử dụng khi miền phiên bản yêu cầu khởi tạo chậm.
6.2.4 Đếm.
- công cộngenumSingletonEnum{
-
- VÍ DỤ;
- công cộngSingletonEnumgetInstance() {
- trở lạiVÍ DỤ;
- }
- }
Singleton được thực hiện bởi bảng liệt kê, mã ngắn gọn và rõ ràng. Và nó cũng tự động hỗ trợ cơ chế tuần tự hóa để ngăn chặn tuyệt đối nhiều lần khởi tạo.
Liên kết gốc: https://mp.weixin.qq.com/s/5OSKWsesOoottffNHEDBWQ.
Bài cuối cùng là về thực chiến! Đến đây là kết thúc bài viết về những mẫu thiết kế thường được sử dụng trong công việc, nếu bạn muốn biết thêm về cách triển khai thực tế! Để biết thông tin về các mẫu thiết kế thường được sử dụng trong công việc, vui lòng tìm kiếm các bài viết của CFSDN hoặc tiếp tục duyệt các bài viết liên quan. Tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!