Lý do tốt để sử dụng thực hiện giao diện rõ ràng cho mục đích duy nhất là ẩn các thành viên là gì?


11

Trong một trong những nghiên cứu của tôi về sự phức tạp của C #, tôi đã bắt gặp một đoạn thú vị liên quan đến việc thực hiện giao diện rõ ràng.

While this syntax is quite helpful when you need to resolve name clashes, you can use explicit interface implementation simply to hide more "advanced" members from the object level.

Sự khác biệt giữa việc cho phép sử dụng object.method()hoặc yêu cầu đúc ((Interface)object).method()có vẻ giống như sự xáo trộn có ý nghĩa với đôi mắt thiếu kinh nghiệm của tôi. Văn bản lưu ý rằng điều này sẽ ẩn phương thức khỏi Intellisense ở cấp đối tượng, nhưng tại sao bạn lại muốn làm điều đó nếu không cần thiết để tránh xung đột tên?


Câu trả lời:


12

Tưởng tượng một tình huống trong đó một giao diện buộc một lớp thực hiện các phương thức không thực sự có ý nghĩa như là một phần của lớp. Điều này thường có thể xảy ra khi các giao diện đặc biệt lớn và vi phạm Nguyên tắc phân chia giao diện.

Lớp này cần triển khai giao diện, nhưng thay vì làm ô nhiễm giao diện công cộng dễ nhìn thấy của nó bằng các phương thức không có ý nghĩa, bạn có thể thực hiện rõ ràng các phương thức mà bạn không muốn có sẵn rõ ràng. Giao diện công cộng có thể nhìn thấy cao của lớp là cách bạn muốn lớp được sử dụng và việc triển khai rõ ràng là có khi lớp được truy cập thông qua giao diện.


6
Có phải chỉ có tôi, hoặc điều này nghe có vẻ là một ý tưởng khủng khiếp?
Kevin Peno

5
@Kevin, làm sao vậy? Đôi khi giao diện nằm ngoài tầm kiểm soát của bạn và bạn phải sử dụng nó. Giảm thiểu thiệt hại của giao diện hống hách có vẻ hợp lý.
Chris Pitman

1
+1 để chỉ ra rằng nhiều lần thế giới thực cản trở cách làm việc đúng đắn. @Kevin vâng, đó là một ý tưởng tồi tệ nhưng đôi khi bạn không có lựa chọn nào và phải làm việc với slop - tốt hơn là giấu nó đi và làm cho mặt tiền của việc thực hiện đúng khi bạn không thể thực hiện đúng.
Wayne Molina

@Wayne, tôi hiểu lý do của bạn khi muốn / sử dụng một hệ thống như vậy. Chết tiệt, có lẽ tôi sẽ sử dụng nó để chính xác trong trường hợp bạn nêu. Tôi đoán nhận xét ngắn của tôi thực sự chỉ ra rằng điều này là không đúng, vậy tại sao lại cho phép nó bằng ngôn ngữ.
Kevin Peno

1
Sự tách biệt giao diện quá mức có thể là một trở ngại lớn cho thành phần và tổng hợp. Ví dụ, xem xét rằng người ta muốn có một triển khai trong IEnumerable<T>đó hành xử như sự kết hợp của hai người khác. Nếu IEnumerable<T>bao gồm một thuộc tính để cho biết số lượng của nó đã được biết, có khả năng là vô hạn hay không, cùng với một phương thức để hỏi số lượng của nó là gì, một lớp tổng hợp nhiều IEnumerable<T>và hoạt động như một phép nối có thể báo cáo số lượng của nó một cách hiệu quả trong trường hợp một số các bộ sưu tập đóng gói biết số lượng của họ.
supercat

4

Ứng dụng hữu ích nhất mà tôi tìm thấy là với các nhà máy triển khai. Trong nhiều trường hợp, rất hữu ích khi tạo các lớp có thể thay đổi bên trong nhà máy nhưng không thay đổi đối với các lớp bên ngoài. Điều này có thể được thực hiện dễ dàng trong Java bằng cách sử dụng các lớp bên trong, bằng cách đặt các trường và setters của chúng ở chế độ riêng tư và chỉ hiển thị các getters là thành viên công cộng. Tuy nhiên, trong C #, tôi đã phải sử dụng các giao diện rõ ràng để đạt được điều tương tự. Tôi sẽ giải thích thêm:

Trong Java, lớp bên trong VÀ lớp bên ngoài có thể truy cập các thành viên riêng của nhau, điều này có nghĩa hoàn toàn vì các lớp có liên quan rất chặt chẽ với nhau. Chúng nằm trong cùng một tệp mã và có thể được phát triển bởi cùng một nhà phát triển. Điều này có nghĩa là các trường và phương thức riêng trong lớp bên trong vẫn có thể được nhà máy truy cập để sửa đổi giá trị của chúng. Nhưng các lớp bên ngoài sẽ không thể truy cập vào các trường này ngoại trừ thông qua các công cụ thu thập công khai của họ.

Tuy nhiên, trong C #, các lớp bên ngoài không thể truy cập các thành viên riêng của các lớp bên trong để khái niệm đó không được áp dụng trực tiếp. Tôi đã sử dụng một giao diện rõ ràng như một cách giải quyết bằng cách định nghĩa một giao diện riêng trong lớp bên ngoài và thực hiện rõ ràng nó trong lớp bên trong. Theo cách này, chỉ có lớp bên ngoài có thể truy cập các phương thức trong giao diện này giống như cách nó được thực hiện trong Java (nhưng chúng phải là các phương thức chứ không phải các trường).

Thí dụ:

public class Factory
{
    // factory method to create a hard-coded Mazda Tribute car.
    public static Car CreateCar()
    {
        Car car = new Car();

        // the Factory class can modify the model because it has access to
        // the private ICarSetters interface
        ((ICarSetters)car).model = "Mazda Tribute";

        return car;
    }

    // define a private interface containing the setters.
    private interface ICarSetters
    {
        // define the setter in the private interface
        string model { set; }
    }

    // This is the inner class. It has a member "model" that should not be modified
    // but clients, but should be modified by the factory.
    public class Car: ICarSetters
    {
        // explicitly implement the setter
        string ICarSetters.model { set; }

        // create a public getter
        public string model { get; }
    }
}

class Client
{
    public Client()
    {
        Factory.Car car = Factory.CreateCar();

        // can only read model because only the getter is public
        // and ICarSetters is private to Factory
        string model = car.model;
    }
}

Đó là những gì tôi sẽ sử dụng giao diện rõ ràng cho.


3

Nếu một lớp thực hiện hai giao diện có chứa một thành viên có cùng chữ ký, thì việc triển khai thành viên đó trên lớp sẽ khiến cả hai giao diện sử dụng thành viên đó làm triển khai. Trong ví dụ sau, tất cả các lệnh gọi để Paintgọi cùng một phương thức. C #

class Test 
{
    static void Main()

    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method. 
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

Ngược lại, việc thực hiện rõ ràng một (hoặc cả hai) chúng cho phép bạn chỉ định các hành vi khác nhau cho mỗi hành vi.


1

Giao diện rõ ràng rất tiện lợi khi một đối tượng có hai hoặc nhiều hình thức giao tiếp rất khác biệt.

Ví dụ, một đối tượng duy trì một tập hợp các đối tượng con cần giao tiếp với cha mẹ.

Có một số hành động chúng tôi chỉ muốn truy cập đối với người gọi bên ngoài và một số hành động chúng tôi chỉ muốn truy cập vào các đối tượng con.

Giao diện rõ ràng giúp đảm bảo sự phân chia này.

    static void Main(string[] args)
    {
        var parent = new Parent();
        parent.DoExternalStuff();
    }

    interface IExternal {
        void DoExternalStuff();
    }

    interface IInternal {
        void DoInternalStuff();
    }

    class Parent : IExternal, IInternal
    {
        public Parent()
        {
            var child = new Child(this);
        }

        public void DoExternalStuff()
        {
           // do something exciting here for external callers
        }

        void IInternal.DoInternalStuff()
        {
            // do something exciting here for internal callers
        }
    }

    class Child
    {
        public Child(IInternal parent)
        {
            parent.DoInternalStuff();
        }
    }

0

Có lẽ nếu bạn có chức năng hữu ích cho người dùng, nhưng chỉ khi bạn thực sự biết những gì bạn đang làm (đủ để đọc tài liệu để tìm hiểu về chức năng) nhưng điều đó có khả năng phá vỡ mọi thứ nếu bạn không.

Tại sao bạn làm điều đó thay vì cung cấp, hãy nói "giao diện tính năng nâng cao" thứ hai mà tôi không biết.


0

Mã thường được đọc nhiều hơn nó được viết và tôi chỉ sử dụng Intellisense để viết mã nhanh chóng. Tôi sẽ không viết mã theo một cách cụ thể chỉ để làm cho Intellisense đẹp hơn.

Tôi đặc biệt không thấy

((Giao diện) đối tượng) .method () là bất kỳ dễ đọc hơn object.method ().

Nếu tôi có rất nhiều phương thức trên một đối tượng cụ thể, tôi có thể đặt câu hỏi liệu đó có phải là một đối tượng thần hay không và thực sự nên được chia thành nhiều đối tượng đơn giản hơn.

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.