Đại biểu vs Giao diện - Còn thông tin nào rõ ràng hơn không?


23

Sau khi đọc bài viết- Khi nào nên sử dụng Đại biểu thay vì Giao diện (Hướng dẫn lập trình C #) , tôi cần một số trợ giúp để hiểu các điểm đã cho bên dưới, điều mà tôi thấy không rõ ràng (đối với tôi). Bất kỳ ví dụ hoặc giải thích chi tiết có sẵn cho những?

Sử dụng một đại biểu khi:

  • Một mẫu thiết kế sự kiện được sử dụng.
  • Đó là mong muốn để đóng gói một phương thức tĩnh.
  • Thành phần dễ dàng là mong muốn.
  • Một lớp có thể cần nhiều hơn một cách thực hiện phương thức.

Sử dụng giao diện khi:

  • Có một nhóm các phương pháp liên quan có thể được gọi.
  • Một lớp chỉ cần một thực hiện của phương thức.

Câu hỏi của tôi là,

  1. Họ có ý nghĩa gì bởi một mẫu thiết kế sự kiện?
  2. Làm thế nào các thành phần hóa ra là dễ dàng nếu một đại biểu được sử dụng?
  3. nếu có một nhóm các phương thức liên quan có thể được gọi, thì hãy sử dụng giao diện - Nó có lợi ích gì?
  4. nếu một lớp chỉ cần một triển khai phương thức, hãy sử dụng giao diện - làm thế nào hợp lý về mặt lợi ích?

Câu trả lời:


11

Họ có ý nghĩa gì bởi một mẫu thiết kế sự kiện?

Họ rất có thể đề cập đến việc triển khai mẫu người quan sát là cấu trúc ngôn ngữ cốt lõi trong C #, được hiển thị dưới dạng ' sự kiện '. Lắng nghe các sự kiện là có thể bằng cách nối một đại biểu với họ. Như Yam Marcovic đã chỉ ra, EventHandlerlà loại đại biểu cơ sở thông thường cho các sự kiện, nhưng bất kỳ loại đại biểu nào cũng có thể được sử dụng.

Làm thế nào các thành phần hóa ra là dễ dàng nếu một đại biểu được sử dụng?

Điều này có lẽ chỉ đề cập đến các đại biểu linh hoạt cung cấp. Bạn có thể dễ dàng 'soạn' hành vi nhất định. Với sự giúp đỡ của lambdas , cú pháp để làm điều này cũng rất súc tích. Hãy xem xét ví dụ sau.

class Bunny
{
    Func<bool> _canHop;

    public Bunny( Func<bool> canHop )
    {
        _canHop = canHop;
    }

    public void Hop()
    {
        if ( _canHop() )  Console.WriteLine( "Hop!" );
    }
}

Bunny captiveBunny = new Bunny( () => IsBunnyReleased );
Bunny lazyBunny = new Bunny( () => !IsLazyDay );
Bunny captiveLazyBunny = new Bunny( () => IsBunnyReleased && !IsLazyDay );

Làm một cái gì đó tương tự với các giao diện sẽ yêu cầu bạn sử dụng mẫu chiến lược hoặc sử dụng Bunnylớp cơ sở (trừu tượng) từ đó bạn mở rộng các chú thỏ cụ thể hơn.

nếu có một nhóm các phương thức liên quan có thể được gọi, thì hãy sử dụng giao diện - Nó có lợi ích gì?

Một lần nữa, tôi sẽ sử dụng thỏ để chứng minh làm thế nào nó sẽ dễ dàng hơn.

interface IAnimal
{
    void Jump();
    void Eat();
    void Poo();
}

class Bunny : IAnimal { ... }
class Chick : IAnimal { ... }

// Using the interface.
IAnimal bunny = new Bunny();
bunny.Jump();  bunny.Eat();  bunny.Poo();
IAnimal chick = new Chick();
chick.Jump();  chick.Eat();  chick.Poo();

// Without the interface.
Action bunnyJump = () => bunny.Jump();
Action bunnyEat = () => bunny.Eat();
Action bunnyPoo = () => bunny.Poo();
bunnyJump(); bunnyEat(); bunnyPoo();
Action chickJump = () => chick.Jump();
Action chickEat = () => chick.Eat();
...

nếu một lớp chỉ cần một triển khai phương thức, hãy sử dụng giao diện - làm thế nào hợp lý về mặt lợi ích?

Đối với điều này, hãy xem xét ví dụ đầu tiên với chú thỏ một lần nữa. Nếu chỉ có một triển khai là cần thiết - không yêu cầu thành phần tùy chỉnh nào - bạn có thể hiển thị hành vi này dưới dạng giao diện. Bạn sẽ không bao giờ phải xây dựng lambdas, bạn chỉ có thể sử dụng giao diện.

Phần kết luận

Các đại biểu cung cấp sự linh hoạt hơn rất nhiều, trong khi các giao diện giúp bạn thiết lập các hợp đồng mạnh mẽ. Do đó tôi thấy điểm cuối cùng được đề cập, "Một lớp có thể cần nhiều hơn một cách thực hiện phương thức." , cho đến nay là một trong những liên quan nhất.

Một lý do bổ sung khi sử dụng đại biểu là khi bạn muốn chỉ hiển thị một phần của lớp mà bạn không thể điều chỉnh tệp nguồn từ đó.

Như một ví dụ về kịch bản như vậy (tính linh hoạt tối đa, không cần sửa đổi nguồn), hãy xem xét việc triển khai thuật toán tìm kiếm nhị phân này cho bất kỳ bộ sưu tập nào có thể bằng cách chỉ cần vượt qua hai đại biểu.


12

Một đại biểu là một cái gì đó giống như một giao diện cho một chữ ký phương thức duy nhất, mà không phải được thực hiện rõ ràng như một giao diện thông thường. Bạn có thể xây dựng nó trên đường chạy.

Một giao diện chỉ là một cấu trúc ngôn ngữ đại diện cho một số hợp đồng - "Tôi đảm bảo tôi sẽ cung cấp các phương thức và thuộc tính sau".

Ngoài ra, tôi không hoàn toàn đồng ý rằng các đại biểu chủ yếu hữu ích khi được sử dụng như một giải pháp cho mẫu người quan sát / người đăng ký. Tuy nhiên, đây là một giải pháp tinh tế cho "vấn đề chi tiết java".

Đối với câu hỏi của bạn:

1 & 2)

Nếu bạn muốn tạo một hệ thống sự kiện trong Java, bạn thường sử dụng một giao diện để truyền bá sự kiện, đại loại như:

interface KeyboardListener
{
    void KeyDown(int key);
    void KeyUp(int key)
    void KeyPress(int key);
    .... and so on
}

Điều này có nghĩa là lớp của bạn sẽ phải thực hiện rõ ràng tất cả các phương thức này và cung cấp các sơ khai cho tất cả chúng ngay cả khi bạn chỉ muốn thực hiện KeyPress(int key).

Trong C #, các sự kiện này sẽ được biểu diễn dưới dạng danh sách đại biểu, được ẩn bởi từ khóa "sự kiện" trong c #, một sự kiện cho mỗi sự kiện đơn lẻ. Điều này có nghĩa là bạn có thể dễ dàng đăng ký những gì bạn muốn mà không làm gánh nặng lớp học của bạn với các phương thức "Khóa" công khai, v.v.

+1 cho điểm 3-5.

Ngoài ra:

Các đại biểu rất hữu ích khi bạn muốn cung cấp ví dụ chức năng "bản đồ", đưa một danh sách và chiếu từng phần tử vào một danh sách mới, với cùng một lượng phần tử, nhưng theo một cách nào đó. Về cơ bản, IEnumerable.Select (...).

IEnumerable.Select lấy một Func<TSource, TDest>, đó là một đại biểu bao bọc một hàm lấy phần tử TSource và biến phần tử đó thành phần tử TDest.

Trong Java, điều này sẽ phải được thực hiện bằng một giao diện. Thường không có nơi tự nhiên để thực hiện một giao diện như vậy. Nếu một lớp chứa một danh sách mà nó muốn chuyển đổi theo một cách nào đó, thì không phải tự nhiên mà nó thực hiện giao diện "ListTransformer", đặc biệt là vì có thể có hai danh sách khác nhau nên được chuyển đổi theo các cách khác nhau.

Tất nhiên, bạn có thể sử dụng các lớp ẩn danh là một khái niệm tương tự (trong java).


Cân nhắc chỉnh sửa bài đăng của bạn để thực sự trả lời câu hỏi của anh ấy
Yam Marcovic

@YamMarcovic Bạn nói đúng, tôi lan man một chút ở dạng tự do thay vì trả lời trực tiếp câu hỏi của anh ấy. Tuy nhiên, tôi nghĩ rằng nó giải thích một chút về các cơ chế liên quan. Ngoài ra, câu hỏi của anh ấy hơi kém khi tôi trả lời câu hỏi. : p
Tối đa

Một cách tự nhiên. Xảy ra với tôi là tốt, và nó có giá trị của nó. Đó là lý do tại sao tôi chỉ đề xuất nó, nhưng không phàn nàn về nó. :)
Yam Marcovic

3

1) Các mẫu sự kiện, cổ điển là mẫu Observer, đây là một liên kết tốt của Microsoft Microsoft talk Observer

Bài viết bạn liên kết đến không được viết rất tốt và làm cho mọi thứ phức tạp hơn mức cần thiết. Kinh doanh tĩnh là lẽ thường, bạn không thể xác định giao diện với các thành viên tĩnh, vì vậy nếu bạn muốn hành vi đa hình trên phương thức tĩnh, bạn sẽ sử dụng một đại biểu.

Liên kết ở trên nói về các đại biểu và lý do tại sao có tốt cho sáng tác, do đó có thể giúp với truy vấn của bạn.


3

Trước hết, câu hỏi hay. Tôi ngưỡng mộ sự tập trung của bạn vào tiện ích thay vì mù quáng chấp nhận "thực hành tốt nhất". +1 cho điều đó.

Tôi đã đọc hướng dẫn đó trước đây. Bạn phải nhớ một cái gì đó về nó - nó chỉ là một hướng dẫn, chủ yếu dành cho những người mới tham gia C #, những người biết cách lập trình nhưng không quá quen thuộc với cách làm việc của C #. Đây không phải là một trang quy tắc vì đây là trang mô tả cách mọi thứ thường được thực hiện. Và vì họ đã thực hiện theo cách này ở mọi nơi, nên có thể nên kiên định.

Tôi sẽ đi đến điểm, trả lời câu hỏi của bạn.

Trước hết, tôi giả sử bạn đã biết giao diện là gì. Đối với một đại biểu, đủ để nói rằng đó là một cấu trúc có chứa một con trỏ được gõ vào một phương thức, cùng với một con trỏ tùy chọn cho đối tượng biểu thị thisđối số cho phương thức đó. Trong trường hợp phương thức tĩnh, con trỏ sau là null.
Ngoài ra còn có các đại biểu Multicast, giống như các đại biểu, nhưng có thể có một vài trong số các cấu trúc này được gán cho chúng (có nghĩa là một lệnh gọi Gọi trên một đại biểu đa hướng gọi tất cả các phương thức trong danh sách gọi được gán của nó).

Họ có ý nghĩa gì bởi một mẫu thiết kế sự kiện?

Chúng có nghĩa là sử dụng các sự kiện trong C # (có các từ khóa đặc biệt để triển khai một cách tinh vi mẫu cực kỳ hữu ích này). Các sự kiện trong C # được thúc đẩy bởi các đại biểu phát đa hướng.

Khi bạn xác định một sự kiện, chẳng hạn như trong ví dụ này:

class MyClass {
  // Note: EventHandler is just a multicast delegate,
  // that returns void and accepts (object sender, EventArgs e)!
  public event EventHandler MyEvent;

  public void DoSomethingThatTriggersMyEvent() {
    // ... some code
    var handler = MyEvent;
    if (handler != null)
      handler(this, EventArgs.Empty);
    // ... some other code
  }
}

Trình biên dịch thực sự biến đổi điều này thành đoạn mã sau:

class MyClass {
  private EventHandler MyEvent = null;

  public void add_MyEvent(EventHandler value) {
    MyEvent += value;
  }

  public void remove_MyEvent(EventHandler value) {
    MyEvent -= value;
  }

  public void DoSomethingThatTriggersMyEvent() {
    // ... some code
    var handler = MyEvent;
    if (handler != null)
      handler(this, EventArgs.Empty);
    // ... some other code
  }
}

Sau đó, bạn đăng ký một sự kiện bằng cách làm

MyClass instance = new MyClass();
instance.MyEvent += SomeMethodInMyClass;

Mà biên dịch xuống

MyClass instance = new MyClass();
instance.add_MyEvent(new EventHandler(SomeMethodInMyClass));

Vì vậy, đó là sự kiện trong C # (hoặc .NET nói chung).

Làm thế nào các thành phần hóa ra là dễ dàng nếu một đại biểu được sử dụng?

Điều này có thể dễ dàng được chứng minh:

Giả sử bạn có một lớp phụ thuộc vào một tập hợp các hành động được truyền cho nó. Bạn có thể gói gọn những hành động đó trong một giao diện:

interface RequiredMethods {
  void DoX();
  int DoY();
};

Và bất cứ ai muốn truyền các hành động đến lớp của bạn trước tiên sẽ phải thực hiện giao diện đó. Hoặc bạn có thể làm cho cuộc sống của họ dễ dàng hơn bằng cách phụ thuộc vào lớp sau:

sealed class RequiredMethods {
  public Action DoX;
  public Func<int> DoY();
}

Bằng cách này, người gọi chỉ phải tạo một thể hiện của RequestMethod và các phương thức liên kết với các đại biểu khi chạy. Đây thường dễ dàng hơn.

Cách làm này cực kỳ có lợi trong hoàn cảnh phù hợp. Hãy suy nghĩ về nó - tại sao phụ thuộc vào một giao diện khi tất cả những gì bạn thực sự quan tâm là việc triển khai được chuyển cho bạn?

Lợi ích của việc sử dụng giao diện khi có một nhóm các phương pháp liên quan

Việc sử dụng các giao diện là có lợi vì các giao diện thường yêu cầu triển khai thời gian biên dịch rõ ràng. Điều này có nghĩa là bạn tạo một lớp mới.
Và nếu bạn có một nhóm các phương thức liên quan trong một gói, thì có ích khi gói đó được sử dụng lại bởi các phần khác của mã. Vì vậy, nếu họ có thể đơn giản khởi tạo một lớp thay vì xây dựng một nhóm đại biểu, điều đó sẽ dễ dàng hơn.

Lợi ích của việc sử dụng giao diện nếu một lớp chỉ cần một triển khai

Như đã lưu ý trước đó, các giao diện được triển khai trong thời gian biên dịch - điều đó có nghĩa là chúng hiệu quả hơn so với việc gọi một đại biểu (đó là một mức độ không xác định trên mỗi se).

"Một triển khai" có thể có nghĩa là một triển khai tồn tại một nơi được xác định rõ.
Nếu một thực hiện có thể đến từ bất cứ nơi nào trong chương trình mà chỉ xảy ra phù hợp với các phương pháp chữ ký. Điều đó cho phép linh hoạt hơn, bởi vì các phương thức chỉ cần tuân theo chữ ký dự kiến, thay vì thuộc về một lớp thực hiện rõ ràng một giao diện cụ thể. Nhưng sự linh hoạt đó có thể phải trả giá và thực sự phá vỡ nguyên tắc Thay thế Liskov , bởi vì hầu hết các lần bạn muốn làm chứng, vì nó giảm thiểu khả năng xảy ra tai nạn. Cũng giống như gõ tĩnh.

Thuật ngữ này cũng có thể đề cập đến các đại biểu phát đa hướng ở đây. Các phương thức được khai báo bởi các giao diện chỉ có thể được thực hiện một lần trong một lớp thực hiện. Nhưng các đại biểu có thể tích lũy nhiều phương thức, sẽ được gọi là tuần tự.

Vì vậy, tất cả, có vẻ như hướng dẫn không đủ thông tin, và chỉ hoạt động như những gì nó là - một hướng dẫn, không phải là một quy tắc. Một số lời khuyên thực sự có thể nghe hơi mâu thuẫn. Tùy bạn quyết định khi nào nên áp dụng cái gì. Hướng dẫn dường như chỉ cho chúng ta một con đường chung.

Tôi hy vọng câu hỏi của bạn đã được trả lời cho sự hài lòng của bạn. Và một lần nữa, danh tiếng cho câu hỏi.


2

Nếu bộ nhớ .NET của tôi vẫn giữ, một đại biểu về cơ bản là một hàm ptr hoặc functor. Nó thêm một lớp cảm ứng vào một lệnh gọi hàm để các hàm có thể được thay thế mà không cần mã gọi. Đây là điều tương tự như một giao diện, ngoại trừ việc một giao diện đóng gói nhiều chức năng lại với nhau và một người triển khai phải thực hiện chúng cùng nhau.

Một mô hình sự kiện, nói rộng ra, là một nơi mà một cái gì đó phản ứng với các sự kiện từ mọi nơi (chẳng hạn như các thông báo của Windows). Tập hợp các sự kiện thường được kết thúc mở, chúng có thể đến theo bất kỳ thứ tự nào và không nhất thiết phải liên quan đến nhau. Các đại biểu hoạt động tốt cho điều này ở chỗ mỗi sự kiện có thể gọi một hàm duy nhất mà không cần tham chiếu đến một loạt các đối tượng triển khai cũng có thể chứa nhiều hàm không liên quan. Ngoài ra (đây là nơi bộ nhớ .NET của tôi mơ hồ), tôi nghĩ rằng nhiều đại biểu có thể được gắn vào một sự kiện.

Việc kết hợp, mặc dù tôi không rành lắm về thuật ngữ này, về cơ bản là thiết kế một đối tượng để có nhiều phần phụ hoặc con tổng hợp mà tác phẩm được truyền lại. Các đại biểu cho phép trẻ em được pha trộn và kết hợp theo cách đặc biệt hơn, nơi giao diện có thể bị quá mức hoặc gây ra quá nhiều khớp nối và sự cứng nhắc và dễ vỡ đi kèm với nó.

Lợi ích của một giao diện cho các phương thức liên quan là các phương thức có thể chia sẻ trạng thái của đối tượng triển khai. Các chức năng ủy nhiệm không thể chia sẻ một cách sạch sẽ, hoặc thậm chí chứa, trạng thái.

Nếu một lớp cần một triển khai duy nhất, một giao diện phù hợp hơn vì trong bất kỳ lớp nào thực hiện toàn bộ bộ sưu tập, chỉ có một thực hiện có thể được thực hiện và bạn nhận được lợi ích của một lớp thực hiện (trạng thái, đóng gói, v.v.). Nếu việc triển khai có thể thay đổi do trạng thái thời gian chạy, các đại biểu hoạt động tốt hơn vì chúng có thể được hoán đổi cho các triển khai khác mà không ảnh hưởng đến các phương thức khác. Ví dụ, nếu có ba đại biểu mà mỗi đại biểu có hai triển khai có thể, bạn sẽ cần tám lớp khác nhau thực hiện giao diện ba phương thức để tính tất cả các kết hợp trạng thái có thể có.


1

Mẫu thiết kế "sự kiện" (được gọi là Mẫu quan sát) cho phép bạn đính kèm nhiều phương thức của cùng một chữ ký cho đại biểu. Bạn thực sự không thể làm điều đó với một giao diện.

Tôi hoàn toàn không tin rằng thành phần dễ dàng hơn cho một đại biểu hơn là một giao diện. Đó là một tuyên bố rất kỳ quặc. Tôi tự hỏi nếu anh ta có nghĩa là bởi vì bạn có thể đính kèm các phương thức ẩn danh cho một đại biểu.


1

Làm rõ lớn nhất tôi có thể cung cấp:

  1. Một đại biểu xác định chữ ký hàm - tham số nào mà hàm khớp sẽ chấp nhận và hàm nào sẽ trả về.
  2. Một giao diện liên quan đến toàn bộ các chức năng, sự kiện, thuộc tính và trường.

Vì thế:

  1. Khi bạn muốn khái quát một số chức năng có cùng chữ ký - hãy sử dụng một đại biểu.
  2. Khi bạn muốn khái quát một số hành vi hoặc chất lượng của một lớp - hãy sử dụng một giao diện.

1

Câu hỏi của bạn về các sự kiện đã được bảo hiểm độc đáo. Và cũng đúng, một giao diện có thể định nghĩa nhiều phương thức (nhưng thực tế không cần thiết), trong khi một loại chức năng chỉ bao giờ đặt các ràng buộc đối với một chức năng riêng lẻ.

Tuy nhiên, sự khác biệt thực sự là:

  • các giá trị hàm được khớp với các kiểu hàm bằng cách phân nhóm cấu trúc (nghĩa là tương thích ngầm với cấu trúc được yêu cầu). Điều đó có nghĩa là, nếu một giá trị hàm có chữ ký tương thích với loại chức năng, thì đó là một giá trị hợp lệ cho loại.
  • các trường hợp được khớp với các giao diện bằng cách phân nhóm danh nghĩa (nghĩa là sử dụng tên rõ ràng). Điều đó có nghĩa là, nếu một cá thể là thành viên của một lớp, thực hiện rõ ràng giao diện đã cho, thì thể hiện đó là một giá trị hợp lệ cho giao diện. Tuy nhiên, nếu đối tượng chỉ có tất cả các thành viên được yêu cầu bởi các giao diện và tất cả các thành viên có chữ ký tương thích, nhưng không thực hiện rõ ràng giao diện, thì đó không phải là giá trị hợp lệ cho giao diện.

Chắc chắn, nhưng điều này có nghĩa là gì?

Hãy lấy ví dụ này (mã nằm trong haXe vì C # của tôi không tốt lắm):

class Collection<T> {
    /* a lot of code we don't care about now */
    public function filter(predicate:T->Bool):Collection<T> { 
         //build a new collection with all elements e, such that predicate(e) == true
    }
    public function remove(e:T):Bool {
         //removes an element from this collection, if contained and returns true, false otherwise
    }
}

Giờ đây, phương thức bộ lọc giúp dễ dàng chuyển qua một đoạn logic nhỏ mà không biết về tổ chức bên trong của bộ sưu tập, trong khi bộ sưu tập không phụ thuộc vào logic được cung cấp cho nó. Tuyệt quá. Ngoại trừ một vấn đề:
Bộ sưu tập không phụ thuộc vào logic. Bộ sưu tập vốn đã đưa ra giả định rằng hàm được truyền vào được thiết kế để kiểm tra giá trị theo điều kiện và trả về thành công của phép thử. Lưu ý rằng không phải tất cả các hàm, lấy một giá trị làm đối số và trả về boolean thực sự chỉ là các vị từ. Ví dụ, phương pháp loại bỏ bộ sưu tập của chúng tôi là một chức năng như vậy.
Giả sử chúng tôi đã gọi c.filter(c.remove). Kết quả sẽ là một bộ sưu tập với tất cả các yếu tố ctrong khictự nó trở nên trống rỗng Điều này thật đáng tiếc, vì tự nhiên chúng ta mong đợi cbản thân sẽ bất biến.

Ví dụ rất được xây dựng. Nhưng vấn đề chính là, mã gọi c.filtervới một số giá trị hàm là đối số không có cách nào để biết liệu đối số đó có phù hợp hay không (nghĩa là cuối cùng sẽ bảo tồn bất biến). Mã tạo ra giá trị hàm có thể hoặc không thể biết, nó sẽ được hiểu là một vị ngữ.

Bây giờ hãy thay đổi mọi thứ:

interface Predicate<T> {
    function test(value:T):Bool;
}
class Collection<T> {
    /* a lot of code we don't care about now */
    public function filter(predicate:Predicate<T>):Collection<T> { 
         //build a new collection with all elements e, such that predicate.test(e) == true
    }
    public function remove(e:T):Bool {
         //removes an element from this collection, if contained and returns true, false otherwise
    }
}

Điều gì đã thay đổi? Điều đã thay đổi là, bất cứ giá trị nào bây giờ được đưa ra filterđều đã ký hợp đồng rõ ràng là một vị ngữ. Tất nhiên các lập trình viên độc hại hoặc cực kỳ ngu ngốc tạo ra các triển khai giao diện không có tác dụng phụ và do đó không phải là vị ngữ.
Nhưng điều không còn có thể xảy ra là ai đó liên kết một đơn vị dữ liệu / mã logic, điều này bị hiểu nhầm là một vị ngữ vì cấu trúc bên ngoài của nó.

Vì vậy, để viết lại những gì đã nói ở trên chỉ trong một vài từ:

  • giao diện có nghĩa là sử dụng gõ danh nghĩa và do đó mối quan hệ rõ ràng
  • đại biểu có nghĩa là sử dụng kiểu cấu trúc và do đó các mối quan hệ ngầm

Ưu điểm của các mối quan hệ rõ ràng là, bạn có thể chắc chắn về chúng. Nhược điểm là, họ yêu cầu chi phí của nhân chứng. Ngược lại, nhược điểm của các mối quan hệ ngầm (trong trường hợp của chúng tôi là chữ ký của một hàm khớp với chữ ký mong muốn), là bạn thực sự không thể chắc chắn 100% rằng bạn có thể sử dụng mọi thứ theo cách này. Ưu điểm là, bạn có thể thiết lập các mối quan hệ mà không cần tất cả chi phí. Bạn có thể nhanh chóng ném mọi thứ lại với nhau, bởi vì cấu trúc của chúng cho phép nó. Đó là những gì thành phần dễ dàng có nghĩa.
Nó hơi giống với LEGO: bạn có thể chỉ cần cắm một ngôi sao chiến tranh LEGO lên một con tàu cướp biển LEGO, đơn giản vì cấu trúc bên ngoài cho phép nó. Bây giờ bạn có thể cảm thấy rằng đó là sai lầm khủng khiếp, hoặc nó có thể chỉ là chính xác những gì bạn muốn. Không ai sẽ ngăn cản bạn.


1
Điều đáng nói là "thực hiện rõ ràng giao diện" (trong "phân nhóm danh nghĩa") không giống như triển khai rõ ràng hoặc ngầm định của các thành viên giao diện. Trong C #, các lớp phải luôn khai báo rõ ràng các giao diện mà chúng thực hiện, như bạn nói. Nhưng các thành viên giao diện có thể được triển khai một cách rõ ràng (ví dụ int IList.Count { get { ... } }:) hoặc ngầm ( public int Count { get { ... } }). Sự khác biệt này không liên quan đến cuộc thảo luận này, nhưng nó đáng được đề cập để tránh gây nhầm lẫn cho độc giả.
phoog

@phoog: Vâng, cảm ơn vì đã sửa đổi. Tôi thậm chí không biết cái đầu tiên là có thể. Nhưng vâng, cách một ngôn ngữ thực sự thực thi giao diện thay đổi rất nhiều. Trong Objective-C chẳng hạn, nó sẽ chỉ đưa ra cảnh báo nếu nó không được thực thi.
back2dos

1
  1. Một mẫu thiết kế sự kiện ngụ ý một cấu trúc bao gồm cơ chế xuất bản và đăng ký. Các sự kiện được xuất bản bởi một nguồn, mọi người đăng ký đều nhận được bản sao của mục được xuất bản và chịu trách nhiệm cho hành động của chính họ. Đó là một cơ chế kết hợp lỏng lẻo vì nhà xuất bản thậm chí không phải biết những người đăng ký đang ở đó. Hãy để một mình có người đăng ký là không bắt buộc (có thể không có)
  2. thành phần là "dễ dàng hơn" nếu một đại biểu được sử dụng so với một giao diện. Các giao diện định nghĩa một thể hiện của lớp là một đối tượng "loại trình xử lý" trong đó như một thể hiện cấu trúc thành phần dường như "có một trình xử lý", do đó sự xuất hiện của cá thể ít bị hạn chế hơn bằng cách sử dụng một ủy nhiệm và trở nên linh hoạt hơn.
  3. Từ nhận xét bên dưới, tôi thấy rằng tôi cần cải thiện bài đăng của mình, hãy xem bài viết này để tham khảo: http://bytes.com/topic/c-sharp/answers/252309-interface-vs-delegate

1
3. Tại sao các đại biểu yêu cầu đúc nhiều hơn và tại sao khớp nối chặt chẽ hơn lại có lợi? 4. Bạn không phải sử dụng các sự kiện để sử dụng các đại biểu và với các đại biểu, nó giống như giữ một phương thức - bạn biết đó là kiểu trả về và kiểu của đối số. Ngoài ra, tại sao một phương thức được khai báo trong giao diện sẽ giúp xử lý lỗi dễ dàng hơn so với phương thức được gắn với đại biểu?
Yam Marcovic

Trong trường hợp chỉ là thông số kỹ thuật của một đại biểu, bạn đã đúng. Tuy nhiên, trong trường hợp xử lý sự kiện (ít nhất đó là những gì tài liệu tham khảo của tôi có liên quan), bạn luôn cần phải kế thừa một số loại eventArss để hoạt động như một thùng chứa cho các loại tùy chỉnh, vì vậy thay vì có thể kiểm tra một cách an toàn cho giá trị bạn sẽ luôn luôn phải mở khóa loại của bạn trước khi xử lý các giá trị.
Carlo Kuip

Về lý do tại sao tôi nghĩ rằng một phương thức được xác định trong giao diện sẽ giúp xử lý lỗi dễ dàng hơn là trình biên dịch có thể kiểm tra các loại ngoại lệ được ném từ phương thức đó. Tôi biết đó không phải là bằng chứng thuyết phục nhưng có lẽ nên được nêu là ưu tiên?
Carlo Kuip

1
Đầu tiên chúng ta đã nói về đại biểu so với giao diện, không phải sự kiện so với giao diện. Thứ hai, làm cho một sự kiện dựa trên một đại biểu EventHandler chỉ là một quy ước và không có nghĩa là bắt buộc. Nhưng một lần nữa, nói về các sự kiện ở đây loại bỏ lỡ điểm. Đối với nhận xét thứ hai của bạn - ngoại lệ không được kiểm tra trong C #. Bạn đang bị lẫn lộn với Java (tình cờ thậm chí không có đại biểu).
Yam Marcovic

Tìm thấy một chủ đề về chủ đề này giải thích sự khác biệt quan trọng: byte.com/topic/c-sharp/answers/252309-interface-vs-delegate
Carlo Kuip
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.