Cách tạo mã OO tốt hơn trong ứng dụng điều khiển cơ sở dữ liệu quan hệ nơi cơ sở dữ liệu được thiết kế kém


19

Tôi đang viết một ứng dụng web Java bao gồm chủ yếu là một loạt các trang tương tự, trong đó mỗi trang có một vài bảng và một bộ lọc áp dụng cho các bảng đó. Dữ liệu trên các bảng này xuất phát từ cơ sở dữ liệu SQL.

Tôi đang sử dụng myBatis dưới dạng ORM, có thể không phải là lựa chọn tốt nhất trong trường hợp của tôi, vì cơ sở dữ liệu được thiết kế kém và mybatis là một công cụ định hướng cơ sở dữ liệu nhiều hơn.

Tôi thấy rằng tôi đang viết rất nhiều mã trùng lặp bởi vì thiết kế kém của cơ sở dữ liệu, tôi phải viết các truy vấn khác nhau cho những điều tương tự vì các truy vấn đó có thể rất khác nhau. Đó là, tôi không thể dễ dàng tham số hóa các truy vấn. Điều này truyền vào mã của tôi và thay vì điền các hàng trên các cột trong bảng của tôi bằng một vòng lặp đơn giản, tôi có mã như:

lấy dữ liệu A (p1, ..., pi);

lấy dữ liệu B (p1, ..., pi);

lấy dữ liệu C (p1, ..., pi);

lấy dữ liệu D (p1, ..., pi); ...

Và điều này sớm bùng nổ khi chúng ta có các bảng khác nhau với các cột khác nhau.

Nó cũng làm tăng thêm sự phức tạp khi tôi sử dụng "wicket", thực chất là ánh xạ các đối tượng đến các phần tử html trong trang. Vì vậy, mã Java của tôi trở thành một bộ chuyển đổi giữa cơ sở dữ liệu và giao diện người dùng, điều này giúp tôi tạo ra rất nhiều hệ thống dây, mã soạn sẵn với một số logic xen kẽ trong đó.

Liệu giải pháp chính xác có bao bọc các trình ánh xạ ORM với một bộ mở rộng thể hiện giao diện đồng nhất hơn với db hoặc có cách nào tốt hơn để xử lý mã spaghetti này mà tôi đang viết không?

EDIT: Thông tin thêm về cơ sở dữ liệu

Cơ sở dữ liệu chủ yếu chứa thông tin cuộc gọi điện thoại. Thiết kế nghèo nàn bao gồm:

Các bảng có ID nhân tạo làm khóa chính không liên quan gì đến kiến ​​thức miền.

Không có duy nhất, kích hoạt, kiểm tra hoặc khóa ngoại nào.

Các trường có tên chung khớp với các khái niệm khác nhau cho các bản ghi khác nhau.

Các bản ghi chỉ có thể được phân loại bằng cách lai với các bảng khác với các điều kiện khác nhau.

Các cột nên là số hoặc ngày được lưu dưới dạng chuỗi.

Tóm lại, một thiết kế lộn xộn / lười biếng xung quanh.


7
Là sửa thiết kế cơ sở dữ liệu là một lựa chọn?
RMalke

1
Hãy giải thích làm thế nào là cơ sở dữ liệu được thiết kế kém.
Tulains Córdova

@Renan Malke Stigliani Thật không may, vì có phần mềm kế thừa phụ thuộc vào nó, tuy nhiên tôi đã nhân đôi một số bảng có thiết kế hơi khác và điền vào chúng, giúp đơn giản hóa mã. Tuy nhiên, tôi không tự hào về điều này và tôi không muốn sao chép các bảng một cách bừa bãi
DPM

1
Cuốn sách này có thể cung cấp cho bạn một số thông tin thú vị về cách bạn có thể bắt đầu sửa lỗi cơ sở dữ liệu và giữ cho mã kế thừa hoạt động: amazon.com/iêu
HLGEM

4
Hầu hết các vấn đề bạn liệt kê. . . không. Việc sử dụng các khóa thay thế thay vì các khóa tự nhiên thực sự là một khuyến nghị khá chuẩn hiện nay; không "thiết kế kém" chút nào. Việc thiếu các ràng buộc và sử dụng các loại cột không phù hợp là một ví dụ tốt hơn cho đến khi "thiết kế kém", nhưng nó thực sự không ảnh hưởng đến mã ứng dụng của bạn (trừ khi bạn có kế hoạch lạm dụng những vấn đề này?).
ruakh

Câu trả lời:


53

Định hướng đối tượng có giá trị cụ thể vì các loại kịch bản này phát sinh và nó cung cấp cho bạn các công cụ để thiết kế trừu tượng hợp lý cho phép bạn gói gọn sự phức tạp.

Câu hỏi thực sự ở đây là, bạn gói gọn sự phức tạp đó ở đâu?

Vì vậy, hãy để tôi lùi lại một lúc và nói về "sự phức tạp" mà tôi đang đề cập ở đây. Vấn đề của bạn (theo tôi hiểu; sửa tôi nếu tôi sai) là một mô hình kiên trì không phải là mô hình có thể sử dụng hiệu quả cho các nhiệm vụ bạn cần hoàn thành với dữ liệu. Nó có thể hiệu quả và có thể sử dụng cho các nhiệm vụ khác, nhưng không phải cho các nhiệm vụ của bạn .

Vậy chúng ta phải làm gì khi có dữ liệu không có mô hình tốt cho phương tiện của mình?

Dịch. Đó là điều duy nhất bạn có thể làm. Bản dịch đó là "sự phức tạp" mà tôi đề cập ở trên. Vì vậy, bây giờ chúng tôi chấp nhận chúng tôi sẽ dịch mô hình, chúng tôi cần quyết định một vài yếu tố.

Chúng ta có cần dịch cả hai hướng không? Cả hai hướng sẽ được dịch giống nhau, như trong:

(Tbl A, Tbl B) -> Obj X (đọc)

Obj X -> (Tbl A, Tbl B) (viết)

hoặc các hoạt động chèn / cập nhật / xóa đại diện cho một loại đối tượng khác để bạn đọc dữ liệu là Obj X, nhưng dữ liệu được chèn / cập nhật từ Obj Y? Một trong hai cách bạn muốn đi, hoặc nếu không có cập nhật / chèn / xóa là những yếu tố quan trọng trong nơi bạn muốn đặt bản dịch.


Bạn dịch ở đâu?

Quay lại với tuyên bố đầu tiên tôi đã đưa ra trong câu trả lời này; OO cho phép bạn gói gọn độ phức tạp và điều tôi đề cập ở đây là thực tế không chỉ bạn nên mà còn phải gói gọn sự phức tạp đó nếu bạn muốn đảm bảo nó không bị rò rỉ và thấm vào tất cả mã của bạn. Đồng thời, điều quan trọng là phải nhận ra bạn không thể có một sự trừu tượng hoàn hảo, vì vậy hãy bớt lo lắng về điều đó hơn là việc có một cách rất hiệu quả và có thể sử dụng được.

Một lần nữa bây giờ; vấn đề của bạn là: bạn đặt sự phức tạp này ở đâu? Vâng, bạn có sự lựa chọn.

Bạn có thể làm điều đó trong cơ sở dữ liệu bằng cách sử dụng các thủ tục được lưu trữ. Điều này có nhược điểm là thường không chơi tốt với ORM nhưng điều đó không phải lúc nào cũng đúng. Thủ tục lưu trữ đủ khả năng một số lợi ích, bao gồm hiệu suất thường xuyên. Tuy nhiên, các quy trình được lưu trữ có thể yêu cầu rất nhiều bảo trì, nhưng tùy thuộc vào bạn để phân tích kịch bản cụ thể của bạn và cho biết việc bảo trì sẽ nhiều hơn hoặc ít hơn các lựa chọn khác. Cá nhân tôi rất thành thạo với các thủ tục được lưu trữ, và như vậy thực tế là tài năng có sẵn này làm giảm chi phí; không bao giờ đánh giá thấp giá trị của việc ra quyết định dựa trên những gì bạn làm biết. Đôi khi giải pháp tối ưu có thể tối ưu hơn giải pháp chính xác bởi vì bạn hoặc nhóm của bạn biết cách tạo và duy trì nó tốt hơn giải pháp tối ưu.

Một tùy chọn trong cơ sở dữ liệu là khung nhìn. Tùy thuộc vào máy chủ cơ sở dữ liệu của bạn, chúng có thể tối ưu cao hoặc tối ưu phụ hoặc thậm chí không hiệu quả, một trong những nhược điểm có thể là thời gian truy vấn tùy thuộc vào tùy chọn lập chỉ mục nào có sẵn trong cơ sở dữ liệu của bạn. Lượt xem trở thành một lựa chọn thậm chí tốt hơn nếu bạn không bao giờ cần thực hiện bất kỳ sửa đổi dữ liệu nào (chèn / cập nhật / xóa).

Bước qua cơ sở dữ liệu, bạn có chế độ chờ cũ bằng cách sử dụng mẫu kho lưu trữ. Đây là một phương pháp thử nghiệm thời gian có thể rất hiệu quả. Những hạn chế có xu hướng bao gồm tấm nồi hơi nhưng kho lưu trữ được trang bị tốt có thể tránh được một số lượng này và ngay cả khi những điều này dẫn đến số lượng đáng tiếc của tấm nồi hơi, kho lưu trữ có xu hướng là mã đơn giản dễ hiểu và duy trì cũng như trình bày một API tốt / trừu tượng. Ngoài ra các kho lưu trữ có thể tốt cho khả năng kiểm tra đơn vị của chúng mà bạn mất với các tùy chọn trong cơ sở dữ liệu.

Có những công cụ như trình ánh xạ tự động ngoài kia có thể giúp sử dụng ORM hợp lý khi chúng có thể dịch giữa mô hình cơ sở dữ liệu từ mô hình orm sang mô hình có thể sử dụng được, nhưng một số công cụ này có thể khó để duy trì / hiểu hành vi giống như ma thuật; mặc dù chúng tạo ra tối thiểu mã trên không dẫn đến chi phí bảo trì ít hơn khi được hiểu rõ.

Tiếp theo, bạn đang tiến xa hơn và xa hơn từ cơ sở dữ liệu , điều đó có nghĩa là sẽ có số lượng mã lớn hơn sẽ xử lý mô hình kiên trì chưa được dịch, điều này thực sự gây khó chịu. Trong các kịch bản này, bạn nói về việc đưa lớp dịch vào giao diện người dùng của bạn, có vẻ như bạn có thể đang làm bây giờ. Đây thường là một ý tưởng rất tồi tệ và phân rã khủng khiếp theo thời gian.


Bây giờ hãy bắt đầu nói chuyện điên rồ .

Đây Objectkhông phải là sự trừu tượng cuối cùng duy nhất tồn tại. Đã có rất nhiều sự trừu tượng được phát triển trong nhiều năm mà khoa học máy tính đã được nghiên cứu và thậm chí trước đó từ nghiên cứu toán học. Nếu chúng ta sẽ bắt đầu sáng tạo, hãy bắt đầu nói về những khái niệm trừu tượng có sẵn đã được nghiên cứu.

Có người mẫu diễn viên.Đây là một cách tiếp cận thú vị bởi vì nó nói rằng tất cả những gì bạn làm là gửi tin nhắn đến mã khác, ủy nhiệm hiệu quả tất cả công việc cho mã khác, rất hiệu quả trong việc gói gọn sự phức tạp khỏi tất cả mã của bạn. Điều này có thể hoạt động trong chừng mực bạn gửi tin nhắn cho một diễn viên nói rằng "Tôi cần Obj X gửi cho Y" và bạn có một thùng chứa chờ phản hồi tại vị trí Y, sau đó xử lý Obj X. Bạn thậm chí có thể gửi tin nhắn hướng dẫn "Tôi cần Obj X và tính toán Y, Z cho nó" và sau đó bạn thậm chí không cần phải chờ đợi; bản dịch xảy ra ở phía bên kia của thông điệp đó và bạn có thể tiếp tục nếu bạn không cần đọc kết quả của nó. Điều này có thể là lạm dụng nhẹ mô hình diễn viên cho mục đích của bạn, nhưng tất cả phụ thuộc;

Một ranh giới đóng gói khác là ranh giới quá trình. Chúng có thể được sử dụng để phân tách độ phức tạp rất hiệu quả. Bạn có thể tạo mã dịch dưới dạng dịch vụ web trong đó giao tiếp là HTTP đơn giản, sử dụng SOAP, REST hoặc nếu bạn thực sự muốn giao thức của riêng mình (không được đề xuất). STOMP không hoàn toàn là một giao thức mới tệ hơn. Hoặc sử dụng dịch vụ daemon bình thường với ống bộ nhớ công khai cục bộ hệ thống để liên lạc lại rất nhanh bằng cách sử dụng bất kỳ giao thức nào bạn chọn. Điều này thực sự có một số lợi ích khá tốt:

  • Bạn có thể có nhiều quy trình đang chạy để dịch cho hỗ trợ phiên bản cũ hơn và mới hơn đồng thời cho phép bạn cập nhật dịch vụ dịch để công khai mô hình đối tượng V2, sau đó riêng biệt tại một điểm sau đó cập nhật mã tiêu thụ để làm việc với đối tượng mới mô hình.
  • Bạn có thể thực hiện những điều thú vị như ghim quy trình vào lõi để thực hiện, bạn cũng có được một lượng an toàn bảo mật trong phương pháp này bằng cách thực hiện quy trình duy nhất chạy với các đặc quyền bảo mật để chạm vào dữ liệu đó.
  • Bạn sẽ nhận được một ranh giới rất mạnh khi bạn nói về các ranh giới quy trình sẽ được cố định để đảm bảo sự rò rỉ tối thiểu của bạn trong một thời gian dài vì viết mã trong không gian dịch sẽ không thể được gọi bên ngoài không gian dịch vì chúng sẽ không chia sẻ phạm vi quy trình, đảm bảo tập hợp các kịch bản sử dụng cố định theo hợp đồng.
  • Khả năng cập nhật không đồng bộ / không chặn trở nên đơn giản hơn.

Hạn chế rõ ràng là bảo trì nhiều hơn là cần thiết, chi phí truyền thông ảnh hưởng đến hiệu suất và bảo trì.


Có rất nhiều cách tuyệt vời để gói gọn sự phức tạp có thể cho phép sự phức tạp đó được đặt ở những nơi kỳ lạ và kỳ lạ hơn trong hệ thống của bạn. Sử dụng các hình thức của các hàm bậc cao hơn (thường được làm giả bằng cách sử dụng mẫu chiến lược hoặc các dạng khác nhau của các mẫu đối tượng), bạn có thể thực hiện một số điều rất thú vị.

Đúng vậy, hãy bắt đầu nói về một đơn nguyên. Bạn có thể tạo lớp dịch này theo cách rất độc lập với các chức năng cụ thể nhỏ, thực hiện các bản dịch độc lập cần thiết, nhưng ẩn tất cả các chức năng dịch đó không nhìn thấy được để chúng khó tiếp cận với mã bên ngoài. Điều này có lợi ích là giảm sự phụ thuộc vào chúng cho phép chúng thay đổi dễ dàng mà không ảnh hưởng đến nhiều mã bên ngoài. Sau đó, bạn tạo một lớp chấp nhận các hàm bậc cao hơn (hàm ẩn danh, hàm lambda, đối tượng chiến lược, tuy nhiên bạn cần cấu trúc chúng) hoạt động trên bất kỳ đối tượng kiểu mô hình OO đẹp nào. Sau đó, bạn để mã bên dưới chấp nhận các hàm đó thực hiện nghĩa đen bằng cách sử dụng các phương thức dịch thích hợp.

Điều này tạo ra một ranh giới nơi tất cả các bản dịch không chỉ tồn tại ở phía bên kia của ranh giới cách xa tất cả mã của bạn; nó chỉ được sử dụng ở phía đó cho phép phần còn lại của mã của bạn thậm chí không biết gì về nó ngoài vị trí điểm vào của ranh giới đó.

Ok, vâng, điều đó thực sự đang nói điên rồ, nhưng ai biết được; bạn có thể trở nên điên rồ (nghiêm túc, không thực hiện các đơn vị với tỷ lệ điên rồ dưới 88%, có nguy cơ thực sự gây thương tích cơ thể).


4
Wow, thật là một câu trả lời cực kỳ toàn diện. Tôi sẽ nâng cao điều này hơn một lần nếu chỉ SE sẽ cho tôi.
Marjan Venema

11
Khi nào phiên bản điện ảnh ra mắt?
yannis

3
@JimmyHoffa Bravo ạ !!! Tôi sẽ đánh dấu câu trả lời này và cho con gái tôi xem khi nó lớn hơn.
Tombatron

4

Đề xuất của tôi:

Tạo các khung nhìn cơ sở dữ liệu:

  1. Đặt tên có ý nghĩa cho các cột
  2. Thực hiện "vượt qua với các bảng khác với các điều kiện khác nhau" để bạn có thể ẩn sự phức tạp đó.
  3. Chuyển đổi số hoặc ngày được lưu trữ dưới dạng chuỗi thành số và ngày tương ứng.
  4. Tạo sự độc đáo ở nơi không có, theo một số tiêu chí.

Ý tưởng là tạo ra một mặt tiền mô phỏng một thiết kế tốt hơn trên đỉnh của cái xấu.

Sau đó làm cho ORM liên quan đến mặt tiền đó thay vì các bảng thực.

Điều này không đơn giản hóa chèn thêm mặc dù.


Sử dụng các khung nhìn cơ sở dữ liệu có vẻ như là một ý tưởng tuyệt vời và quá trình hành động tao nhã nhất giúp loại bỏ sự xấu xí ở mức thấp nhất, vì một số lý do tôi đã không xem xét nó. Cảm ơn bạn.
DPM

3

Tôi có thể thấy cách lược đồ cơ sở dữ liệu hiện tại của bạn khiến bạn viết mã và truy vấn cụ thể hơn cho các tác vụ có thể được trừu tượng hóa bằng lược đồ được thiết kế tốt hơn, nhưng nó không cản trở khả năng của bạn để viết mã hướng đối tượng tốt.

  • Ghi nhớ các nguyên tắc RẮN .
  • Viết mã có thể dễ dàng kiểm tra đơn vị (thường đi qua các nguyên tắc RẮN).
  • Giữ logic kinh doanh của bạn tách biệt với logic hiển thị của bạn.
  • Đọc tài liệu và ví dụ về Wicket của Apache - khung này có thể giúp bạn tiết kiệm nhiều mã soạn sẵn hơn bạn nghĩ, vì vậy hãy tìm hiểu cách sử dụng nó hiệu quả.
  • Giữ logic phải xử lý cơ sở dữ liệu trong một lớp riêng biệt cung cấp giao diện sạch mà logic nghiệp vụ của bạn có thể hoạt động. Theo cách này, nếu bạn (hoặc một người duy trì trong tương lai) từng có cơ hội cải thiện lược đồ, họ có thể làm như vậy mà không cần quá nhiều thay đổi đối với logic nghiệp vụ.

Khi bạn thấy mình làm việc với một lược đồ cơ sở dữ liệu không hoàn hảo, bạn sẽ dễ dàng nắm bắt được tất cả các cách khiến công việc của bạn trở nên khó khăn hơn, nhưng đến một lúc nào đó, bạn phải gạt bỏ những phàn nàn đó và tận dụng nó.

Hãy nghĩ về nó như một cơ hội để sử dụng sự sáng tạo của bạn để viết mã sạch, có thể tái sử dụng, dễ bảo trì mặc dù lược đồ không hoàn hảo.


1

Trả lời câu hỏi ban đầu của bạn về mã hướng đối tượng tốt hơn, tôi khuyên bạn nên sử dụng các đối tượng nói SQL . Về bản chất, ORM đi ngược lại các nguyên tắc hướng đối tượng, vì nó hoạt động dựa trên một đối tượng và đối tượng trong OOP là một thực thể tự cung tự cấp, người có tất cả các nguồn lực để thực hiện mục tiêu của mình. Tôi chắc rằng cách tiếp cận này có thể làm cho mã của bạn đơn giản hơn.

Nói về không gian vấn đề, tức là tên miền của bạn, tôi sẽ cố gắng xác định các gốc tổng hợp . Đây là ranh giới nhất quán của tên miền của bạn. Ranh giới tuyệt đối phải giữ sự nhất quán của nó mọi lúc. Tập hợp giao tiếp thông qua các sự kiện tên miền. Nếu bạn có một hệ thống đủ lớn, có lẽ bạn nên bắt đầu chia tách nó trên các hệ thống con (gọi nó là SOA, microservice, các hệ thống khép kín, v.v.)

Tôi cũng sẽ xem xét sử dụng CQRS - nó có thể đơn giản hóa rất nhiều cả mặt viết và đọc của bạn. Hãy chắc chắn đọc bài viết của Udi Dahan về chủ đề này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.