Bao nhiêu logic trong Getters


46

Các đồng nghiệp của tôi nói với tôi rằng nên có ít logic nhất có thể trong getters và setters.

Tuy nhiên, tôi tin chắc rằng rất nhiều thứ có thể được ẩn trong getters và setters để bảo vệ người dùng / lập trình viên khỏi các chi tiết triển khai.

Một ví dụ về những gì tôi làm:

public List<Stuff> getStuff()
{
   if (stuff == null || cacheInvalid())
   {
       stuff = getStuffFromDatabase();
   }
   return stuff;
}

Một ví dụ về cách công việc bảo tôi làm mọi việc (họ trích dẫn 'Mã sạch' từ chú Bob):

public List<Stuff> getStuff()
{
    return stuff;
}

public void loadStuff()
{
    stuff = getStuffFromDatabase();
}

Bao nhiêu logic là thích hợp trong một setter / getter? Việc sử dụng getters và setters trống là gì ngoại trừ vi phạm ẩn dữ liệu?


6
Điều này trông giống như tryGetStuff () với tôi ...
Bill Michell

16
Đây không phải là một 'getter'. Thuật ngữ này được sử dụng cho người truy cập đọc tài sản, không phải là phương thức bạn vô tình đặt 'get' trong tên.
Boris Yankov

6
Tôi không biết nếu ví dụ thứ hai đó là một ví dụ công bằng của cuốn sách mã sạch mà bạn đề cập đến, hoặc ai đó đã hiểu sai về nó, nhưng có một điều mà mớ hỗn độn không phải là mã sạch.
Jon Hanna

@BorisYankov Chà ... phương pháp thứ hai là. public List<Stuff> getStuff() { return stuff; }
R. Schmitz

Tùy thuộc vào trường hợp sử dụng chính xác, tôi muốn tách riêng bộ nhớ đệm của mình thành một lớp riêng. Tạo một StuffGettergiao diện, thực hiện một StuffComputerphép tính và bọc nó bên trong một đối tượng StuffCacher, chịu trách nhiệm truy cập bộ đệm hoặc chuyển tiếp các cuộc gọi đến StuffComputernó.
Alexander

Câu trả lời:


71

Cách làm việc bảo bạn làm mọi việc là khập khiễng.

Theo nguyên tắc thông thường, cách tôi thực hiện như sau: nếu nhận được nội dung có giá rẻ về mặt tính toán, (hoặc nếu nhiều khả năng nó sẽ được tìm thấy trong bộ đệm,) thì phong cách getStuff () của bạn vẫn ổn. Nếu nhận được các công cụ được biết là đắt tiền về mặt tính toán, đắt đến mức quảng cáo mở rộng của nó là cần thiết tại giao diện, thì tôi sẽ không gọi nó là getStuff (), tôi sẽ gọi nó là notifyStuff () hoặc một cái gì đó tương tự, để chỉ ra rằng sẽ có một số việc phải làm.

Trong cả hai trường hợp, cách làm việc bảo bạn làm mọi thứ đều khập khiễng, bởi vì getStuff () sẽ nổ tung nếu loadStuff () không được gọi trước, vì vậy về cơ bản họ muốn bạn làm phức tạp giao diện của bạn bằng cách đưa ra sự phức tạp về thứ tự hoạt động với nó Trật tự hoạt động là khá nhiều về loại phức tạp tồi tệ nhất mà tôi có thể nghĩ đến.


23
+1 để đề cập đến độ phức tạp của thứ tự hoạt động. Như một giải pháp thay thế, có thể công việc sẽ yêu cầu tôi luôn gọi loadStuff () trong hàm tạo, nhưng điều đó cũng rất tệ bởi vì điều đó có nghĩa là nó sẽ luôn phải được tải. Trong ví dụ đầu tiên, dữ liệu chỉ được tải một cách lười biếng khi cần thiết, điều này tốt nhất có thể.
nguyệt quế

6
Tôi thường tuân theo quy tắc "nếu nó thực sự rẻ, hãy sử dụng một getter bất động sản. Nếu nó đắt, hãy sử dụng một chức năng". Điều đó thường phục vụ tôi tốt, và đặt tên thích hợp như bạn chỉ ra để nhấn mạnh nó dường như cũng tốt cho tôi.
Xe đẩy Denis

3
nếu nó có thể thất bại - nó không phải là một getter. Trong trường hợp này, nếu liên kết DB không hoạt động thì sao?
Martin Beckett

6
+1, tôi hơi sốc khi có bao nhiêu câu trả lời sai đã được đăng. Getters / Setters tồn tại để ẩn chi tiết triển khai, nếu không thì biến chỉ nên được công khai.
Izkata

2
Đừng quên rằng việc yêu cầu loadStuff()hàm được gọi trước getStuff()hàm cũng có nghĩa là lớp không được trừu tượng hóa một cách chính xác những gì đang diễn ra dưới mui xe.
rjzii

23

Logic trong getters là hoàn toàn tốt.

Nhưng nhận dữ liệu từ cơ sở dữ liệu không chỉ là "logic". Nó liên quan đến một loạt các hoạt động rất tốn kém, trong đó rất nhiều thứ có thể sai, và theo một cách không xác định. Tôi ngần ngại làm điều đó ngầm trong một getter.

Mặt khác, hầu hết các ORM đều hỗ trợ tải các bộ sưu tập một cách lười biếng, về cơ bản chính xác là những gì bạn đang làm.


18

Tôi nghĩ rằng theo 'Clean Code', nó nên được chia càng nhiều càng tốt, thành một cái gì đó như:

public List<Stuff> getStuff() {
   if (hasStuff()) {
       return stuff;
   }
   loadStuff();
   return stuff;
}

private boolean hasStuff() {
    if (stuff == null) {
       return false;
    }
    if (cacheInvalid()) {
       return false;        
    }
    return true;
} 

private void loadStuff() {
    stuff = getStuffFromDatabase();
}

Tất nhiên, điều này là hoàn toàn vô nghĩa, cho rằng hình thức đẹp, mà bạn đã viết, thực hiện đúng với một phần mã mà bất cứ ai hiểu trong nháy mắt:

public List<Stuff> getStuff() {
   if (stuff == null || cacheInvalid()) {
       stuff = getStuffFromDatabase();
   }
   return stuff;
}

Không nên làm đau đầu người gọi làm thế nào các công cụ được đặt dưới mui xe, và đặc biệt không nên đau đầu của người gọi để nhớ gọi mọi thứ theo một thứ tự "đúng thứ tự".


8
-1. Đau đầu thực sự sẽ là khi người gọi bị mắc kẹt tìm ra lý do tại sao một cuộc gọi getter đơn giản dẫn đến truy cập cơ sở dữ liệu chậm như địa ngục.
Domenic

14
@Domenic: Dù sao thì việc truy cập cơ sở dữ liệu phải được thực hiện, bạn không lưu bất kỳ ai thực hiện bằng cách không thực hiện. Nếu bạn cần điều này List<Stuff>, chỉ có một cách để có được nó.
DeadMG

4
@lukas: Cảm ơn, tôi đã không nhớ tất cả các thủ thuật được sử dụng trong 'Clean' Code để tạo ra các đoạn mã tầm thường nhưng dài hơn một dòng ;-) Đã sửa bây giờ.
Joonas Pulakka

2
Bạn đang nói xấu Robert Martin. Anh ta sẽ không bao giờ mở rộng một hàm boolean đơn giản thành một hàm chín dòng. Chức năng của bạn hasStufflà ngược lại với mã sạch.
kevin cline

2
Tôi đã đọc phần đầu của câu trả lời này và tôi sẽ bỏ qua nó, nghĩ rằng "có một người tôn thờ cuốn sách khác", và rồi phần "Tất nhiên, điều này hoàn toàn vô nghĩa" lọt vào mắt tôi. Nói hay lắm! C -: =
Mike Nakis

8

Họ nói với tôi rằng nên có ít logic nhất có thể trong getters và setters.

Cần phải có nhiều logic như cần thiết để đáp ứng nhu cầu của lớp. Sở thích cá nhân của tôi là càng ít càng tốt, nhưng khi duy trì mã, bạn thường phải rời khỏi giao diện ban đầu với các getters / setters hiện có, nhưng đặt nhiều logic vào chúng để sửa lỗi logic kinh doanh mới hơn (ví dụ: "khách hàng "getter trong môi trường sau 911 phải đáp ứng các quy định " biết khách hàng của bạn "và OFAC , kết hợp với chính sách của công ty cấm sự xuất hiện của khách hàng từ một số quốc gia nhất định xuất hiện [như Cuba hoặc Iran]).

Trong ví dụ của bạn, tôi thích mẫu của bạn và không thích mẫu "chú bob" vì phiên bản "chú bob" yêu cầu người dùng / người bảo trì nhớ gọi loadStuff()trước khi họ gọi getStuff()- đây là một công thức cho thảm họa nếu bất kỳ một người bảo trì nào của bạn quên (hoặc tệ hơn, không bao giờ biết). Hầu hết những nơi tôi đã làm việc trong thập kỷ qua vẫn đang sử dụng mã đã hơn một thập kỷ, vì vậy việc bảo trì dễ dàng là một yếu tố quan trọng cần xem xét.


6

Bạn đúng, đồng nghiệp của bạn sai.

Hãy quên quy tắc ngón tay cái của mọi người về những gì một phương thức get nên hay không nên làm. Một lớp học nên trình bày một sự trừu tượng của một cái gì đó. Lớp học của bạn đã có thể đọc được stuff. Trong Java, thông thường sử dụng các phương thức 'get' để đọc các thuộc tính. Hàng tỷ dòng khung đã được viết để mong đọc stuffbằng cách gọi getStuff. Nếu bạn đặt tên cho hàm của bạn fetchStuffhoặc bất cứ thứ gì khác getStuff, thì lớp của bạn sẽ không tương thích với tất cả các khung đó.

Bạn có thể trỏ chúng đến Hibernate, trong đó 'getStuff ()' có thể thực hiện một số điều rất phức tạp và ném RuntimeException khi thất bại.


Hibernate là một ORM, vì vậy gói chính nó thể hiện ý định. Mục đích này không dễ hiểu như vậy nếu bản thân gói không phải là ORM.
FMJaguar

@FMJaguar: nó hoàn toàn dễ hiểu. Hibernate trừu tượng hóa các hoạt động cơ sở dữ liệu để trình bày một mạng các đối tượng. OP đang trừu tượng hóa một hoạt động cơ sở dữ liệu để trình bày một đối tượng có thuộc tính được đặt tên stuff. Cả hai đều ẩn chi tiết để viết mã cuộc gọi dễ dàng hơn.
kevin cline

Nếu lớp đó là một lớp ORM, thì ý định đã được thể hiện, trong các bối cảnh khác: câu hỏi vẫn là: "Làm thế nào để một lập trình viên khác biết các tác dụng phụ của việc gọi getter?". Nếu chương trình chứa 1k lớp và 10k getters, chính sách cho phép gọi cơ sở dữ liệu trong bất kỳ trong số chúng có thể gặp rắc rối
FMJaguar

4

Âm thanh như thế này có thể là một chút của chủ nghĩa thuần túy so với tranh luận về ứng dụng có thể bị ảnh hưởng bởi cách bạn muốn kiểm soát tên hàm. Từ quan điểm được áp dụng, tôi muốn thấy:

List<String> names = clientRoster.getNames();
List<String> emails = clientRoster.getEmails();

Như trái ngược với:

myObject.load();
List<String> names = clientRoster.getNames();
List<String> emails = clientRoster.getEmails();

Hoặc thậm chí tệ hơn:

myObject.loadNames();
List<String> names = clientRoster.getNames();
myOjbect.loadEmails();
List<String> emails = clientRoster.getEmails();

Mà chỉ có xu hướng làm cho mã khác trở nên dư thừa và khó đọc hơn nhiều vì bạn phải bắt đầu lội qua tất cả các cuộc gọi tương tự. Ngoài ra, việc gọi các hàm của trình tải hoặc tương tự phá vỡ toàn bộ mục đích sử dụng OOP mà bạn không còn bị trừu tượng hóa khỏi các chi tiết triển khai của đối tượng bạn đang làm việc. Nếu bạn có một clientRosterđối tượng, bạn không cần phải quan tâm đến cách thức getNameshoạt động, như bạn sẽ phải gọi a loadNames, bạn chỉ nên biết rằng getNamescung cấp cho bạn một List<String>tên của khách hàng.

Do đó, có vẻ như vấn đề liên quan nhiều đến ngữ nghĩa và tên tốt nhất cho chức năng để lấy dữ liệu. Nếu công ty (và những người khác) có vấn đề với tiền tố getsettiền tố, vậy thì làm thế nào để gọi hàm này giống như retrieveNamesthế nào? Nó nói những gì đang diễn ra nhưng không ngụ ý rằng hoạt động sẽ diễn ra tức thời như mong đợi của một getphương thức.

Về mặt logic trong một phương thức truy cập, hãy giữ nó ở mức tối thiểu vì chúng thường được ngụ ý là tức thời chỉ với tương tác danh nghĩa xảy ra với biến. Tuy nhiên, điều đó cũng thường chỉ áp dụng cho các loại đơn giản, các loại dữ liệu phức tạp (nghĩa là List) tôi thấy khó khăn hơn để đóng gói đúng cách trong một thuộc tính và thường sử dụng các phương thức khác để tương tác với chúng thay vì một trình truy cập và trình truy cập nghiêm ngặt.


2

Gọi một getter sẽ thể hiện hành vi tương tự như đọc một trường:

  • Nó nên rẻ để lấy giá trị
  • Nếu bạn đặt một giá trị với setter và sau đó đọc nó với getter, giá trị sẽ giống nhau
  • Lấy giá trị không có tác dụng phụ
  • Nó không nên ném một ngoại lệ

2
Tôi không hoàn toàn đồng ý về điều này. Tôi đồng ý rằng nó sẽ không có tác dụng phụ, nhưng tôi nghĩ việc thực hiện nó theo cách khác biệt với một lĩnh vực là hoàn toàn tốt. Nhìn vào .Net BCL, UnlimitedOperationException được sử dụng rộng rãi khi nhìn vào getters. Ngoài ra, hãy xem câu trả lời của MikeNakis về thứ tự hoạt động.
Tối đa

Đồng ý với tất cả các điểm trừ điểm cuối cùng. Chắc chắn có thể nhận được một giá trị có thể liên quan đến việc thực hiện một phép tính hoặc một số thao tác khác phụ thuộc vào các giá trị hoặc tài nguyên khác có thể chưa được đặt. Trong những trường hợp đó, tôi sẽ mong người getter ném một loại ngoại lệ nào đó.
TMN

1
@TMN: Trong trường hợp tốt nhất, lớp nên được tổ chức theo cách sao cho các getters không cần phải chạy các hoạt động có khả năng phát sinh ngoại lệ. Giảm thiểu những nơi có thể ném ngoại lệ dẫn đến những bất ngờ ít bất ngờ hơn.
hugomg

8
Tôi sẽ không đồng ý với điểm thứ hai với một ví dụ cụ thể : foo.setAngle(361); bar = foo.getAngle(). barcó thể 361, nhưng cũng có thể hợp pháp 1nếu các góc bị ràng buộc trong một phạm vi.
zzzzBov

1
-1. (1) giá rẻ trong ví dụ này - sau khi tải lười biếng. (2) hiện tại không có "setter" trong ví dụ, nhưng nếu ai đó thêm một cái sau và nó chỉ đặt stuff, getter sẽ trả về cùng một giá trị. (3) Tải chậm như trong ví dụ không tạo ra tác dụng phụ "có thể nhìn thấy". (4) gây tranh cãi, có thể là một điểm hợp lệ, vì việc đưa ra "tải lười biếng" sau đó có thể thay đổi hợp đồng API cũ - nhưng người ta phải xem xét hợp đồng đó để đưa ra quyết định.
Doc Brown

2

Một getter gọi các thuộc tính và phương thức khác để tính giá trị riêng của nó cũng bao hàm một sự phụ thuộc. Ví dụ: nếu thuộc tính của bạn phải có khả năng tự tính toán và làm như vậy đòi hỏi phải đặt thành viên khác, thì bạn phải lo lắng về các tham chiếu null vô tình nếu thuộc tính của bạn được truy cập trong mã khởi tạo mà tất cả các thành viên không nhất thiết phải đặt.

Điều đó không có nghĩa là 'không bao giờ truy cập vào một thành viên khác không phải là trường sao lưu thuộc tính trong getter', điều đó chỉ có nghĩa là chú ý đến những gì bạn đang ám chỉ về trạng thái cần thiết của đối tượng là gì và nếu phù hợp với bối cảnh bạn mong đợi tài sản này được truy cập trong.

Tuy nhiên, trong hai ví dụ cụ thể mà bạn đưa ra, lý do tôi sẽ chọn cái này hoàn toàn khác. Getter của bạn được khởi tạo trong lần truy cập đầu tiên, ví dụ: Khởi tạo lười biếng . Ví dụ thứ hai được giả định là được khởi tạo tại một số điểm trước đó, ví dụ: Khởi tạo rõ ràng .

Khi chính xác khởi tạo xảy ra có thể hoặc không quan trọng.

Ví dụ, nó có thể rất rất chậm và cần được thực hiện trong bước tải mà người dùng đang mong đợi sự chậm trễ, thay vì hiệu suất bất ngờ bị trục trặc khi người dùng kích hoạt quyền truy cập lần đầu (ví dụ: nhấp chuột phải của người dùng, menu ngữ cảnh xuất hiện, người dùng xuất hiện đã nhấp chuột phải một lần nữa).

Ngoài ra, đôi khi có một điểm rõ ràng trong thực thi khi mọi thứ có thể ảnh hưởng / làm bẩn giá trị thuộc tính được lưu trong bộ nhớ cache xảy ra. Bạn thậm chí có thể đang xác minh rằng không có sự phụ thuộc nào thay đổi và ném ngoại lệ sau này. Trong tình huống này, điều hợp lý là cũng lưu trữ giá trị tại thời điểm đó, ngay cả khi nó không đặc biệt tốn kém để tính toán, chỉ để tránh làm cho việc thực thi mã trở nên phức tạp hơn và khó theo dõi hơn về mặt tinh thần.

Điều đó nói rằng, Khởi tạo lười biếng có rất nhiều ý nghĩa trong rất nhiều tình huống khác. Vì vậy, như thường xảy ra trong lập trình, thật khó để hiểu được quy tắc, nó đi xuống mã cụ thể.


0

Cứ làm như @MikeNakis đã nói ... Nếu bạn nhận được thứ đó thì không sao ... Nếu bạn làm gì đó hãy tạo một chức năng mới thực hiện công việc và công khai nó.

Nếu tài sản / chức năng của bạn chỉ làm những gì mà tên của nó nói thì sẽ không còn nhiều sự phức tạp. Sự gắn kết là IMO quan trọng


1
Hãy cẩn thận về điều này, bạn có thể kết thúc việc phơi bày quá nhiều trạng thái nội bộ của bạn. Bạn không muốn kết thúc với nhiều phương thức trống rỗng loadFoo()hoặc preloadDummyReferences()hoặc createDefaultValuesForUninitializedFields()chỉ vì việc triển khai ban đầu của lớp bạn cần chúng.
TMN

Chắc chắn ... tôi chỉ nói rằng nếu bạn làm những gì mà cái tên nói rằng không nên có nhiều vấn đề ... nhưng những gì bạn nói là hoàn toàn đúng ...
Ivan Crojach Karačić

0

Cá nhân, tôi sẽ đưa ra yêu cầu của Stuff thông qua một tham số trong hàm tạo và cho phép bất kỳ lớp nào đang khởi tạo công cụ để thực hiện công việc tìm ra nó nên đến từ đâu. Nếu công cụ là null, nó sẽ trả về null. Tôi không muốn thử các giải pháp thông minh như bản gốc của OP vì đó là cách dễ dàng để ẩn các lỗi sâu bên trong triển khai của bạn, nơi không rõ ràng điều gì có thể xảy ra khi có sự cố.


0

Có nhiều vấn đề quan trọng hơn sau đó chỉ là "sự phù hợp" ở đây và bạn nên dựa trên quyết định của mình . Chủ yếu, quyết định lớn ở đây là bạn có muốn cho phép mọi người bỏ qua bộ đệm hay không.

  1. Trước tiên, hãy suy nghĩ xem có cách nào để sắp xếp lại mã của bạn để tất cả các lệnh gọi tải và quản lý bộ đệm cần thiết được thực hiện trong hàm tạo / bộ khởi tạo. Nếu điều này là có thể, bạn có thể tạo một lớp có bất biến cho phép bạn thực hiện với trình getter đơn giản từ phần 2 với sự an toàn của trình getter phức tạp từ phần 1. (Kịch bản win-win)

  2. Nếu bạn không thể tạo một lớp như vậy, hãy quyết định xem bạn có đánh đổi hay không và cần quyết định thời điểm bạn muốn cho phép người tiêu dùng bỏ qua mã kiểm tra bộ đệm hay không.

    1. Nếu điều quan trọng là người tiêu dùng không bao giờ bỏ qua kiểm tra bộ đệm và bạn không bận tâm đến các hình phạt về hiệu suất, thì hãy kết hợp kiểm tra bên trong getter và khiến người tiêu dùng không thể làm sai.

    2. Nếu bạn bỏ qua kiểm tra bộ đệm hoặc điều rất quan trọng là bạn được đảm bảo hiệu suất O (1) trong getter thì hãy sử dụng các cuộc gọi riêng biệt.

Như bạn có thể đã lưu ý, tôi không phải là một fan hâm mộ lớn của triết lý "mã sạch", "chia mọi thứ thành các hàm nhỏ". Nếu bạn có một loạt các hàm trực giao có thể được gọi theo bất kỳ thứ tự nào thì chúng sẽ cung cấp cho bạn sức mạnh biểu cảm nhiều hơn với chi phí thấp. Tuy nhiên, nếu các chức năng của bạn có phụ thuộc đơn hàng (hoặc chỉ thực sự hữu ích theo một thứ tự cụ thể) thì việc tách chúng chỉ làm tăng số cách bạn có thể làm sai, trong khi thêm ít lợi ích.


-1, các nhà xây dựng nên xây dựng, không khởi tạo. Việc đưa logic cơ sở dữ liệu vào một hàm tạo làm cho lớp đó hoàn toàn không thể kiểm tra được và nếu bạn có nhiều hơn một vài trong số này thì thời gian khởi động ứng dụng của bạn sẽ trở nên vô cùng lớn. Và đó chỉ là cho người mới bắt đầu.
Domenic

@Domenic: Đây là một vấn đề phụ thuộc vào ngữ nghĩa và ngôn ngữ. Điểm mà một đối tượng phù hợp để sử dụng và cung cấp các bất biến thích hợp sau và chỉ sau khi nó được xây dựng hoàn chỉnh.
hugomg

0

Theo tôi, Getters không nên có nhiều logic trong đó. Chúng không nên có tác dụng phụ và bạn không bao giờ nên có ngoại lệ từ chúng. Tất nhiên trừ khi bạn biết bạn đang làm gì. Hầu hết các getters của tôi không có logic trong đó và chỉ đi đến một lĩnh vực. Nhưng ngoại lệ đáng chú ý là với API công khai cần đơn giản nhất có thể để sử dụng. Vì vậy, tôi đã có một getter sẽ thất bại nếu một getter khác đã không được gọi. Giải pháp? Một dòng mã như var throwaway=MyGetter;trong getter phụ thuộc vào nó. Tôi không tự hào về điều đó, nhưng tôi vẫn không thấy cách nào sạch hơn để làm điều đó


0

Điều này trông giống như đọc từ bộ nhớ cache với tải lười biếng. Như những người khác đã lưu ý, kiểm tra và tải có thể thuộc về các phương pháp khác. Tải có thể cần phải được đồng bộ hóa để bạn không nhận được hai mươi luồng tất cả tải cùng một lúc.

Có thể thích hợp để sử dụng tên getCachedStuff()cho getter vì nó sẽ không có thời gian thực hiện nhất quán.

Tùy thuộc vào cách thức cacheInvalid()hoạt động của thói quen, việc kiểm tra null có thể không cần thiết. Tôi sẽ không mong đợi bộ đệm là hợp lệ trừ khi stuffđã được điền từ cơ sở dữ liệu.


0

Logic chính mà tôi mong đợi sẽ thấy trong các getters trả về danh sách là logic để đảm bảo danh sách không thể thay đổi. Vì nó đứng cả hai ví dụ của bạn có khả năng phá vỡ đóng gói.

cái gì đó như:

public List<Stuff> getStuff()
{
    return Collections.unmodifiableList(stuff);
}

đối với bộ nhớ đệm trong getter, tôi nghĩ rằng nó sẽ ổn nhưng tôi có thể bị lôi ra khỏi bộ đệm logic nếu việc xây dựng bộ đệm mất một thời gian đáng kể. tức là nó phụ thuộc


0

Tùy thuộc vào trường hợp sử dụng chính xác, tôi muốn tách riêng bộ nhớ đệm của mình thành một lớp riêng. Tạo một StuffGettergiao diện, thực hiện một StuffComputerphép tính và bọc nó bên trong một đối tượng StuffCacher, chịu trách nhiệm truy cập bộ đệm hoặc chuyển tiếp các cuộc gọi đến StuffComputernó.

interface StuffGetter {
     public List<Stuff> getStuff();
}

class StuffComputer implements StuffGetter {
     public List<Stuff> getStuff() {
         getStuffFromDatabase()
     }
}

class StuffCacher implements StuffGetter {
     private stuffComputer; // DI this
     private Cache<List<Stuff>> cache = new Cache<>();

     public List<Stuff> getStuff() {
         if cache.hasStuff() {
             return cache.getStuff();
         }

         List<Stuffs> stuffs = stuffComputer.getStuff();
         cache.store(stuffs);
         return stuffs;
     }
}

Thiết kế này cho phép bạn dễ dàng thêm bộ nhớ đệm, xóa bộ nhớ đệm, thay đổi logic phái sinh cơ bản (ví dụ: truy cập DB so với trả lại dữ liệu giả), v.v.


-1

IMHO nó rất đơn giản nếu bạn sử dụng một thiết kế theo hợp đồng. Quyết định những gì getter của bạn nên cung cấp và chỉ mã phù hợp (mã đơn giản hoặc một số logic phức tạp có thể liên quan hoặc được ủy quyền ở đâu đó).


+1: Tôi đồng ý với bạn! Nếu đối tượng chỉ có nghĩa là để giữ một số dữ liệu, thì các getters chỉ nên trả về nội dung hiện tại của đối tượng. Trong trường hợp này, trách nhiệm của một số đối tượng khác là tải dữ liệu. Nếu hợp đồng nói rằng đối tượng là proxy của một bản ghi cơ sở dữ liệu, thì getter sẽ lấy dữ liệu một cách nhanh chóng. Nó có thể còn phức tạp hơn nữa nếu dữ liệu đã được tải nhưng không cập nhật: đối tượng có nên được thông báo về những thay đổi trong cơ sở dữ liệu không? Tôi nghĩ rằng không có câu trả lời duy nhất cho câu hỏi này.
Giorgio
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.