Có nhất quán nên được ưa chuộng hơn quy ước lập trình?


14

Khi thiết kế một lớp nên nhất quán trong hành vi được ưa chuộng hơn so với thực hành lập trình phổ biến? Để đưa ra một ví dụ cụ thể:

Một quy ước chung là: Nếu một lớp sở hữu một đối tượng (ví dụ như nó đã tạo ra nó), nó có trách nhiệm làm sạch nó sau khi hoàn thành. Một ví dụ cụ thể sẽ có trong .NET rằng nếu lớp của bạn sở hữu một IDisposableđối tượng thì nó sẽ loại bỏ nó vào cuối vòng đời của nó. Và nếu bạn không sở hữu nó thì đừng chạm vào nó.

Bây giờ nếu chúng ta nhìn vào StreamWriterlớp trong .NET thì chúng ta có thể tìm thấy trong tài liệu rằng nó đóng luồng bên dưới khi nó được đóng / xử lý. Điều này là cần thiết trong trường hợp StreamWriterđược khởi tạo bằng cách chuyển vào một tên tệp khi người viết tạo ra luồng tệp bên dưới và do đó cần phải đóng nó. Tuy nhiên, người ta cũng có thể vượt qua trong một luồng bên ngoài mà nhà văn cũng đóng.

Điều này đã gây khó chịu cho tôi rất nhiều lần (vâng tôi biết bạn có thể tạo một trình bao bọc không đóng nhưng đó không phải là vấn đề) nhưng rõ ràng Microsoft đã đưa ra quyết định rằng sẽ luôn nhất quán khi đóng luồng bất kể nó đến từ đâu.

Khi tôi gặp một mẫu như vậy trong một trong các lớp của mình, tôi thường tạo một ownsFooBarcờ được đặt thành false trong trường hợp FooBarđược đưa vào qua hàm tạo và ngược lại. Bằng cách này, trách nhiệm làm sạch nó được chuyển cho người gọi khi anh ta vượt qua ví dụ một cách rõ ràng.

Bây giờ tôi đang tự hỏi liệu có thể nhất quán nên được ưu tiên hơn thực tiễn tốt nhất (hoặc có thể thực hành tốt nhất của tôi không tốt)? Bất kỳ đối số cho / chống lại nó?

Chỉnh sửa để làm rõ

Với "tính nhất quán" Ý tôi là: Hành vi nhất quán của lớp luôn nắm quyền sở hữu (và đóng luồng) so với "thực tiễn tốt nhất" để chỉ sở hữu một đối tượng nếu bạn tạo ra nó hoặc chuyển giao quyền sở hữu một cách rõ ràng.

Đối với một ví dụ nơi nó đang mê hoặc:

Giả sử bạn có hai lớp nhất định (từ thư viện của bên thứ 3) chấp nhận luồng để thực hiện điều gì đó với nó, như tạo và xử lý một số dữ liệu:

 public class DataProcessor
 {
     public Result ProcessData(Stream input)
     {
          using (var reader = new StreamReader(input))
          {
              ...
          }
     }
 }

 public class DataSource
 {
     public void GetData(Stream output)
     {
          using (var writer = new StreamWriter(output))
          {
               ....
          }
     }
 }

Bây giờ tôi muốn sử dụng nó như thế này:

 Result ProcessSomething(DataSource source)
 {
      var processor = new DataProcessor();
      ...
      var ms = new MemoryStream();
      source.GetData(ms);
      return processor.ProcessData(ms);
 }

Điều này sẽ thất bại với một ngoại lệ Cannot access a closed streamtrong bộ xử lý dữ liệu. Đó là một chút được xây dựng nhưng nên minh họa điểm. Có nhiều cách khác nhau để khắc phục nhưng tuy nhiên tôi cảm thấy rằng tôi làm việc xung quanh một cái gì đó tôi không cần phải làm.


Bạn có phiền khi nói về lý do TẠI SAO hành vi minh họa của StreamWriter khiến bạn đau không? Bạn có muốn tiêu thụ cùng một luồng ở nơi khác không? Tại sao?
Công việc

@Job: Tôi đã làm rõ câu hỏi của mình
ChrisWue

Câu trả lời:


9

Tôi cũng sử dụng kỹ thuật này, tôi gọi nó là 'bàn giao' và tôi tin rằng nó xứng đáng với trạng thái của một mẫu. Khi một đối tượng A chấp nhận một đối tượng B dùng một lần làm tham số thời gian xây dựng, nó cũng chấp nhận một boolean gọi là 'handoff', (mặc định là false,) và nếu đó là sự thật, thì xử lý các tầng A để xử lý B.

Tôi không ủng hộ việc phổ biến những lựa chọn không may của người khác, vì vậy tôi sẽ không bao giờ chấp nhận hành vi xấu của Microsoft như một quy ước đã được thiết lập, tôi cũng không coi những người khác mù quáng theo những lựa chọn không may của Microsoft là "hành vi nhất quán" dưới bất kỳ hình thức, hình thức hay hình thức nào .


Lưu ý: Tôi đã viết một định nghĩa chính thức về mẫu này trong một bài đăng trên blog của mình: michael.gr: Mẫu "Handoff" .
Mike Nakis

Là một phần mở rộng hơn nữa của handOffmẫu, nếu một thuộc tính như vậy được đặt trong hàm tạo, nhưng một phương thức khác tồn tại để sửa đổi nó, thì phương thức khác đó cũng sẽ chấp nhận một handOffcờ. Nếu handOffđược chỉ định cho mục được giữ hiện tại, mục đó phải được xử lý, thì mục mới sẽ được chấp nhận và handOffcờ nội bộ được cập nhật để phản ánh trạng thái của mục mới. Phương pháp không cần kiểm tra các tham chiếu cũ và mới để tìm sự bình đẳng, vì nếu tham chiếu ban đầu bị "bỏ qua" thì tất cả các tham chiếu được giữ bởi người gọi ban đầu sẽ bị bỏ qua.
supercat

@supercat Tôi đồng tình, nhưng tôi có một vấn đề với câu cuối cùng: nếu bàn giao là đúng với mặt hàng đang nắm giữ, nhưng mặt hàng mới được cung cấp bằng với tham chiếu đến mặt hàng hiện đang giữ, sau đó loại bỏ mặt hàng hiện đang giữ có hiệu lực xử lý các mặt hàng mới được cung cấp. Tôi nghĩ rằng việc cung cấp lại cùng một mặt hàng nên là bất hợp pháp.
Mike Nakis

Việc cung cấp lại cùng một mặt hàng thường là bất hợp pháp trừ khi đối tượng là bất biến, không thực sự nắm giữ bất kỳ tài nguyên nào và không quan tâm nếu nó bị xử lý. Ví dụ: nếu Disposecác yêu cầu bị bỏ qua cho các cọ hệ thống, thì phương thức "color.CreateBrush` có thể tạo ra một bàn chải mới (sẽ yêu cầu xử lý) hoặc trả về một cọ vẽ hệ thống (sẽ bỏ qua xử lý). tái sử dụng một vật thể nếu nó quan tâm đến việc xử lý, nhưng cho phép tái sử dụng nếu nó không, là để loại bỏ nó.
supercat

3

Tôi nghĩ bạn đúng, phải trung thực. Tôi nghĩ rằng Microsoft đã nhầm lẫn với lớp StreamWriter, cụ thể, vì những lý do bạn mô tả.

Tuy nhiên, tôi đã từng thấy rất nhiều mã mà mọi người thậm chí không cố gắng xử lý Luồng của riêng họ vì khung sẽ làm điều đó cho họ, vì vậy sửa lỗi bây giờ sẽ gây hại nhiều hơn là tốt.


2

Tôi muốn đi với sự nhất quán. Như bạn đã đề cập với hầu hết mọi người, sẽ có ý nghĩa rằng nếu đối tượng của bạn tạo ra một đối tượng con, nó sẽ sở hữu nó và phá hủy nó. Nếu nó được cung cấp một tham chiếu từ bên ngoài, tại một số thời điểm "bên ngoài" cũng đang giữ cùng một đối tượng và nó không nên được sở hữu. Nếu bạn muốn chuyển quyền sở hữu từ bên ngoài vào đối tượng của mình, hãy sử dụng các phương thức Đính kèm / Tách hoặc một nhà xây dựng chấp nhận một boolean cho thấy rõ rằng quyền sở hữu được chuyển giao.

Đã gõ tất cả những câu trả lời đầy đủ, tôi nghĩ rằng hầu hết các mẫu thiết kế chung chung sẽ không nhất thiết phải thuộc cùng loại với các lớp StreamWriter / StreamReader. Tôi đã luôn nghĩ rằng lý do MS chọn để StreamWriter / Reader tự động chiếm quyền sở hữu là vì trong trường hợp cụ thể này, việc sở hữu chung không có nhiều ý nghĩa. Bạn không thể có hai đối tượng khác nhau đồng thời đọc từ / ghi vào cùng một luồng. Tôi chắc chắn rằng bạn có thể viết một số loại chia sẻ thời gian xác định, nhưng đó sẽ là một trong những mô hình chống đối. Vì vậy, đó có thể là lý do tại sao họ nói, vì việc chia sẻ một luồng không có ý nghĩa gì, chúng ta hãy luôn luôn kiểm soát nó. Nhưng tôi sẽ không coi hành vi này đã trở thành một quy ước chung cho tất cả các lớp.

Bạn có thể coi Luồng là một mẫu nhất quán trong đó đối tượng được truyền vào không thể chia sẻ theo quan điểm thiết kế và do đó không có bất kỳ cờ bổ sung nào, người đọc / người viết chỉ chiếm quyền sở hữu của nó.


Tôi đã làm rõ câu hỏi của mình - với sự nhất quán tôi có nghĩa là hành vi luôn luôn sở hữu.
ChrisWue

2

Hãy cẩn thận. Những gì bạn nghĩ là "tính nhất quán" có thể chỉ là một tập hợp các thói quen ngẫu nhiên.

"Phối hợp các giao diện người dùng để thống nhất", SIGCHI Bulletin 20, (1989), 63-65. Xin lỗi, không có liên kết, đó là một bài viết cũ. "... một hội thảo kéo dài hai ngày gồm 15 chuyên gia đã không thể đưa ra định nghĩa về tính nhất quán." Vâng, đó là về "giao diện", nhưng tôi tin rằng nếu bạn nghĩ về "tính nhất quán" trong một thời gian, bạn sẽ thấy bạn cũng không thể định nghĩa nó.


bạn đã đúng, nếu các chuyên gia đến với nhau từ các vòng tròn khác nhau, nhưng khi phát triển mã, tôi thấy thật dễ dàng để làm theo một số mô hình hiện có (hoặc thói quen nếu bạn muốn) mà nhóm của tôi đã quen thuộc. Ví dụ, một ngày nọ, một trong những người của chúng tôi đã hỏi về một số hoạt động không đồng bộ mà chúng tôi có và khi tôi nói với anh ấy, họ hành xử giống như Win32 chồng chéo I / O, trong 30 giây anh ấy đã hiểu toàn bộ giao diện ... trong trường hợp này cả hai Tôi và anh ấy đến từ cùng một máy chủ nền Win32. Kiên định ftw! :)
DXM

@Bruce Tôi hiểu ý của bạn - Tôi nghĩ rõ ràng ý tôi là gì với sự nhất quán nhưng dường như không phải vậy.
ChrisWue
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.