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 220VKỹ 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.
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ênIDisplayabletrong vùng tênReports. 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ứcRender()trả về một chuỗi.IPrintable.cs: Định nghĩa giao diện có tênIPrintabletrong vùng tênReports. 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ứcRender()trả về một chuỗi. Lưu ý: điều này tạo ra xung đột tên vớiIDisplayable!Report.cs: Định nghĩa lớpReporttrong vùng tênReports, triển khai cảIDisplayablevàIPrintable. Báo cáo cần có thuộc tínhTitle(chuỗi) và thuộc tínhContent(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 viRender()khác nhau:- Khi hiển thị:
"[SCREEN] {Title}: {Content}" - Khi in:
"[PRINT] {Title} | {Content}"
- Khi hiển thị:
Program.cs: Trong tệp chính, tạo mộtReportbằ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ếnIDisplayablelẫn biếnIPrintable, sau đó gọiRender()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
}
}
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
1Các khái niệm cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiKhông gian tên và chỉ thịGiới thiệu về lớp và đối tượngTừ khóa thisPhương thức và tham sốTrường và thuộc tínhHàm khởi tạoKhởi tạo đối tượngÔn tập: Máy tính đơn giản4Kế thừa
Cú pháp kế thừa cơ bản (:)Từ khóa 'base'Từ khóa virtual và overrideLớp sealedLớp cơ sở 'object'Ôn tập: hệ phân cấp nhân viên7Tính năng nâng cao
Nạp chồng toán tửBộ lập chỉ mục (this[])Ghi đè ToString()Phương thức mở rộngÔn tập: Danh sách tùy chỉnh10Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếSingleton an toàn với luồngMẫu FactoryMẫu Observer (sự kiện)Mẫu Strategy2Thuộc tính và thành viên tĩnh
Thuộc tính tự độngThuộc tính chỉ đọc hoặc chỉ ghiTrường và phương thức tĩnhLớp tĩnhThành viên có thân biểu thức5Đa hình và interface
Đa hình lúc biên dịch và lúc chạyInterface và lớp trừu tượngNhiều interfaceInterface tường minhUpcasting và downcastingÔn tập: bộ tính toán hình học8Các khái niệm OOP nâng cao
Ưu tiên composition hơn kế thừaGenerics (lớp và phương thức)Delegate và sự kiệnAttribute và reflectionGiao diện IDisposable và câu lệnh usingKiến thức cơ bản về dependency injection11Các mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpTự luyện tập: Trình biên dịch C# online