Interface trong C#, loosely coupling

Interface trong C # là một bản “ hợp đồng ” miêu tả những gì cần phải làm mà những class thực thi interface đó phải tuân thủ theo. Interface trong C # là một công cụ đặc biệt quan trọng mạnh giúp tạo ra mối quan hệ lỏng giữa những class, qua đó giúp tăng trưởng và test những thành phần ( class ) một cách độc lập .Tuy vậy, đây là một kiểu tài liệu rất khó hiểu với nhiều bạn. Đặc biệt, rất nhiều bạn không biết cách vận dụng interface trong lập trình .Bài học này sẽ cố gắng nỗ lực giúp bạn hiểu interface là gì, vai trò của interface trong C #, cũng như những kỹ thuật thao tác với interface trong C # .

Interface trong C # là gì ?

Quan hệ phụ thuộc vào giữa những class

Class B được coi là nhờ vào chặt vào class A nếu class A được sử dụng trong code của B ( như tham chiếu tới A, nhận tham số kiểu A, khởi tạo object của A, khai báo biến của A, v.v. ) .Quan hệ phụ thuộc vào chặt này đơn thuần khi sử dụng nhưng hoàn toàn có thể gây ra nhiều hậu quả. Quan hệ phụ thuộc vào chặt nhu yếu những lớp phụ thuộc vào phải kiến thiết xây dựng sau, dẫn tới không hề tăng trưởng song song những class. Quan hệ chặt cũng hoàn toàn có thể gây khó khăn vất vả cho việc test những class độc lập ( vì chúng phụ thuộc vào vào nhau ) .Để hoàn toàn có thể tăng trưởng song song hoặc thuận tiện sửa chữa thay thế class này bằng class khác, người ta cần làm giảm sự nhờ vào giữa những class, thay phụ thuộc vào chặt bằng phụ thuộc vào lỏng ( loosely-coupling ) .Một công cụ rất mạnh thường được sử dụng để làm giảm sự phụ thuộc vào này là giao diện ( interface ) .

Khái niệm interface trong C #

Interface là một kiểu tài liệu tương tự như như class nhưng chỉ đưa ra diễn đạt ( specification / declaration ) của những thành viên mà không đưa ra phần thực thi ( phần thân, body toàn thân / implementation ). Phần thân của những phương pháp sẽ phải được kiến thiết xây dựng trong những class thực thi giao diện này .Một cách gần đúng, interface gần giống như một abstract class trong đó tổng thể những phương pháp của class đều được ghi lại là abstract .Cũng giống như abstract class, interface không hề dùng để khởi tạo object mà chỉ để những lớp đơn cử “ kế thừa ”. Khi một class “ thừa kế ” từ một interface, nó bắt buộc phải phân phối phần thực thi cho tổng thể những thành viên của interface ( tương tự như như phải thực thi toàn bộ những thành viên abstract ) .Interface tạo ra một bản “ hợp đồng ” miêu tả những gì cần phải làm mà những class thực thi interface đó phải tuân thủ theo. Khi đó, những class phối hợp với nhau trải qua bản hợp đồng này mà không cần biết đến nhau nữa ( làm mất quan hệ chặt ) .Vì đặc thù đó, interface trở thành một công cụ đặc biệt quan trọng mạnh giúp tạo ra mối quan hệ lỏng giữa những class, qua đó giúp tăng trưởng và test những thành phần ( class ) một cách độc lập .Khi sử dụng interface vẫn phải thực thi khởi tạo object của một class đơn cử thực thi interface này. Thao tác khởi tạo này triển khai ở một class trung gian .

Ví dụ minh họa

Hãy cùng thực thi ví dụ sau để hiểu kỹ hơn về cách sử dụng interface

using System;
namespace ConsoleApp
{
    internal interface IPet // khai báo interface với hai phương thức
    {
        void Feed(); // mô tả phương thức (không có thân)
        void Sound();
    }
    internal interface IBird // khai báo interface với ba phương thức
    {
        void Fly();
        void Sound();
        void Feed();
    }
    internal class Cat : IPet // Cat thực thi IPet
    {
        public Cat() => Console.WriteLine("I'm a cat. ");
        // thực thi cho phương thức Feed và Sound
        // hai phương thức này thực thi theo kiểu implicit
        public void Feed() => Console.WriteLine("Fish, please!");
        public void Sound() => Console.WriteLine("Meow meow!");
    }
    internal class Dog : IPet // Dog thực thi IPet
    {
        public Dog() => Console.WriteLine("I'm a dog. ");
        // cả hai phương thức Feed và Sound thực thi kiểu explicit.
        // Object của Dog không thể gọi hai phương thức này.
        // Hai phương thức này chỉ có thể gọi qua giao diện IPet
        void IPet.Feed() => Console.WriteLine("Bone, please!");
        void IPet.Sound() => Console.WriteLine("Woof woof!");
    }
    internal class Parrot : IPet, IBird // Parrot thực thi cả hai giao diện
    {
        public Parrot() => Console.WriteLine("I'm a parrot. ");
        // hai phương thức này thực thi kiểu implicit, do đó
        // có thể gọi từ object của Parrot
        public void Feed() => Console.WriteLine("Nut, please!");
        public void Fly() => Console.WriteLine("Yeah, I can fly!");
        // hai phương thức này thực thi kiểu explicit, do đó
        // không thể gọi từ object của Parrot 
        // mà chỉ có thể gọi qua giao diện IPet hoặc IBird
        void IPet.Sound() => Console.WriteLine("I can speak!");
        void IBird.Sound() => Console.WriteLine("I can sing, too!");
    }
    internal class BirdLover
    {
        private IBird _bird;
        public BirdLover(IBird bird) => _bird = bird;
        public void Play()
        {
            // _bird có thể gọi đủ các phương thức của IBird
            Console.Write("Fly ...");
            _bird.Fly();
            Console.Write("Say something ...");
            _bird.Sound();
            Console.Write("What do you like to eat? ");
            _bird.Feed();
        }
    }
    internal class PetLover
    {
        private IPet _pet;
        public PetLover(IPet pet) => _pet = pet;
        public PetLover() { }
        public void Play()
        {
            // _pet có thể gọi đủ các phương thức của IPet
            Console.Write("What do you like to eat? ");
            _pet.Feed();
            Console.Write("Now say something ... ");
            _pet.Sound();
        }
    }
    internal class _18_interface
    {
        private static void Main()
        {
            IPet pet = new Dog();
            PetLover petLover = new PetLover(pet);            
            petLover.Play();
            petLover = new PetLover(new Parrot());
            petLover.Play();
            BirdLover birdLover = new BirdLover(new Parrot());
            birdLover.Play();
            Cat cat = new Cat();
            // cat có thể gọi được Feed và Sound
            cat.Feed(); cat.Sound();
            IPet cat2 = new Cat();
            // cat2 có thể gọi Feed và Sound
            cat2.Feed(); cat2.Sound();
            Parrot parrot = new Parrot();
            // (gọi qua object) parrot chỉ gọi được Feed và Fly, không gọi được Sound
            parrot.Feed(); parrot.Fly();
            IBird parrot2 = new Parrot();
            // (gọi qua giao diện) parrot2 gọi được đủ 3 phương thức của IBird 
            parrot2.Feed(); parrot2.Fly(); parrot2.Sound();
            // dog không gọi được phương thức nào (gọi qua object) do
            // cả hai phương thức của Dog đều thực hiện kiểu explicit
            Dog dog = new Dog();
            IPet dog2 = new Dog();
            // gọi qua giao diện: dog2 gọi được cả Feed và Sound
            dog2.Feed(); dog2.Sound();            
            Console.ReadKey();
        }
    }
}

Kỹ thuật lập trình với Interface trong C #

Khai báo kiểu interface

Trong ví dụ trên tất cả chúng ta kiến thiết xây dựng hai interface : IPet và IBird

  • IPet
  • IBird
internal interface IPet // khai báo interface với hai phương thức
{
    void Feed(); // mô tả phương thức (không có thân)
    void Sound();
}

Interface được khai báo với từ khóa interface và list diễn đạt những phương pháp, đặc tính hoặc biến thành viên .Một interface hoàn toàn có thể được sử dụng nội bộ trong project, hoặc được sử dụng bởi những project khác. Trong trường hợp thứ nhất ( mặc định ), interface sử dụng từ khóa điểu khiển truy vấn internal ( tựa như class ), và do đó hoàn toàn có thể không cần viết từ khóa internal. Trong trường hợp thứ hai sử dụng từ khóa public .Interface là một kiểu tài liệu cùng Lever với class, do đó hoàn toàn có thể được khai báo trực tiếp trong khoảng trống tên hoặc trong khoanh vùng phạm vi của class khác. Tên của interface được đặt giống quy ước tên class nhưng có thêm chữ “ I ” đứng trước. Như ví dụ trên, tên hai interface lần lượt là IPet, IBird .Trong interface chỉ có những diễn đạt, không có thân phương pháp. Mô tả phương pháp không có từ khóa tinh chỉnh và điều khiển truy vấn ( tức là không có public, private, protected trước những miêu tả ) .

Thực thi interface

Mặc dù interface là một kiểu tài liệu nhưng tự bản thân nó không có năng lực sinh ra object mà chỉ hoàn toàn có thể tạo ra biến tham chiếu đến object của những class khác tuân thủ theo pháp luật của interface .Interface được sử dụng làm khuôn mẫu để sinh ra những class khác ( gần giống như lớp abstract ). Việc tạo ra một class trên cơ sở khuôn mẫu của interface gọi là thực thi interface .Cấu trúc cú pháp để một class thực thi một interface như sau :

internal class Cat : IPet // Cat thực thi IPet
internal class Dog : IPet // Dog thực thi IPet 

Một class cũng hoàn toàn có thể thực thi nhiều interface :

internal class Parrot : IPet, IBird // Parrot thực thi cả hai giao diện

Khi một class thực thi một hoặc nhiều interface, nó có nghĩa vụ phải xây dựng tất cả các thành viên được mô tả trong interface. Visual Studio hỗ trợ bằng cách đánh dấu lỗi cú pháp (gạch chân đỏ) nếu class chưa xây dựng đủ các thành viên của interface theo yêu cầu.

Có hai phương pháp thực thi những thành viên của interface : implicit và explicit .Trong cách thực thi implicit không chỉ rõ là phương pháp được thực thi thuộc về interface nào ; ngược lại, cách thực thi explicit phải chỉ rõ phương pháp đang thực thi thuộc về interface nào .Lớp Cat ở đây trọn vẹn vận dụng cách thực thư implicit .

internal class Cat : IPet // Cat thực thi IPet
{
    public Cat() => Console.WriteLine("I'm a cat. ");
    // thực thi cho phương thức Feed và Sound
    // hai phương thức này thực thi theo kiểu implicit
    public void Feed() => Console.WriteLine("Fish, please!");
    public void Sound() => Console.WriteLine("Meow meow!");
}

Lớp Dog lại trọn vẹn thực thi kiểu explicit. Mỗi phương pháp khi thực thi phải chỉ rõ nó thuộc interface nào .

internal class Dog : IPet // Dog thực thi IPet
{
    public Dog() => Console.WriteLine("I'm a dog. ");
    // cả hai phương thức Feed và Sound thực thi kiểu explicit.
    // Object của Dog không thể gọi hai phương thức này.
    // Hai phương thức này chỉ có thể gọi qua giao diện IPet
    void IPet.Feed() => Console.WriteLine("Bone, please!");
    void IPet.Sound() => Console.WriteLine("Woof woof!");
}
internal class Dog : IPet // Dog thực thi IPet
{
    public Dog() => Console.WriteLine("I'm a dog. ");
    // cả hai phương thức Feed và Sound thực thi kiểu explicit.
    // Object của Dog không thể gọi hai phương thức này.
    // Hai phương thức này chỉ có thể gọi qua giao diện IPet
    void IPet.Feed() => Console.WriteLine("Bone, please!");
    void IPet.Sound() => Console.WriteLine("Woof woof!");
}

Lớp Parrot vận dụng cả implicit và explicit

internal class Parrot : IPet, IBird // Parrot thực thi cả hai giao diện
{
    public Parrot() => Console.WriteLine("I'm a parrot. ");
    // hai phương thức này thực thi kiểu implicit, do đó
    // có thể gọi từ object của Parrot
    public void Feed() => Console.WriteLine("Nut, please!");
    public void Fly() => Console.WriteLine("Yeah, I can fly!");

    // hai phương thức này thực thi kiểu explicit, do đó
    // không thể gọi từ object của Parrot 
    // mà chỉ có thể gọi qua giao diện IPet hoặc IBird
    void IPet.Sound() => Console.WriteLine("I can speak!");
    void IBird.Sound() => Console.WriteLine("I can sing, too!");
}

Nếu phương pháp được thực thi theo kiểu explicit thì không được phép sử dụng từ khóa điều khiển và tinh chỉnh truy vấn .Sự độc lạ lớn nhất giữa implicit và explicit bộc lộ ở việc sử dụng object của class .

Sử dụng interface trong C #

Kiểu interface

Interface hoàn toàn có thể sử dụng như một kiểu tài liệu để khai báo biến. Biến của interface được cho phép gọi những thành viên của interface giống như một object thông thường của class .

  • BirdLover
  • PetLover
internal class BirdLover
{
    private IBird _bird;
    public BirdLover(IBird bird) => _bird = bird;
    public void Play()
    {
        // _bird có thể gọi đủ các phương thức của IBird
        Console.Write("Fly ...");
        _bird.Fly();
        Console.Write("Say something ...");
        _bird.Sound();
        Console.Write("What do you like to eat? ");
        _bird.Feed();
    }
}

Trong hai class BirdLover và PetLover tất cả chúng ta sử dụng hai biến _bird và _pet giống như một object thông thường .Tuy nhiên, biến của interface bắt buộc phải tham chiếu tới một object thực sự. Như trong hai lớp trên, object của class được truyền qua tham số của hàm tạo. Nếu không cho biến của interface tham chiếu tới một object thực sự, khi chạy chương trình sẽ gặp lỗi ‘ Object reference not set to an instance of an object ’ ở những lời gọi hàm hoặc truy xuất thành viên .Ví dụ, lệnh sau sẽ báo lỗi khi chạy :

PetLover petLover2 = new PetLover();
petLover2.Play(); 

Ở đây tất cả chúng ta sử dụng constructor không tham số của lớp PetLover ( nghĩa là không truyền object nào để gán cho biến _pet. Chương trình sẽ báo lỗi ở lời gọi _pet. Feed ( ) vì _pet không hề tham chiếu tới một object nào .

Khởi tạo object

Interface hoàn toàn có thể dùng để khai báo biến ( như ở trên ) nhưng không hề tự khởi tạo object. Biến kiểu interface chỉ hoàn toàn có thể tham chiếu tới object của class thực thi interface đó .

IPet pet = new Dog();
PetLover petLover = new PetLover(pet);            
petLover.Play();

petLover = new PetLover(new Parrot());
petLover.Play();

BirdLover birdLover = new BirdLover(new Parrot());
birdLover.Play();

Nói một cách khác, tất cả chúng ta cần sử dụng một class đơn cử thực thi interface để khởi tạo object rồi gán object đó cho biến interface .Đối với những class thực thi interface nhờ vào vào cách thực thi ( explicit hay implicit ), có sự độc lạ khi sử dụng object của những class này :

Cat cat = new Cat();
// cat có thể gọi được Feed và Sound
cat.Feed(); cat.Sound();

IPet cat2 = new Cat();
// cat2 có thể gọi Feed và Sound
cat2.Feed(); cat2.Sound();

Parrot parrot = new Parrot();
// (gọi qua object) parrot chỉ gọi được Feed và Fly, không gọi được Sound
parrot.Feed(); parrot.Fly();

IBird parrot2 = new Parrot();
// (gọi qua giao diện) parrot2 gọi được đủ 3 phương thức của IBird 
parrot2.Feed(); parrot2.Fly(); parrot2.Sound();

// dog không gọi được phương thức nào (gọi qua object) do
// cả hai phương thức của Dog đều thực hiện kiểu explicit
Dog dog = new Dog();

IPet dog2 = new Dog();
// gọi qua giao diện: dog2 gọi được cả Feed và Sound
dog2.Feed(); dog2.Sound();

Việc thực thi implicit tạo cho object của class năng lực sử dụng những phương pháp như class thông thường :

Cat cat = new Cat();
// cat có thể gọi được Feed và Sound
cat.Feed(); cat.Sound();

Những phương pháp nào được thực thi kiểu explicit thì không hề gọi được trên object :

Parrot parrot = new Parrot();
// (gọi qua object) parrot chỉ gọi được Feed và Fly, không gọi được Sound
parrot.Feed(); parrot.Fly();
// dog không gọi được phương thức nào (gọi qua object) do
// cả hai phương thức của Dog đều thực hiện kiểu explicit
Dog dog = new Dog();

Vì có sự độc lạ giữa hai cách sử dụng object ( parrot và parrot2 )

Parrot parrot = new Parrot(); parrot.Feed(); parrot.Fly()
IBird parrot2 = new Parrot(); parrot2.Feed(); parrot2.Fly(); parrot2.Sound();

Chúng ta gọi cách sử dụng thứ nhất là “ gọi qua object ”, cách sử dụng thứ hai gọi là “ gọi qua interface ” .

Trong ví dụ trên, sự phụ thuộc giữa các class được thể hiện qua sơ đồ code sau.

Sơ đồ code của ví dụSơ đồ code của ví dụ

Kết luận

Trong bài này tất cả chúng ta đã xem xét một kỹ thuật đặc biệt quan trọng quan trọng trong tăng trưởng ứng dụng : sử dụng interface. Chúng ta cũng thấy, interface được cho phép giảm bớt sự phụ thuộc vào giữa những class và tạo ra quan hệ lỏng giữa chúng. Quan hệ này được cho phép những class hoàn toàn có thể được kiến thiết xây dựng và test độc lập .

+ Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
+ Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
+ Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
Cảm ơn bạn!

5/5 - (1 vote)

Bài viết liên quan

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments