Menu
CoddyTech

Interface tường minh

Thuộc phần Lập trình hướng đối tượng trong hành trình C# của Coddy. Bài học 28 trên 70.

Khi một lớp triển khai nhiều giao diện có thành viên cùng tên, bạn sẽ gặp xung đột tên. Triển khai giao diện tường minh giúp giải quyết vấn đề này bằng cách cho phép bạn cung cấp cách triển khai riêng cho từng giao diện.

Với cách triển khai tường minh, bạn thêm tên giao diện vào trước tên thành viên và bỏ bộ điều chỉnh quyền truy cập:

public interface IAmericanPlug
{
    void Connect();
}

public interface IEuropeanPlug
{
    void Connect();
}

public class UniversalAdapter : IAmericanPlug, IEuropeanPlug
{
    void IAmericanPlug.Connect()
    {
        Console.WriteLine("Connected to 120V");
    }
    
    void IEuropeanPlug.Connect()
    {
        Console.WriteLine("Connected to 220V");
    }
}

Chỉ có thể truy cập các thành viên được triển khai tường minh thông qua biến có kiểu giao diện, không phải thông qua chính lớp đó:

var adapter = new UniversalAdapter();
// adapter.Connect();  // Không biên dịch được!

IAmericanPlug american = adapter;
american.Connect();  // Đã kết nối với 120V

IEuropeanPlug european = adapter;
european.Connect();  // Đã kết nối với 220V

Kỹ thuật này cũng hữu ích khi bạn muốn ẩn các thành viên giao diện khỏi API công khai của lớp. Nếu một phương thức chỉ có ý nghĩa khi xem đối tượng như một giao diện cụ thể, thì việc triển khai tường minh giúp lớp của bạn có bề mặt API gọn gàng mà vẫn đáp ứng đúng hợp đồng.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống tạo báo cáo để minh họa cách triển khai giao diện tường minh cho phép một lớp cung cấp các hành vi khác nhau tùy thuộc vào giao diện đang được sử dụng. Bạn sẽ tạo một tài liệu có thể được định dạng khác nhau để hiển thị và in.

Bạn sẽ sắp xếp code trong bốn tệp:

  • IDisplayable.cs: Định nghĩa giao diện có tên IDisplayable trong vùng tên Reports. Giao diện này đại diện cho nội dung có thể hiển thị trên màn hình. Giao diện cần khai báo phương thức Render() trả về một chuỗi.
  • IPrintable.cs: Định nghĩa giao diện có tên IPrintable trong vùng tên Reports. Giao diện này đại diện cho nội dung có thể gửi đến máy in. Giao diện cũng cần khai báo phương thức Render() trả về một chuỗi. Lưu ý: điều này tạo ra xung đột tên với IDisplayable!
  • Report.cs: Định nghĩa lớp Report trong vùng tên Reports, triển khai cả IDisplayable và IPrintable. Báo cáo cần có thuộc tính Title (chuỗi) và thuộc tính Content (chuỗi), cùng một hàm khởi tạo đặt cả hai giá trị. Dùng cách triển khai giao diện tường minh để cung cấp các hành vi Render() khác nhau:
    • Khi hiển thị: "[SCREEN] {Title}: {Content}"
    • Khi in: "[PRINT] {Title} | {Content}"
  • Program.cs: Trong tệp chính, tạo một Report bằng các giá trị đầu vào. Vì chỉ có thể truy cập các phần triển khai tường minh thông qua biến giao diện, hãy lưu cùng một báo cáo vào cả biến IDisplayable lẫn biến IPrintable, sau đó gọi Render() trên mỗi biến để cho thấy cùng một đối tượng tạo ra đầu ra khác nhau tùy thuộc vào giao diện bạn dùng.

Bạn sẽ nhận hai giá trị đầu vào:

  • Tiêu đề báo cáo
  • Nội dung báo cáo

In đầu ra theo định dạng sau:

Display version:
{Render() via IDisplayable}
Print version:
{Render() via IPrintable}

Ví dụ: nếu đầu vào là Sales Report và Q4 revenue increased by 15%, đầu ra sẽ là:

Display version:
[SCREEN] Sales Report: Q4 revenue increased by 15%
Print version:
[PRINT] Sales Report | Q4 revenue increased by 15%

Hãy chú ý cách cùng một đối tượng Report hoạt động khác nhau tùy thuộc vào tham chiếu giao diện bạn dùng để truy cập đối tượng đó. Đây chính là sức mạnh của việc triển khai giao diện tường minh, giải quyết xung đột tên đồng thời cung cấp hành vi phù hợp với từng ngữ cảnh!

Tự làm thử

using System;
using Reports;

class Program
{
    public static void Main(string[] args)
    {
        // Đọc đầu vào
        string title = Console.ReadLine();
        string content = Console.ReadLine();

        // TODO: Tạo đối tượng (object) Report với tiêu đề và nội dung

        // TODO: Lưu báo cáo vào biến (variable) IDisplayable

        // TODO: Lưu cùng báo cáo đó vào biến IPrintable

        // TODO: In ra phiên bản hiển thị bằng cách gọi Render() trên IDisplayable
        Console.WriteLine("Display version:");
        // In kết quả Render() ra đây

        // TODO: In ra phiên bản để in bằng cách gọi Render() trên IPrintable
        Console.WriteLine("Print version:");
        // In kết quả Render() ra đây
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch C# online