2012-04-17 41 views
17

Dường như mỗi ví dụ tôi tìm thấy mẫu kho lưu trữ, việc triển khai thực hiện khác nhau theo một cách nào đó. Sau đây là hai ví dụ tôi chủ yếu tìm thấy.Thực hiện mẫu lưu trữ

interface IProductRepository 
{ 
    IQueryable<Product> FindAll(); 
} 

sau đó Thường có một lớp mà nói chuyện với các kho lưu trữ và gọi phương thức FindAll() và thực hiện bất kỳ thao tác như tìm kiếm các sản phẩm bắt đầu bằng chữ 's' hoặc sản phẩm lấy trong một thể loại cụ thể.

Ví dụ khác mà tôi tìm thấy rất nhiều đặt tất cả các phương pháp tìm vào kho

interface IProductRepository 
{ 
    IEnumerable<Product> GetProductsInCategory(int categoryId); 
    IEnumerable<Product> GetProductsStartingWith(string letter); 
    IEnumerable<PromoCode> GetProductPromoCodes(int productId); 
} 

con đường nào bạn khuyên tôi đi? Hoặc những ưu điểm/nhược điểm của nhau là gì?

Từ hiểu biết của tôi khi đọc http://martinfowler.com/eaaCatalog/repository.html cách tiếp cận đầu tiên dường như phản ánh tốt nhất điều này?

Trả lời

16

Thứ nhất là khủng khiếp. IQueryable giống như GOD object. Thật khó để tìm được 100% việc triển khai hoàn chỉnh nó (ngay cả trong tất cả OR/Ms). Bạn có thể hiển thị ORM trực tiếp thay vì sử dụng nó vì bạn có thể sẽ nhận được leaky abstraction layer nếu không.

Joel nói nó tốt nhất (văn bản là từ wikipedia article):

Trong bài Spolsky, ông kêu gọi sự chú ý đến nhiều ví dụ về các khái niệm trừu tượng mà làm việc hầu hết thời gian, nhưng với trường hợp chi tiết của sự phức tạp tiềm ẩn có thể không bị bỏ qua, và do đó thúc đẩy sự phức tạp vào các phần mềm mà lẽ ra phải được đơn giản hóa bằng cách trừu tượng bản thân

Joels blog entry

Cách tiếp cận thứ hai dễ thực hiện hơn và giữ nguyên sự trừu tượng.

Cập nhật

kho của bạn vi phạm Độc Nguyên tắc Trách nhiệm vì nó có hai lý do để thay đổi. Đầu tiên là nếu API sản phẩm bị thay đổi và phương pháp kia là nếu API PromoCode bị thay đổi. Bạn IMHO nên sử dụng hai kho khác nhau như:

interface IProductRepository 
{ 
    IEnumerable<Product> FindForCategory(int categoryId); 
    IEnumerable<Product> FindAllStartingWith(string letter); 
} 

interface IPromoCodeRepository 
{ 
    IEnumerable<PromoCode> FindForProduct(int productId); 
} 

thứ đã thay đổi:

  • tôi có xu hướng để bắt đầu phương pháp với Find khi một số mặt hàng được trả về và Get nếu một mục duy nhất được trả về.
  • Tên phương thức ngắn hơn = dễ đọc hơn.
  • Trách nhiệm duy nhất. Nó dễ dàng hơn để cho biết những gì các lớp sử dụng kho có cho các phụ thuộc.

Giao diện được xác định nhỏ giúp dễ dàng phát hiện vi phạm các nguyên tắc SOLID vì lớp học phá vỡ các nguyên tắc có xu hướng nhận các nhà xây dựng cồng kềnh.

+0

Cảm ơn bạn đã trả lời. Tôi nghĩ bạn đã có nghĩa là để nhóm bởi aggregates? Tôi có nên có một kho lưu trữ cho mỗi thực thể? Đây cũng là ví dụ về ví dụ đầu tiên mà tôi cung cấp.http: // stackoverflow.com/questions/5049363/sự khác biệt giữa kho-và-dịch vụ-lớp – Scott

+0

Mỗi tổng hợp. Tôi không thể nói rằng mã khuyến mại chỉ dành cho các sản phẩm. (kể từ khi nó được đặt tên là 'GetProductPromos' và không chỉ là' GetPromos') – jgauffin

+0

không có giao diện đầu tiên của bạn, cũng có 2 lý do để thay đổi? đầu tiên: thay đổi phương thức FindForCategory để thay đổi sản phẩm trả về mặc định nếu nó không tìm thấy bất kỳ sản phẩm nào bằng cách vượt qua categoryId, và thứ hai thay đổi FindAllStartingWith để áp dụng một số bộ lọc mặc định? – Masoud

0

Cá nhân tôi sẽ đề xuất sử dụng ví dụ thứ hai, theo cách bạn đang gói gọn logic tìm kiếm ở một nơi và mục đích của người gọi được xác định rõ ràng bằng tên của phương thức mà họ đang gọi. Nếu bạn đi với ví dụ đầu tiên, mã truy vấn của bạn sẽ bị rò rỉ trong suốt ứng dụng của bạn và bạn sẽ kết thúc các truy vấn trùng lặp.

0

Tôi khuyên bạn nên tránh trùng lặp. Đó là mục tiêu đầu tiên. Nếu bạn có logic tìm thấy sản phẩm bắt đầu bằng một số chữ cái ở một vài nơi, thì đó là trường hợp đặc biệt và giá trị của nó được trích xuất theo phương pháp riêng biệt (cũng có thể mô tả tốt cho trường hợp cụ thể của bạn). Mã không trùng lặp dễ dàng hơn nhiều để thay đổi, hiểu và duy trì.

Vì vậy, tôi có xu hướng có một phương pháp chung tìm kiếm với IQueryable và tập hợp các phương pháp mà sử dụng nhiều hơn một lần:

interface IRepository<T> 
{ 
    IQueryable<T> FindAll(); 
} 

interface IProductRepository : IRepository<Product> 
{ 
    IEnumerable<Product> GetProductsInCategory(int categoryId); 
    IEnumerable<Product> GetProductsStartingWith(string letter); 
    IEnumerable<PromoCode> GetProductPromoCodes(int productId); 
} 

cũng xem xét đơn vị thử nghiệm. Các phương thức cụ thể dễ dàng hơn nhiều so với IQueryable.

0

Tôi thực sự nghĩ rằng câu hỏi đầu tiên tốt hơn. Tôi cho rằng quyết định của tôi trên các yếu tố sau:

  1. Nếu cơ cấu sản phẩm sẽ được refactored:

    • cách tiếp cận IQueryable - bạn chỉ cần thay đổi phương pháp gọi trong mã.
    • IEnumerables - bạn cũng cần đổi tên phương thức.

  2. Nếu nhiều về để lấy được các giao diện mà bạn muốn lặp trong cách đa hình:

    • IQueryable cách tiếp cận - lợi ích của tên phương pháp chung thống nhất
    • IEnumerables - một số tên có thể không được mô tả phương pháp bạn cần.

  3. Elasticness

    • cách tiếp cận IQueryable - dễ dàng để chia thành cách tiếp cận IEnumerables.
    • Cách tiếp cận IE có thể truy cập được - khó chuyển đổi về phương pháp tiếp cận IQueryable.

Vì vậy, tôi đề nghị bạn bắt đầu với IQueryable như lựa chọn mặc định và khi bạn tiến bộ với mã của bạn, bạn luôn có thể thay đổi vào IEnumerables cụ thể hơn tiếp cận mà bạn cần.

1

Đồng thuận đang xây dựng: tùy chọn thứ 2 theo mọi cách. Ngoài logic truy vấn bị rò rỉ khắp nơi với IQueryable, khó khăn trong việc thực hiện nó đúng, rất khó để KIỂM TRA và thử.

Các vấn đề liên quan