Bài viết sẽ làm rõ khái niệm interface, các đặc điểm quan trọng và các trường hợp thường sử dụng interface. Với những ví dụ đơn giản, bài viết rất thích hợp với những lập trình viên mới tiếp cận và làm việc với các ngôn ngữ cấp cao C#, Java,..
OOP C# Huỳnh Minh Tân 2017-06-14 14:53:54

Giới thiệu

Thông thường những lập trình viên mới tiếp cận và làm việc với các ngôn ngữ cấp cao như Java, JavaScript hay C#, v...v... sẽ lúng túng trong việc hiểu rõ về khái niệm interface và không thể áp dụng một cách linh hoạt.

Bài viết này tập trung giải quyết interface là gì? Sử dụng interface như thế nào.

Tôi sẽ hiện thực interface bằng ngôn ngữ C#, các ngôn ngữ khác tương tự.

Tiền đề bài viết

Trong quá trình tự học C#, tôi đã gặp nhiều khó khăn để tiếp cận vấn đề một cách thấu đáo, chính vì thế tôi luôn cố gắn tìm ra đáp án giải quyết vấn đề của chính bản thân. Nhưng đối với bài viết này thì đặc biệt hơn, tôi đã nhận được một lời động viên từ một người bạn và một người thầy, điều này thúc đẩy tôi càng học tập, càng làm việc và đóng góp cho STDIO nhiều hơn nữa.

Đối tượng hướng đến

Yêu cầu phải có kiến thức về lập trình hướng đối tượng OOP.

Interface trong C# là gì?

Interface được xem như là một lớp, lớp đó có thể được một class hoặc struct khác implement nó.

Một interface có các đặc điểm sau:

  • Interface có các thành viên là methods, properties, indexers, events và không chứa field.
interface IShape
{
	// methods
	void Draw();

	// properties
	double Side { get; set; }

	// indexers
	double this[int index] { get; set; }

	// event
	event EventHandler SideChanged;

	// field
	double m_side; // lỗi:  error CS0525: Interfaces cannot contain fields
}
  • Một interface có thể implement nhiều interface cơ sở (base interface), một class hoặc struct có thể implement nhiều interface.
interface IPath
{
 	void setSize();
}
interface IColor
{
	void setBorderColor();
	void setFillColor();
}
interface IPoint
{
	void setPoint();
}

// interface có thể implement nhiều interface 
interface IShape : IColor, IPath
{
	void Draw();
	double Side { get; set; }
}

// class hoặc struct có thể implement nhiều interface
class CSquare : IShape, IPoint
{
	private double m_side;
	public void setSize() { }
	public void setPoint() { }
	public void setBorderColor() { }
	public void setFillColor() { }
	public void Draw() { }
	public double Side {
		get { return m_side; }
		set { m_side = value; }
	}
}
  • Bất kỳ class hay struct nào implement một interface thì implement tất cả các thành viên và định nghĩa đầy đủ các thành viên của interface đó.
interface IColor
{
	void setBorderColor();
	void setFillColor();
}

class CRectangle : IColor
{
	public void setBorderColor(){ }
	// lỗi: error CS0535: 'CRectangle' does not implement interface member 'IColor.setFillColor()'
	// thiếu phương thức: setFillColor()
}
  • Các thành viên của interface không được phép định nghĩa (definition) mà chỉ được khai báo (declaration).
interface IShape
{
	double Side { get; set; }
	void Draw()
	{
		Console.WriteLine("drawing drawing.");
	}
	// lỗi:  error CS0531: 'IShape.Draw()': interface members cannot have a definition
}
  • Interface không có constuctors.
interface IPath
{
	void setSize();
	public IPath()
	{
		Console.WriteLine("Size of path.");
	}
	// lỗi: error CS0526: Interfaces cannot contain constructors
}
  • Nếu một lớp implement từ nhiều interface có cùng tên thành viên thì trong lớp phải chỉ rõ thành viên đó thuộc interface nào (explicit interface).
interface IPath
{
	void setSize();
}

interface IBorder
{
	void setSize();
}

class CCircle : IPath, IBorder
{
	private int m_size;

	// bỏ modifier public
	// nếu có sẽ gây lỗi: error CS0106: The modifier 'public' is not valid for this item
	// có lỗi vì khi ta sử dụng kiểu định nghĩa đầy đủ <kiểu-trả-về><tên-interface>.<phương-thức> thì compiler sẽ không cần khai báo từ khóa public

	// explicit interface 
	void IPath.setSize()
	{
		 m_size = 3;
		Console.WriteLine("path: {0}", m_size);
	}

	// explicit interface
	void IBorder.setSize()
	{
		m_size = 5;
		Console.WriteLine("border: {0}", m_size);
	}
}

class Program
{
	static void Main(string[] args)
	{
		// Khai báo một thể hiện của lớp CCricle
		CCircle circle = new CCircle();

		// Khai báo một thể hiện của interface IPath 
		IPath path = (IPath)circle;

		// Khai báo một thể hiện của interface IBorder
		IBorder border = (IBorder)circle;

		/*
		circle.setSize();
		*/
		// đoạn code trên gây ra lỗi vì không thể truy cập thành viên explicit interface từ một thể hiện của lớp.

		// phương thức được gọi thành công từ một thể hiện interface.
		path.setSize();
		border.setSize();

		Console.ReadKey();
	}
}

Output:

path: 3
border: 5

Implement và inherits trong C#

Trong phạm vi bài viết này tôi không tập trung vào chi tiết bản chất của hai khái niệm, chúng ta có thể hiểu một cách đơn giản:

  • Implement một interface
class derivedClass : IBaseInterface
  • Inherits một class
class derivedClass : public CBaseClass

Cú pháp định nghĩa interface trong C# 

Khai báo sử dụng từ khóa interface. Interface có modifier là public hoặc internal, nếu không ghi rõ mặc định là internal. 

[modifier] interface <interface-name> [: interface-base] {
        // interface member
 }

Ví dụ: định nghĩa một interface thể hiện đối tượng cảnh trong game engine.

interface IScence
{
	string Title { get; set; }
	void process();
}

class CMainScence : IScence
{
	private string m_title;
	public CMainScence()
	{
		m_title = ":: Game Tank :: menu";
	}

	public string Title
	{
		get { return m_title; }
		set { m_title = value; }
	}

	 public void process()
	{
		Console.WriteLine("process game");
	}
}

class Program
{
	static void Main(string[] args)
	{
		CMainScence mainScence = new CMainScence();
		mainScence.process();
		Console.WriteLine("{0}", mainScence.Title);
	}
}

Output:

Process game
:: Game Tank :: menu

Sử dụng interface trong C#

Thể hiện tính đa kế thừa, tính đa hình

Trong kỹ thuật lập trình hướng đối tượng ở C++ nếu chúng ta muốn kế thừa một lớp từ nhiều lớp (multiple-inheritance) thì ta có thể hiện thực một cách dễ dàng.

class derivedClass : public baseClassA, public baseClassB, public baseClassC {
	// code something
}

Nếu sử dụng C#, ta làm tương tự là điều không thể, đa kế thừa không được hỗ trợ. Vì vậy, ta sử dụng interface để hiện thực đa kế thừa (multiple-implementations).

interface IShape
{
	double Area();
	double Height { get; set; }
	double Width { get; set; }
}

interface IShapeDisplay
{
      	void Display();
}

// lớp CSquare kế thừa từ 2 interface IShape và IShapeDisplay
public class CSquare : IShape, IShapeDisplay
{
	private double m_size;
	public double Height
	{
		get { return m_size; }
		set { m_size = value; }
	}

	public double Width	
	{
		get { return m_size; }
		set { m_size = value; }
	}

	public double Area()
	{
		return m_size * m_size;
	}
	
	public void Display()
	{
		Console.WriteLine("SQUARE-Size: {0}", this.m_size);
		Console.WriteLine("SQUARE-Area: {0}", this.Area());
	}
	
	public CSquare()
	{
		m_size = 7;
	}
}

// lớp CRectangle kế thừa từ 2 interface IShape và IShapeDisplay
public class CRectangle : IShape, IShapeDisplay
{
	private double m_width;
	private double m_height;

	public double Height
	{
		get { return m_height; }
		set { m_height = value; }
	}

	public double Width
	{
		get { return m_width; }
		set { m_width = value; }
	}

	public double Area()
	{
		return m_width * m_height;
	}

	public void Display()
	{
		Console.WriteLine("REC-Width: {0}", this.m_width);
		Console.WriteLine("REC-Height: {0}", this.m_height);
		Console.WriteLine("REC-Area: {0}", this.Area());
	}

	public CRectangle()
	{
		m_width = 7;
		m_height = 3;
	}
}

class Program
{
	static void Main(string[] args)
	{
		CRectangle rec = new CRectangle();
		rec.Height = 8;
		rec.Display();

		// polymorphism
		Console.WriteLine("-- polymorphism --");
		IShapeDisplay[] shapeDisplay = { new CRectangle(), new CSquare() };
		for (int i = 0; i < 2; i++)
		{
			shapeDisplay[i].Display();
		}
	}
}

Output:

REC-Width: 7
REC-Height: 8
REC-Area: 56
-- polymorphism --
REC-Width: 7
REC-Height: 3
REC-Area: 21
SQUARE-Size: 7
SQUARE-Area: 49

Sử dụng interface chúng ta có thể gọi phương thức giống nhau từ các lớp khác nhau, các lớp đó phải cùng implement một interface (polymorphism).

Quy định kiến trúc

Khi một lớp implement một interface thì phải định nghĩa đầy đủ các phương thức của interface đó, vì thế khi bạn muốn tạo ra các lớp thực hiện một số nhiệm vụ như nhau thì interface sẽ là lựa chọn tốt nhất trong trường hợp này. 
-> Interface được dùng để qui định kiến trúc cho lớp nào implement interface đó.

Ví dụ: Xây dựng chương trình mã hóa các tập tin png, mp3. Chương trình bao gồm nhiều công đoạn: mở tập tin, xử lý mã hóa, nén tập tin, lưu tập tin. Tôi chỉ hiện thực công đoạn mở tập tin các cộng đoạn khác hiện thực tương tự.

Việc mở tập tin gồm các bước: load lên bộ nhớ, giải nén, giải mã, đóng tập tin. Các bước này đều rất cần thiết, mỗi lớp sẽ định nghĩa các phương thức khác nhau tương ứng cho từng lớp và để tránh sai sót thiếu một phương thức nào đó khi lập trình ta sẽ định nghĩa một interface để tạo kiến trúc áp dụng cho các lớp mã hóa png, mp3.

interface IReadFile
{
    bool OpenFile();
    void DecryptionFile();
    void DecompressionFile();
    void ClosedFile();
}

class CEnCryptionPng : IReadFile
{
	#region Open and entry file
	public bool OpenFile()
	{
		// đoạn code open file cho file png 
		return true;
	}
	public void DecryptionFile()
	{
		// đoạn code decryption cho file png 
	}
	public void DecompressionFile()
	{
		// đoạn code decompression cho file png
	}
	public void ClosedFile()
	{
	}
	#endregion
 
	#region Process Encryption
	#endregion
 
	#region Process Compression
	#endregion
 
	#region Dispose
	#endregion
}
 
class CEnCryptionMp3 : IReadFile
{
	#region Open and entry file
	public bool OpenFile()
	{
		// đoạn code open file cho file mp3 
		return true;
	}
	public void DecryptionFile()
	{
		// đoạn code decryption cho file mp3 
	}
	public void DecompressionFile()
	{
		// đoạn code decompression cho file mp3
	}
	public void ClosedFile()
	{
	}
	#endregion
 
	#region Process Encryption
	#endregion
	
	#region Process Compression
	#endregion
 
	#region Dispose
	#endregion
}

Việc xây dựng kiến trúc giữa các lớp rất quan trọng điều đó giúp cho chương trình thêm tính chặt chẽ, có logic và giúp cho lập trình viên dễ kiểm soát khi viết code.

Lời kết

Bài viết này cho biết interface là gì, các đặc điểm quan trọng và một số trường hợp sử dụng interface. Từ đó các bạn có thể áp dụng một cách hiệu quả, nó sẽ giúp cho chương trình có hệ thống, cấu trúc rõ ràng, dễ quản lý và bảo trì.