Bài tập 1 (B10_C#): Xây dựng các lớp nhân viên

Cấu trúc
NhanVien (Abstract)
├── NhanVienVanPhong
│   └── TinhLuong() = LuongCoBan + SoNgayLam * 200000
├── NhanVienSanXuat
│   └── TinhLuong() = LuongCoBan + SoSanPham * 50000
└── NhanVienQuanLy
    └── TinhLuong() = LuongCoBan * 2 + Thuong

CẤU TRÚC PROJECT

Bai1_NhanVien (Project)
├── Models/
│   ├── NhanVien.cs          ← Lớp cha trừu tượng
│   ├── NhanVienVanPhong.cs  ← Lớp con 1
│   ├── NhanVienSanXuat.cs   ← Lớp con 2
│   └── NhanVienQuanLy.cs    ← Lớp con 3
└── Program.cs               ← Chạy test

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

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

BƯỚC 3: VIẾT CODE CÁC LỚP

BƯỚC 4: VIẾT CHƯƠNG TRÌNH TEST (Program.cs)

1. Tạo file NhanVien.cs (Lớp cha trừu tượng)

 
using System;

namespace Bai1_NhanVien.Models
{
    /// <summary>
    /// Lớp trừu tượng NhanVien - Làm lớp nền cho các lớp con
    /// abstract: Không thể tạo đối tượng từ lớp này
    /// </summary>
    public abstract class NhanVien
    {
        // === FIELDS (Trường dữ liệu) ===
        private string _maNV;          // Mã nhân viên
        private string _hoTen;         // Họ tên
        private DateTime _ngaySinh;    // Ngày sinh
        private double _luongCoBan;    // Lương cơ bản

        // === PROPERTIES (Thuộc tính) ===
        
        /// <summary>
        /// Mã nhân viên - Không được để trống
        /// </summary>
        public string MaNV
        {
            get { return _maNV; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                    throw new ArgumentException("Mã NV không được để trống!");
                _maNV = value.Trim();
            }
        }

        /// <summary>
        /// Họ tên - Không được để trống
        /// </summary>
        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();
            }
        }

        /// <summary>
        /// Ngày sinh - Hợp lệ, không trong tương lai
        /// </summary>
        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;
            }
        }

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

        // === COMPUTED PROPERTIES (Tính toán tự động) ===
        
        /// <summary>
        /// Tuổi - Tự động tính từ ngày sinh
        /// </summary>
        public int Tuoi
        {
            get
            {
                DateTime today = DateTime.Today;
                int age = today.Year - _ngaySinh.Year;
                if (_ngaySinh.Date > today.AddYears(-age))
                    age--;
                return age;
            }
        }

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

        /// <summary>
        /// Constructor có tham số
        /// </summary>
        public NhanVien(string ma, string ten, DateTime ngaySinh, double luong)
        {
            MaNV = ma;
            HoTen = ten;
            NgaySinh = ngaySinh;
            LuongCoBan = luong;
        }

        // === ABSTRACT METHOD - BẮT BUỘC LỚP CON TRIỂN KHAI ===
        
        /// <summary>
        /// Phương thức trừu tượng TinhLuong()
        /// Lớp con BẮT BUỘC phải override (ghi đè) phương thức này
        /// </summary>
        public abstract double TinhLuong();

        // === VIRTUAL METHOD - CÓ THỂ GHI ĐÈ ===
        
        /// <summary>
        /// Hiển thị thông tin nhân viên
        /// virtual: Cho phép lớp con ghi đè (override)
        /// </summary>
        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($"  Tuổi        : {Tuoi}");
            Console.WriteLine($"  Lương CB    : {_luongCoBan:N0} VND");
            Console.WriteLine($"  Lương TT    : {TinhLuong():N0} VND");
            Console.WriteLine("═══════════════════════════════════════");
        }
    }
}

2. Tạo file NhanVienVanPhong.cs (Lớp con 1)

csharp
using System;

namespace Bai1_NhanVien.Models
{
    /// <summary>
    /// Lớp NhanVienVanPhong kế thừa từ NhanVien
    /// </summary>
    public class NhanVienVanPhong : NhanVien  // : NhanVien = kế thừa
    {
        // === FIELD RIÊNG ===
        private int _soNgayLamViec;  // Số ngày làm việc trong tháng

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

        // === CONSTRUCTORS ===
        
        /// <summary>
        /// Constructor mặc định
        /// : base() gọi constructor của NhanVien
        /// </summary>
        public NhanVienVanPhong() : base()
        {
            _soNgayLamViec = 0;
        }

        /// <summary>
        /// Constructor có tham số
        /// : base(ma, ten, ngaySinh, luong) gọi constructor của NhanVien
        /// </summary>
        public NhanVienVanPhong(string ma, string ten, DateTime ngaySinh, 
                               double luong, int soNgay) 
            : base(ma, ten, ngaySinh, luong)
        {
            SoNgayLamViec = soNgay;
        }

        // === GHI ĐÈ PHƯƠNG THỨC TRỪU TƯỢNG ===
        
        /// <summary>
        /// Tính lương cho nhân viên văn phòng
        /// Lương = Lương cơ bản + (Số ngày làm việc × 200,000)
        /// override: Ghi đè phương thức abstract của lớp cha
        /// </summary>
        public override double TinhLuong()
        {
            return LuongCoBan + (_soNgayLamViec * 200000);
        }

        // === GHI ĐÈ PHƯƠNG THỨC HIỂN THỊ ===
        
        /// <summary>
        /// Hiển thị thông tin nhân viên văn phòng
        /// override: Ghi đè phương thức HienThi() của lớp cha
        /// </summary>
        public override void HienThi()
        {
            // Gọi phương thức của lớp cha để hiển thị thông tin cơ bản
            base.HienThi();
            // Thêm thông tin riêng
            Console.WriteLine($"  Loại       : Nhân viên văn phòng");
            Console.WriteLine($"  Số ngày làm: {_soNgayLamViec}");
            Console.WriteLine("═══════════════════════════════════════");
        }
    }
}

3. Tạo file NhanVienSanXuat.cs (Lớp con 2)

using System;

namespace Bai1_NhanVien.Models
{
    /// <summary>
    /// Lớp NhanVienSanXuat kế thừa từ NhanVien
    /// </summary>
    public class NhanVienSanXuat : NhanVien
    {
        // === FIELD RIÊNG ===
        private int _soSanPham;        // Số sản phẩm làm được
        private double _donGiaSanPham; // Đơn giá mỗi sản phẩm

        // === 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á sản phẩm không được âm!");
                _donGiaSanPham = value;
            }
        }

        // === CONSTRUCTORS ===
        public NhanVienSanXuat() : base()
        {
            _soSanPham = 0;
            _donGiaSanPham = 50000;  // Mặc định 50,000 VND/sản phẩm
        }

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

        // === GHI ĐÈ ===
        
        /// <summary>
        /// Tính lương nhân viên sản xuất
        /// Lương = Lương cơ bản + (Số sản phẩm × Đơn giá)
        /// </summary>
        public override double TinhLuong()
        {
            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("═══════════════════════════════════════");
        }
    }
}

4. Tạo file NhanVienQuanLy.cs (Lớp con 3)

using System;

namespace Bai1_NhanVien.Models
{
    /// <summary>
    /// Lớp NhanVienQuanLy kế thừa từ NhanVien
    /// </summary>
    public class NhanVienQuanLy : NhanVien
    {
        // === FIELD RIÊNG ===
        private double _thuong;           // Tiền thưởng
        private int _soNhanVienQuanLy;    // Số nhân viên quản lý

        // === 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ố nhân viên 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 soNhanVien)
            : base(ma, ten, ngaySinh, luong)
        {
            Thuong = thuong;
            SoNhanVienQuanLy = soNhanVien;
        }

        // === GHI ĐÈ ===
        
        /// <summary>
        /// Tính lương nhân viên quản lý
        /// Lương = Lương cơ bản × 2 + Thưởng
        /// </summary>
        public override double TinhLuong()
        {
            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("═══════════════════════════════════════");
        }
    }
}

Program.cs

using System;
using System.Collections.Generic;
using Bai1_NhanVien.Models;

namespace Bai1_NhanVien
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            
            Console.WriteLine("═══════════════════════════════════════════════════");
            Console.WriteLine("     BÀI TẬP 1: XÂY DỰNG CÁC LỚP NHÂN VIÊN");
            Console.WriteLine("═══════════════════════════════════════════════════\n");

            // ================================================================
            // PHẦN 1: TẠO DANH SÁCH NHÂN VIÊN (ĐA HÌNH)
            // ================================================================

            /// <summary>
            /// List<NhanVien> có thể chứa cả 3 loại nhân viên
            /// Đây là tính đa hình (Polymorphism)
            /// </summary>
            List<NhanVien> danhSachNV = new List<NhanVien>();

            // Tạo nhân viên văn phòng
            danhSachNV.Add(new NhanVienVanPhong(
                "NV001", 
                "Nguyễn Văn A", 
                new DateTime(1990, 5, 10), 
                8000000, 
                22
            ));

            // Tạo nhân viên sản xuất
            danhSachNV.Add(new NhanVienSanXuat(
                "NV002", 
                "Trần Thị B", 
                new DateTime(1995, 8, 15), 
                6000000, 
                150
            ));

            // Tạo nhân viên quản lý
            danhSachNV.Add(new NhanVienQuanLy(
                "NV003", 
                "Lê Văn C", 
                new DateTime(1985, 12, 20), 
                12000000, 
                5000000, 
                10
            ));

            // ================================================================
            // PHẦN 2: HIỂN THỊ DANH SÁCH
            // ================================================================

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

            /// <summary>
            /// Duyệt qua danh sách và gọi HienThi()
            /// Mỗi nhân viên tự biết cách hiển thị của mình
            /// </summary>
            foreach (NhanVien nv in danhSachNV)
            {
                nv.HienThi();  // Gọi HienThi() đã được override
                Console.WriteLine();
            }

            // ================================================================
            // PHẦN 3: THỐNG KÊ
            // ================================================================

            Console.WriteLine("▶ THỐNG KÊ NHÂN VIÊN:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            /// <summary>
            /// Tính tổng lương công ty
            /// </summary>
            double tongLuong = 0;
            foreach (NhanVien nv in danhSachNV)
            {
                tongLuong += nv.TinhLuong();
            }
            Console.WriteLine($"  Tổng lương công ty: {tongLuong:N0} VND");

            /// <summary>
            /// Tìm lương cao nhất
            /// </summary>
            double luongCaoNhat = 0;
            NhanVien nvCaoNhat = null;
            foreach (NhanVien nv in danhSachNV)
            {
                if (nv.TinhLuong() > luongCaoNhat)
                {
                    luongCaoNhat = nv.TinhLuong();
                    nvCaoNhat = nv;
                }
            }
            if (nvCaoNhat != null)
            {
                Console.WriteLine($"  Nhân viên lương cao nhất: {nvCaoNhat.HoTen}");
                Console.WriteLine($"  Lương: {luongCaoNhat:N0} VND");
            }

            /// <summary>
            /// Đếm số nhân viên theo loại
            /// </summary>
            int nvVP = 0, nvSX = 0, nvQL = 0;
            foreach (NhanVien nv in danhSachNV)
            {
                if (nv is NhanVienVanPhong) nvVP++;
                else if (nv is NhanVienSanXuat) nvSX++;
                else if (nv is NhanVienQuanLy) nvQL++;
            }
            Console.WriteLine($"\n  Nhân viên văn phòng: {nvVP}");
            Console.WriteLine($"  Nhân viên sản xuất : {nvSX}");
            Console.WriteLine($"  Nhân viên quản lý  : {nvQL}");

            // ================================================================
            // PHẦN 4: THỬ NHẬP TỪ BÀN PHÍM
            // ================================================================

            Console.WriteLine("\n▶ THỬ NHẬP NHÂN VIÊN MỚI:");
            Console.WriteLine("───────────────────────────────────────────────────\n");

            try
            {
                Console.Write("  Mã NV: ");
                string ma = Console.ReadLine();

                Console.Write("  Họ tên: ");
                string ten = Console.ReadLine();

                Console.Write("  Ngày sinh (dd/MM/yyyy): ");
                DateTime ngaySinh = DateTime.ParseExact(Console.ReadLine(), "dd/MM/yyyy", 
                    System.Globalization.CultureInfo.InvariantCulture);

                Console.Write("  Lương cơ bản: ");
                double luong = double.Parse(Console.ReadLine());

                Console.Write("  Loại nhân viên (1: VP, 2: SX, 3: QL): ");
                int loai = int.Parse(Console.ReadLine());

                NhanVien nvMoi = null;

                switch (loai)
                {
                    case 1: // Văn phòng
                        Console.Write("  Số ngày làm việc: ");
                        int ngay = int.Parse(Console.ReadLine());
                        nvMoi = new NhanVienVanPhong(ma, ten, ngaySinh, luong, ngay);
                        break;

                    case 2: // Sản xuất
                        Console.Write("  Số sản phẩm: ");
                        int sp = int.Parse(Console.ReadLine());
                        Console.Write("  Đơn giá sản phẩm (mặc định 50000): ");
                        string donGiaStr = Console.ReadLine();
                        double donGia = string.IsNullOrEmpty(donGiaStr) ? 50000 : double.Parse(donGiaStr);
                        nvMoi = new NhanVienSanXuat(ma, ten, ngaySinh, luong, sp, donGia);
                        break;

                    case 3: // Quản lý
                        Console.Write("  Thưởng: ");
                        double thuong = double.Parse(Console.ReadLine());
                        Console.Write("  Số nhân viên quản lý: ");
                        int soNV = int.Parse(Console.ReadLine());
                        nvMoi = new NhanVienQuanLy(ma, ten, ngaySinh, luong, thuong, soNV);
                        break;

                    default:
                        Console.WriteLine("  ❌ Loại nhân viên không hợp lệ!");
                        break;
                }

                if (nvMoi != null)
                {
                    danhSachNV.Add(nvMoi);
                    Console.WriteLine("\n  ✅ Đã thêm nhân viên mới thành công!");
                    nvMoi.HienThi();
                }
            }
            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();
        }
    }
}

KẾT QUẢ DỰ KIẾN

text
═══════════════════════════════════════════════════
     BÀI TẬP 1: XÂY DỰNG CÁC LỚP NHÂN VIÊN
═══════════════════════════════════════════════════

▶ DANH SÁCH NHÂN VIÊN:
───────────────────────────────────────────────────

═══════════════════════════════════════
  Mã NV       : NV001
  Họ tên      : Nguyễn Văn A
  Ngày sinh   : 10/05/1990
  Tuổi        : 34
  Lương CB    : 8,000,000 VND
  Lương TT    : 12,400,000 VND
  Loại       : Nhân viên văn phòng
  Số ngày làm: 22
═══════════════════════════════════════

═══════════════════════════════════════
  Mã NV       : NV002
  Họ tên      : Trần Thị B
  Ngày sinh   : 15/08/1995
  Tuổi        : 29
  Lương CB    : 6,000,000 VND
  Lương TT    : 13,500,000 VND
  Loại       : Nhân viên sản xuất
  Số SP      : 150
  Đơn giá SP : 50,000 VND
═══════════════════════════════════════

═══════════════════════════════════════
  Mã NV       : NV003
  Họ tên      : Lê Văn C
  Ngày sinh   : 20/12/1985
  Tuổi        : 39
  Lương CB    : 12,000,000 VND
  Lương TT    : 29,000,000 VND
  Loại       : Nhân viên quản lý
  Thưởng     : 5,000,000 VND
  Số NV QL   : 10
═══════════════════════════════════════

▶ THỐNG KÊ NHÂN VIÊN:
───────────────────────────────────────────────────

  Tổng lương công ty: 54,900,000 VND
  Nhân viên lương cao nhất: Lê Văn C
  Lương: 29,000,000 VND

  Nhân viên văn phòng: 1
  Nhân viên sản xuất : 1
  Nhân viên quản lý  : 1

▶ THỬ NHẬP NHÂN VIÊN MỚI:
───────────────────────────────────────────────────

  Mã NV: NV004
  Họ tên: Phạm Văn D
  Ngày sinh (dd/MM/yyyy): 25/12/2000
  Lương cơ bản: 7000000
  Loại nhân viên (1: VP, 2: SX, 3: QL): 1
  Số ngày làm việc: 20

  ✅ Đã thêm nhân viên mới thành công!

═══════════════════════════════════════
  Mã NV       : NV004
  Họ tên      : Phạm Văn D
  Ngày sinh   : 25/12/2000
  Tuổi        : 24
  Lương CB    : 7,000,000 VND
  Lương TT    : 11,000,000 VND
  Loại       : Nhân viên văn phòng
  Số ngày làm: 20
═══════════════════════════════════════

═══════════════════════════════════════════════════
NHẤN PHÍM BẤT KỲ ĐỂ THOÁT...