Bài tập 2 (B10_C#):Xây dựng Interface cho hệ thống

SƠ ĐỒ TỔNG QUÁT CHO BÀI TẬP

┌─────────────────────────────────────────────────────────────┐
│              INTERFACES (Hợp đồng)                         │
├─────────────────────────────────────────────────────────────┤
│  IPrintable                IStorable              ICalculable │
│  ├── Print()               ├── Serialize()        ├── Calculate() │
│  └── GetSummary()          └── Deserialize()                 │
└─────────────────────────────────────────────────────────────┘
                           │
                           │ Implement (Triển khai)
                           ▼
┌─────────────────────────────────────────────────────────────┐
│             NHÂN VIÊN (Abstract Class)                      │
├─────────────────────────────────────────────────────────────┤
│  Kế thừa:                                                   │
│  ├── IPrintable  → Print(), GetSummary()                   │
│  ├── IStorable   → Serialize(), Deserialize()              │
│  └── ICalculable → Calculate()  (Tính lương)               │
└─────────────────────────────────────────────────────────────┘
           │                    │                    │
           ▼                    ▼                    ▼
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ NhanVienVanPhong  │ │ NhanVienSanXuat   │ │ NhanVienQuanLy    │
│ (Lớp con 1)       │ │ (Lớp con 2)       │ │ (Lớp con 3)       │
└───────────────────┘ └───────────────────┘ └───────────────────┘

BƯỚC 1: TẠO PROJECT MỚI

1.1. Mở Visual Studio

text
File → New → Project

Hoặc nhấn: Ctrl + Shift + N

1.2. Chọn loại Project

  • Chọn Console App (.NET Core)

  • Nhấn Next

1.3. Đặt tên Project

Project name: Bai2_Interface
Location: D:\CSharp\LapTrinhHuongDoiTuong
Solution: LapTrinhHuongDoiTuong (Add to Solution)
  • Nhấn Next

1.4. Chọn Framework

  • Chọn .NET 6.0 hoặc .NET 8.0

  • Nhấn Create

BƯỚC 2: TẠO CẤU TRÚC THƯ MỤC

2.1. Tạo thư mục

Bai2_Interface (Project)
├── Interfaces/          ← Chứa các Interface
│   ├── IPrintable.cs
│   ├── IStorable.cs
│   └── ICalculable.cs
├── Models/              ← Chứa các lớp
│   ├── NhanVien.cs
│   ├── NhanVienVanPhong.cs
│   ├── NhanVienSanXuat.cs
│   └── NhanVienQuanLy.cs
└── Program.cs           ← Chạy test

2.2. Tạo thư mục

  1. Click chuột phải vào Project → Add → New Folder → Đặt tên Interfaces

  2. Click chuột phải vào Project → Add → New Folder → Đặt tên Models

BƯỚC 3: XÂY DỰNG CÁC INTERFACE

3.1. Interface IPrintable.cs

  1. Click chuột phải vào thư mục Interfaces

  2. Chọn Add → Class → Đặt tên IPrintable.cs

  3. Nhấn Add

  4. Viết code:

using System;

namespace Bai2_Interface.Interfaces
{
    /// <summary>
    /// Interface IPrintable - Định nghĩa hành vi in ấn
    /// Interface này quy định: Lớp nào implement phải có khả năng in ấn
    /// </summary>
    public interface IPrintable
    {
        /// <summary>
        /// Phương thức Print() - In thông tin ra màn hình
        /// Không có body (không triển khai)
        /// Lớp implement phải tự triển khai
        /// </summary>
        void Print();
        
        /// <summary>
        /// Phương thức GetSummary() - Trả về chuỗi tóm tắt
        /// Không có body
        /// </summary>
        string GetSummary();
    }
}

.2. Interface IStorable.cs

  1. Click chuột phải vào thư mục Interfaces

  2. Chọn Add → Class → Đặt tên IStorable.cs

  3. Nhấn Add

  4. Viết code:

csharp
using System;

namespace Bai2_Interface.Interfaces
{
    /// <summary>
    /// Interface IStorable - Định nghĩa hành vi lưu trữ
    /// Interface này quy định: Lớp nào implement phải có khả năng serialize/deserialize
    /// </summary>
    public interface IStorable
    {
        /// <summary>
        /// Serialize() - Chuyển đối tượng thành chuỗi để lưu trữ
        /// </summary>
        string Serialize();
        
        /// <summary>
        /// Deserialize() - Chuyển chuỗi thành đối tượng
        /// </summary>
        /// <param name="data">Chuỗi dữ liệu cần chuyển đổi</param>
        void Deserialize(string data);
    }
}

3.3. Interface ICalculable.cs

  1. Click chuột phải vào thư mục Interfaces

  2. Chọn Add → Class → Đặt tên ICalculable.cs

  3. Nhấn Add

  4. Viết code:

csharp
using System;

namespace Bai2_Interface.Interfaces
{
    /// <summary>
    /// Interface ICalculable - Định nghĩa hành vi tính toán
    /// Interface này quy định: Lớp nào implement phải có khả năng tính toán
    /// </summary>
    public interface ICalculable
    {
        /// <summary>
        /// Calculate() - Thực hiện tính toán (ví dụ: tính lương)
        /// </summary>
        double Calculate();
    }
}

BƯỚC 4: XÂY DỰNG CÁC LỚP IMPLEMENT INTERFACE

4.1. Lớp NhanVien.cs (Abstract Class)

  1. Click chuột phải vào thư mục Models

  2. Chọn Add → Class → Đặt tên NhanVien.cs

  3. Nhấn Add

  4. Viết code:

using System;
using Bai2_Interface.Interfaces;

namespace Bai2_Interface.Models
{
    /// <summary>
    /// Lớp trừu tượng NhanVien
    /// Implement 3 Interface: IPrintable, IStorable, ICalculable
    /// </summary>
    public abstract class NhanVien : IPrintable, IStorable, ICalculable
    {
        // === FIELDS ===
        private string _maNV;
        private string _hoTen;
        private DateTime _ngaySinh;
        private double _luongCoBan;

        // === PROPERTIES ===
        public string MaNV
        {
            get { return _maNV; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                    throw new ArgumentException("Mã NV không được để trống!");
                _maNV = value.Trim();
            }
        }

        public string HoTen
        {
            get { return _hoTen; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                    throw new ArgumentException("Họ tên không được để trống!");
                _hoTen = value.Trim();
            }
        }

        public DateTime NgaySinh
        {
            get { return _ngaySinh; }
            set
            {
                if (value > DateTime.Now)
                    throw new ArgumentException("Ngày sinh không được trong tương lai!");
                if (value < new DateTime(1900, 1, 1))
                    throw new ArgumentException("Ngày sinh không hợp lệ!");
                _ngaySinh = value;
            }
        }

        public double LuongCoBan
        {
            get { return _luongCoBan; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("Lương cơ bản không được âm!");
                _luongCoBan = value;
            }
        }

        // === CONSTRUCTORS ===
        public NhanVien()
        {
            _maNV = "NV001";
            _hoTen = "Chưa xác định";
            _ngaySinh = new DateTime(2000, 1, 1);
            _luongCoBan = 0;
        }

        public NhanVien(string ma, string ten, DateTime ngaySinh, double luong)
        {
            MaNV = ma;
            HoTen = ten;
            NgaySinh = ngaySinh;
            LuongCoBan = luong;
        }

        // === ABSTRACT METHOD ===
        public abstract double Calculate();  // Implement ICalculable

        // === VIRTUAL METHOD ===
        public virtual void HienThi()
        {
            Console.WriteLine("═══════════════════════════════════════");
            Console.WriteLine($"  Mã NV       : {_maNV}");
            Console.WriteLine($"  Họ tên      : {_hoTen}");
            Console.WriteLine($"  Ngày sinh   : {_ngaySinh:dd/MM/yyyy}");
            Console.WriteLine($"  Lương CB    : {_luongCoBan:N0} VND");
            Console.WriteLine($"  Lương TT    : {Calculate():N0} VND");
            Console.WriteLine("═══════════════════════════════════════");
        }

        // === IMPLEMENT IPrintable ===
        
        /// <summary>
        /// Implement phương thức Print() từ IPrintable
        /// In thông tin nhân viên
        /// </summary>
        public virtual void Print()
        {
            Console.WriteLine($"--- NHÂN VIÊN: {_hoTen} ---");
            HienThi();
        }

        /// <summary>
        /// Implement phương thức GetSummary() từ IPrintable
        /// Trả về chuỗi tóm tắt
        /// </summary>
        public virtual string GetSummary()
        {
            return $"{_maNV}|{_hoTen}|{Calculate():N0}";
        }

        // === IMPLEMENT IStorable ===
        
        /// <summary>
        /// Implement phương thức Serialize() từ IStorable
        /// Chuyển đối tượng thành chuỗi để lưu
        /// </summary>
        public virtual string Serialize()
        {
            // Định dạng: TênLớp|Mã|Tên|NgàySinh|LươngCB
            return $"{GetType().Name}|{_maNV}|{_hoTen}|{_ngaySinh:yyyy-MM-dd}|{_luongCoBan}";
        }

        /// <summary>
        /// Implement phương thức Deserialize() từ IStorable
        /// Chuyển chuỗi thành đối tượng
        /// </summary>
        public virtual void Deserialize(string data)
        {
            // Tách chuỗi thành các phần
            string[] parts = data.Split('|');
            if (parts.Length >= 5)
            {
                MaNV = parts[1];                    // Mã NV
                HoTen = parts[2];                   // Họ tên
                NgaySinh = DateTime.Parse(parts[3]); // Ngày sinh
                LuongCoBan = double.Parse(parts[4]); // Lương CB
            }
        }
    }
}

4.2. Lớp NhanVienVanPhong.cs

using System;
using Bai2_Interface.Interfaces;

namespace Bai2_Interface.Models
{
    /// <summary>
    /// Lớp NhanVienVanPhong kế thừa từ NhanVien
    /// Tự động implement các Interface từ lớp cha
    /// </summary>
    public class NhanVienVanPhong : NhanVien
    {
        // === FIELD RIÊNG ===
        private int _soNgayLamViec;

        // === PROPERTY RIÊNG ===
        public int SoNgayLamViec
        {
            get { return _soNgayLamViec; }
            set
            {
                if (value < 0 || value > 31)
                    throw new ArgumentException("Số ngày làm phải từ 0 đến 31!");
                _soNgayLamViec = value;
            }
        }

        // === CONSTRUCTORS ===
        public NhanVienVanPhong() : base()
        {
            _soNgayLamViec = 0;
        }

        public NhanVienVanPhong(string ma, string ten, DateTime ngaySinh, 
                               double luong, int soNgay)
            : base(ma, ten, ngaySinh, luong)
        {
            SoNgayLamViec = soNgay;
        }

        // === IMPLEMENT ABSTRACT Calculate() ===
        
        /// <summary>
        /// Tính lương nhân viên văn phòng
        /// Lương = Lương CB + Số ngày làm × 200,000
        /// </summary>
        public override double Calculate()
        {
            return LuongCoBan + (_soNgayLamViec * 200000);
        }

        // === GHI ĐÈ PHƯƠNG THỨC HIỂN THỊ ===
        public override void HienThi()
        {
            base.HienThi();
            Console.WriteLine($"  Loại       : Nhân viên văn phòng");
            Console.WriteLine($"  Số ngày làm: {_soNgayLamViec}");
            Console.WriteLine("═══════════════════════════════════════");
        }

        // === GHI ĐÈ CÁC PHƯƠNG THỨC TỪ INTERFACE ===
        public override string GetSummary()
        {
            return $"{base.GetSummary()}|VP|{_soNgayLamViec}";
        }

        public override string Serialize()
        {
            return $"{base.Serialize()}|{_soNgayLamViec}";
        }

        public override void Deserialize(string data)
        {
            base.Deserialize(data);
            string[] parts = data.Split('|');
            if (parts.Length >= 6)
            {
                SoNgayLamViec = int.Parse(parts[5]);
            }
        }
    }
}

4.3. Lớp NhanVienSanXuat.cs

using System;
using Bai2_Interface.Interfaces;

namespace Bai2_Interface.Models
{
    /// <summary>
    /// Lớp NhanVienSanXuat kế thừa từ NhanVien
    /// </summary>
    public class NhanVienSanXuat : NhanVien
    {
        // === FIELDS RIÊNG ===
        private int _soSanPham;
        private double _donGiaSanPham;

        // === PROPERTIES RIÊNG ===
        public int SoSanPham
        {
            get { return _soSanPham; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("Số sản phẩm không được âm!");
                _soSanPham = value;
            }
        }

        public double DonGiaSanPham
        {
            get { return _donGiaSanPham; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("Đơn giá không được âm!");
                _donGiaSanPham = value;
            }
        }

        // === CONSTRUCTORS ===
        public NhanVienSanXuat() : base()
        {
            _soSanPham = 0;
            _donGiaSanPham = 50000;
        }

        public NhanVienSanXuat(string ma, string ten, DateTime ngaySinh,
                              double luong, int soSanPham, double donGia = 50000)
            : base(ma, ten, ngaySinh, luong)
        {
            SoSanPham = soSanPham;
            DonGiaSanPham = donGia;
        }

        // === IMPLEMENT ABSTRACT Calculate() ===
        
        /// <summary>
        /// Tính lương nhân viên sản xuất
        /// Lương = Lương CB + Số sản phẩm × Đơn giá
        /// </summary>
        public override double Calculate()
        {
            return LuongCoBan + (_soSanPham * _donGiaSanPham);
        }

        public override void HienThi()
        {
            base.HienThi();
            Console.WriteLine($"  Loại       : Nhân viên sản xuất");
            Console.WriteLine($"  Số SP      : {_soSanPham}");
            Console.WriteLine($"  Đơn giá SP : {_donGiaSanPham:N0} VND");
            Console.WriteLine("═══════════════════════════════════════");
        }

        public override string GetSummary()
        {
            return $"{base.GetSummary()}|SX|{_soSanPham}|{_donGiaSanPham}";
        }

        public override string Serialize()
        {
            return $"{base.Serialize()}|{_soSanPham}|{_donGiaSanPham}";
        }

        public override void Deserialize(string data)
        {
            base.Deserialize(data);
            string[] parts = data.Split('|');
            if (parts.Length >= 7)
            {
                SoSanPham = int.Parse(parts[5]);
                DonGiaSanPham = double.Parse(parts[6]);
            }
        }
    }
}

4.4. Lớp NhanVienQuanLy.cs

using System;
using Bai2_Interface.Interfaces;

namespace Bai2_Interface.Models
{
    /// <summary>
    /// Lớp NhanVienQuanLy kế thừa từ NhanVien
    /// </summary>
    public class NhanVienQuanLy : NhanVien
    {
        // === FIELDS RIÊNG ===
        private double _thuong;
        private int _soNhanVienQuanLy;

        // === PROPERTIES RIÊNG ===
        public double Thuong
        {
            get { return _thuong; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("Thưởng không được âm!");
                _thuong = value;
            }
        }

        public int SoNhanVienQuanLy
        {
            get { return _soNhanVienQuanLy; }
            set
            {
                if (value < 0)
                    throw new ArgumentException("Số NV quản lý không được âm!");
                _soNhanVienQuanLy = value;
            }
        }

        // === CONSTRUCTORS ===
        public NhanVienQuanLy() : base()
        {
            _thuong = 0;
            _soNhanVienQuanLy = 0;
        }

        public NhanVienQuanLy(string ma, string ten, DateTime ngaySinh,
                             double luong, double thuong, int soNV)
            : base(ma, ten, ngaySinh, luong)
        {
            Thuong = thuong;
            SoNhanVienQuanLy = soNV;
        }

        // === IMPLEMENT ABSTRACT Calculate() ===
        
        /// <summary>
        /// Tính lương nhân viên quản lý
        /// Lương = Lương CB × 2 + Thưởng
        /// </summary>
        public override double Calculate()
        {
            return LuongCoBan * 2 + _thuong;
        }

        public override void HienThi()
        {
            base.HienThi();
            Console.WriteLine($"  Loại       : Nhân viên quản lý");
            Console.WriteLine($"  Thưởng     : {_thuong:N0} VND");
            Console.WriteLine($"  Số NV QL   : {_soNhanVienQuanLy}");
            Console.WriteLine("═══════════════════════════════════════");
        }

        public override string GetSummary()
        {
            return $"{base.GetSummary()}|QL|{_thuong}|{_soNhanVienQuanLy}";
        }

        public override string Serialize()
        {
            return $"{base.Serialize()}|{_thuong}|{_soNhanVienQuanLy}";
        }

        public override void Deserialize(string data)
        {
            base.Deserialize(data);
            string[] parts = data.Split('|');
            if (parts.Length >= 7)
            {
                Thuong = double.Parse(parts[5]);
                SoNhanVienQuanLy = int.Parse(parts[6]);
            }
        }
    }
}

BƯỚC 5: CODE TEST (Program.cs)

using System;
using System.Collections.Generic;
using Bai2_Interface.Interfaces;
using Bai2_Interface.Models;

namespace Bai2_Interface
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;

            Console.WriteLine("═══════════════════════════════════════════════════");
            Console.WriteLine("     BÀI TẬP 2: XÂY DỰNG INTERFACE CHO HỆ THỐNG");
            Console.WriteLine("═══════════════════════════════════════════════════\n");

            // ================================================================
            // PHẦN 1: TẠO DANH SÁCH NHÂN VIÊN
            // ================================================================

            List<NhanVien> danhSachNV = new List<NhanVien>();

            danhSachNV.Add(new NhanVienVanPhong("NV001", "Nguyễn Văn A",
                new DateTime(1990, 5, 10), 8000000, 22));

            danhSachNV.Add(new NhanVienSanXuat("NV002", "Trần Thị B",
                new DateTime(1995, 8, 15), 6000000, 150));

            danhSachNV.Add(new NhanVienQuanLy("NV003", "Lê Văn C",
                new DateTime(1985, 12, 20), 12000000, 5000000, 10));

            Console.WriteLine("▶ DANH SÁCH NHÂN VIÊN (ĐA HÌNH):");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            // ================================================================
            // PHẦN 2: DEMO INTERFACE IPrintable
            // ================================================================

            Console.WriteLine("1. DEMO INTERFACE IPRINTABLE:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            foreach (NhanVien nv in danhSachNV)
            {
                nv.Print();  // Gọi phương thức từ IPrintable
                Console.WriteLine($"   Tóm tắt: {nv.GetSummary()}");
                Console.WriteLine();
            }

            // ================================================================
            // PHẦN 3: DEMO INTERFACE IStorable
            // ================================================================

            Console.WriteLine("2. DEMO INTERFACE ISTORABLE:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            foreach (NhanVien nv in danhSachNV)
            {
                string serialized = nv.Serialize();
                Console.WriteLine($"   Serialize: {serialized}");

                // Tạo đối tượng mới và Deserialize
                NhanVien nvMoi = new NhanVienVanPhong();  // Tạm thời
                nvMoi.Deserialize(serialized);
                Console.WriteLine($"   Deserialize: {nvMoi.GetSummary()}");
                Console.WriteLine();
            }

            // ================================================================
            // PHẦN 4: DEMO INTERFACE ICalculable
            // ================================================================

            Console.WriteLine("3. DEMO INTERFACE ICALCULABLE:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            Console.WriteLine("   BẢNG TÍNH LƯƠNG NHÂN VIÊN:");
            Console.WriteLine("   ───────────────────────────────────────────────");
            Console.WriteLine($"   {"Mã NV",-10} {"Họ tên",-20} {"Lương TT",15}");
            Console.WriteLine("   ───────────────────────────────────────────────");

            foreach (NhanVien nv in danhSachNV)
            {
                // Gọi Calculate() từ ICalculable
                Console.WriteLine($"   {nv.MaNV,-10} {nv.HoTen,-20} {nv.Calculate(),15:N0}");
            }
            Console.WriteLine("   ───────────────────────────────────────────────");

            // ================================================================
            // PHẦN 5: SỬ DỤNG INTERFACE LÀM KIỂU DỮ LIỆU
            // ================================================================

            Console.WriteLine("\n4. SỬ DỤNG INTERFACE LÀM KIỂU DỮ LIỆU:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            // Có thể tạo danh sách kiểu Interface
            List<IPrintable> printableList = new List<IPrintable>();
            foreach (NhanVien nv in danhSachNV)
            {
                printableList.Add(nv);  // NhanVien implement IPrintable
            }

            Console.WriteLine("   Danh sách IPrintable (đa hình):");
            foreach (IPrintable item in printableList)
            {
                Console.WriteLine($"   - {item.GetSummary()}");
            }

            // ================================================================
            // PHẦN 6: THỬ NHẬP DỮ LIỆU TỪ SERIALIZE
            // ================================================================

            Console.WriteLine("\n5. THỬ NHẬP DỮ LIỆU TỪ CHUỖI SERIALIZE:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            try
            {
                Console.Write("   Nhập chuỗi Serialize: ");
                string input = Console.ReadLine();

                if (!string.IsNullOrEmpty(input))
                {
                    // Tạo đối tượng từ chuỗi
                    string[] parts = input.Split('|');
                    NhanVien nvFromData = null;

                    switch (parts[0])
                    {
                        case "NhanVienVanPhong":
                            nvFromData = new NhanVienVanPhong();
                            break;
                        case "NhanVienSanXuat":
                            nvFromData = new NhanVienSanXuat();
                            break;
                        case "NhanVienQuanLy":
                            nvFromData = new NhanVienQuanLy();
                            break;
                        default:
                            Console.WriteLine("   ❌ Loại nhân viên không hợp lệ!");
                            break;
                    }

                    if (nvFromData != null)
                    {
                        nvFromData.Deserialize(input);
                        Console.WriteLine("   ✅ Đã tạo đối tượng từ dữ liệu:");
                        nvFromData.Print();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"   ❌ Lỗi: {ex.Message}");
            }

            Console.WriteLine("\n═══════════════════════════════════════════════════");
            Console.WriteLine("NHẤN PHÍM BẤT KỲ ĐỂ THOÁT...");
            Console.ReadKey();
        }
    }
}