Những nhược điểm của mẫu ActiveRecord là gì?


30

Tôi tò mò những hạn chế của việc sử dụng mẫu ActiveRecord cho các đối tượng truy cập / kinh doanh dữ liệu. Điều duy nhất tôi có thể nghĩ ra khỏi đỉnh đầu là nó vi phạm Nguyên tắc Trách nhiệm duy nhất, nhưng mẫu AR đủ phổ biến để lý do này không có vẻ "đủ tốt" để biện minh cho việc không sử dụng nó (tất nhiên là của tôi Chế độ xem có thể bị sai lệch vì thường không có mã nào tôi làm việc tuân theo bất kỳ nguyên tắc RẮN nào).

Cá nhân tôi không phải là fan hâm mộ của ActiveRecord (ngoại trừ việc viết ứng dụng Ruby on Rails, trong đó AR cảm thấy "tự nhiên") vì cảm giác như lớp học đang hoạt động quá nhiều và truy cập dữ liệu không nên tự đến lớp giải quyết. Tôi thích sử dụng các kho lưu trữ trả về các đối tượng kinh doanh. Hầu hết các mã tôi làm việc với xu hướng sử dụng một biến thể của ActiveRecord, ở dạng (Tôi không biết tại sao phương thức này là một boolean):

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

hoặc đôi khi là cách "truyền thống" hơn để có một public static Foo FindFooByID(int fooID)phương pháp cho người tìm và một cái gì đó dọc theo dòng public void Save()để lưu / cập nhật.

Tôi nhận được rằng ActiveRecord là thường đơn giản hơn nhiều để thực hiện và sử dụng, nhưng nó có vẻ hơi quá đơn giản cho các ứng dụng phức tạp và bạn có thể có một kiến trúc mạnh mẽ hơn bằng cách đóng gói logic truy cập dữ liệu của bạn trong một Repository (chưa kể đến việc nó dễ dàng hơn để trao đổi trên chiến lược truy cập dữ liệu, ví dụ có thể bạn sử dụng Procs + DataSets được lưu trữ và muốn chuyển sang LINQ hoặc một cái gì đó)

Vì vậy, những hạn chế khác đối với mô hình này cần được xem xét khi quyết định xem ActiveRecord có phải là ứng cử viên tốt nhất cho công việc không?

Câu trả lời:


28

Hạn chế chính là "các thực thể" của bạn nhận thức được sự kiên trì của chính họ dẫn đến rất nhiều quyết định thiết kế tồi tệ khác.

Các vấn đề khác là hầu hết các bộ công cụ ghi hoạt động cơ bản ánh xạ từ 1 đến 1 thành các trường bảng với các lớp không xác định. Điều này hoạt động trên quy mô nhỏ nhưng sụp đổ khi bạn có vấn đề khó giải quyết hơn.


Chà, để đối tượng của bạn biết về sự kiên trì của họ có nghĩa là bạn cần phải làm những việc như:

  • dễ dàng có kết nối cơ sở dữ liệu có sẵn ở khắp mọi nơi. Điều này thường dẫn đến mã hóa khó chịu hoặc một số loại kết nối tĩnh bị tấn công từ khắp mọi nơi.
  • các đối tượng của bạn có xu hướng trông giống SQL hơn các đối tượng.
  • khó có thể làm bất cứ điều gì trong ứng dụng bị ngắt kết nối vì cơ sở dữ liệu đã ăn sâu.

Cuối cùng, có một loạt các quyết định tồi tệ khác.


2
bạn có thể giải thích về "các quyết định thiết kế xấu khác" không?
kevin cline

2
Cảm ơn. Tôi chưa thấy những vấn đề này là một vấn đề trong quá trình phát triển Ruby on Rails. Vẫn có thể kiểm tra hành vi và sự kiên trì một cách riêng biệt. IMO tách sự kiên trì khỏi hành vi có ít giá trị thực tế.
kevin cline

@kevin: những thứ này ít trở ngại hơn với các tính năng của ruby ​​như mixins và gõ vịt. Với các ngôn ngữ tĩnh - như C #, đó là những gì OP đã sử dụng trong câu hỏi của mình - việc phân tách hai ngôn ngữ này sẽ khó khăn hơn một chút.
Wyatt Barnett

@Wayne: với tôi, các lớp chỉ là các hộp để đưa các phương thức vào - Tôi có thể tách logic kinh doanh khỏi sự kiên trì bằng cách đặt chúng vào các lớp riêng biệt hoặc tôi chỉ có thể tách chúng theo khái niệm bằng cách đảm bảo rằng các phương thức kinh doanh không làm tôi / Ôi Trong các ngôn ngữ có sự hỗ trợ ủy nhiệm kém (ví dụ Java), điều này giúp tiết kiệm một lượng lớn mã. OTOH, tôi mới vào Hibernate nên tôi có thể sai hoàn toàn.
kevin cline

4
Tôi sẽ thêm hai điều; 1. Việc ghép với cơ chế kiên trì làm cho mã khó, nếu không nói là không thể kiểm tra đơn vị đúng. 2. Active Record sẽ dán mã của bạn vào các mối quan hệ theo cơ chế bền vững tập trung, khiến cho việc phân chia nguyên khối của bạn trở nên rất khó khăn nếu bạn quyết định.
istepaniuk 7/07/2015

15

Hạn chế lớn nhất đối với hồ sơ hoạt động là tên miền của bạn thường được kết hợp chặt chẽ với một cơ chế lưu giữ cụ thể. Nếu cơ chế đó yêu cầu thay đổi toàn cầu, có lẽ từ sự kiên trì dựa trên tệp đến DB hoặc giữa các khung truy cập dữ liệu, MỌI lớp thực hiện mẫu này có thể thay đổi. Tùy thuộc vào ngôn ngữ, khung và thiết kế, thậm chí một thứ đơn giản như thay đổi vị trí của DB hoặc ai "sở hữu" nó có thể yêu cầu phải đi qua mọi đối tượng để cập nhật các phương thức truy cập dữ liệu (điều này không phổ biến trong hầu hết các ngôn ngữ cung cấp quyền truy cập dễ dàng để cấu hình tập tin với chuỗi kết nối).

Nó cũng thường yêu cầu bạn lặp lại chính mình. Hầu hết các cơ chế kiên trì có rất nhiều mã chung, để kết nối với DB và bắt đầu giao dịch. DRY (Đừng lặp lại chính mình) sẽ cho bạn biết là một lập trình viên để tập trung logic như vậy.

Nó cũng làm cho hoạt động nguyên tử khó khăn. Nếu một nhóm đối tượng phải được lưu theo kiểu toàn bộ hoặc không có gì (chẳng hạn như Hóa đơn và Hóa đơn và / hoặc Khách hàng và / hoặc GL), một đối tượng phải biết về tất cả các đối tượng khác này và kiểm soát sự tồn tại của chúng ( mở rộng phạm vi của đối tượng kiểm soát, các bản ghi liên kết lớn có thể dễ dàng trở thành "đối tượng thần" biết mọi thứ về sự phụ thuộc của chúng) hoặc kiểm soát toàn bộ giao dịch phải được xử lý từ bên ngoài miền (và trong trường hợp đó bạn đang sử dụng AR?)

Nó cũng "sai" từ góc độ Hướng đối tượng. Trong thế giới thực, một hóa đơn không biết cách tự nộp, vậy tại sao một đối tượng mã Hóa đơn sẽ biết cách tự lưu vào DB? Tất nhiên, việc tuân thủ quá mức tôn giáo đối với "các đối tượng chỉ nên mô hình hóa những gì đối tác trong thế giới thực của họ có thể làm" sẽ dẫn đến các mô hình miền thiếu máu (một hóa đơn cũng không biết cách tính tổng của chính nó, nhưng chia nhỏ cách tính tổng đó một đối tượng khác thường được coi là một ý tưởng tồi).


Trong Ruby, nơi mà sự kiên trì có thể được xác định hoặc trừu tượng hóa đằng sau một từ khóa hoặc lệnh rất đơn giản, có lẽ đó không phải là vấn đề. Trong .NET, đòi hỏi nhiều LoC hơn để thiết lập các cơ chế lưu giữ lâu dài khác nhau, thường có kết nối SQL cho mỗi đối tượng, đặc biệt là nếu nhiều đối tượng phải được lưu trong một giao dịch nguyên tử. Kết nối với DB trông giống nhau cho dù bạn đang lưu Hóa đơn hay Khách hàng. Bạn có thể trừu tượng hóa những thứ phổ biến thành một lớp cơ sở chung cho tất cả các đối tượng ActiveRecord trong cơ sở mã của bạn hoặc trích xuất nó vào lớp của chính nó (Kho lưu trữ)
KeithS

bạn có thể cung cấp các ví dụ về việc sử dụng Ruby ActiveRecord minh họa điểm của bạn không? Tôi không gặp phải những vấn đề này, nhưng ứng dụng của tôi khá nhỏ. Tôi có thể viết các chuyển đổi trong Ruby và triển khai chúng cho các triển khai DB khác nhau. Tôi thấy rằng ActiveRecord của Ruby rất hữu ích trong việc loại bỏ sự lặp lại, vì vậy tôi không hiểu tại sao bạn lại khẳng định rằng sử dụng ActiveRecord sẽ có tác dụng ngược lại. Tôi không gặp vấn đề gì với việc lưu: Tôi chỉ sửa đổi mô hình đối tượng và để AR cập nhật DB để phản ánh mô hình đối tượng.
kevin cline

2

Hạn chế cơ bản là, Nó làm cho mô hình miền của bạn trở nên phức tạp vì Nó không chỉ chứa logic kinh doanh mà còn cả thông tin bền bỉ.

Vì vậy, giải pháp là sử dụng triển khai ORM của Data Mapper . Điều đó tách ra khỏi lớp kiên trì và bây giờ chúng ta tập trung hơn vào logic kinh doanh thực thể. Học thuyết là ORM Mapper dữ liệu .

Nhưng cách tiếp cận này cũng có một số phức tạp, Đối với truy vấn bây giờ bạn phụ thuộc quá nhiều vào Data Mapper, tạo môi trường theo hướng truy vấn. Để đơn giản hóa nó, một lớp khác được giới thiệu giữa Mô hình miềnTrình ánh xạ dữ liệu được gọi là Kho lưu trữ .

Kho lưu trữ trừu tượng ra lớp kiên trì. Nó tạo cảm giác về lập trình hướng đối tượng theo nghĩa, nó là tập hợp của tất cả các đối tượng cùng loại (như tất cả các thực thể được lưu trữ trong bảng cơ sở dữ liệu) và bạn có thể thực hiện thao tác trên chúng như thao tác thu thập, thêm , xóa . chứa vv

Ví dụ: Thực thể người dùng , sẽ có UserRep repository đại diện cho bộ sưu tập cùng loại đối tượng người dùng (được lưu trữ trong bảng người dùng) mà bạn có thể thực hiện thao tác trên. Để truy vấn bảng người dùng, nó sử dụng Trình ánh xạ dữ liệu người dùng nhưng nó được trừu tượng hóa cho người dùng mô hình miền .

Mẫu kho lưu trữ là loại Lớp truy cập dữ liệu , một loại khác là Đối tượng truy cập dữ liệu chỉ khác nhau Kho lưu trữ có tính năng Tổng hợp gốc


Các mẫu lưu trữDAO khác nhau, DAO là truy cập dữ liệu chung, Kho lưu trữ dành cho việc lưu trữ tất cả các đối tượng cùng loại. Tức là tất cả các kho lưu trữ nên có cùng giao diện (như mảng hoặc danh sách). Vua phương pháp khác không thuộc về Kho lưu trữ. DAO ở cấp độ thấp hơn, Kho lưu trữ có thể sử dụng DAO. Thông thường, các lập trình viên (đặc biệt là PHP) sử dụng Kho lưu trữ dưới dạng DAO, nhưng điều đó không chính xác.
xmedeko
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.