OOP面向对象编程
大约 12 分钟约 3514 字
OOP面向对象编程
简介
Object Oriented Programming
OOP面向对象编程,是按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法。
OOP 是目前主流的编程范式,C#、Java、Python 等语言都支持面向对象。其核心思想是将现实世界中的事物抽象为对象,通过对象之间的交互来完成系统功能。
特点
OOP的四个特征:抽象、封装、继承、多态
1. 抽象性
将一些事物的共性和相似点抽离出来,将这些属性归为一类。这个类只考虑这些事物的共性和相似之处,并且会忽略与当前业务和目标无关的方面,将注意力集中在当前目标有关的方面。
抽象包括两个方面:过程抽象和数据抽象。
/// <summary>
/// 抽象:从现实中提取共同特征
/// 现实中有猫、狗、鸟 —— 它们都有名字、年龄,都能发出声音
/// 抽象出一个 Animal 类
/// </summary>
public abstract class Animal
{
// 数据抽象:提取共同属性
public string Name { get; set; }
public int Age { get; set; }
// 过程抽象:提取共同行为(但不具体实现)
public abstract void Speak();
public abstract void Move();
// 通用行为可以直接实现
public void ShowInfo()
{
Console.WriteLine($"我是{Name},今年{Age}岁");
}
}
/// <summary>
/// 具体的动物类 — 继承抽象类并实现抽象方法
/// </summary>
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("汪汪汪!");
}
public override void Move()
{
Console.WriteLine("四条腿奔跑");
}
}
public class Bird : Animal
{
public override void Speak()
{
Console.WriteLine("叽叽喳喳!");
}
public override void Move()
{
Console.WriteLine("展翅飞翔");
}
}2. 封装性
一种信息隐蔽技术,用户只能看见对象封装界面上的信息,对象的内部实现对用户是隐蔽的。封装的目的是使对象的使用者和生成者分离,使对象的定义和实现分开。
/// <summary>
/// 封装:隐藏内部实现,暴露必要接口
/// </summary>
public class BankAccount
{
// 私有字段 — 外部不可直接访问
private decimal _balance;
private string _accountNumber;
private List<string> _transactionHistory = new List<string>();
// 公开属性 — 控制访问方式
public string AccountNumber => _accountNumber;
public decimal Balance => _balance; // 只读,不允许外部直接修改余额
public BankAccount(string accountNumber, decimal initialBalance = 0)
{
_accountNumber = accountNumber;
_balance = initialBalance;
RecordTransaction("开户", initialBalance);
}
// 公开方法 — 提供受控的操作入口
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("存款金额必须大于0");
_balance += amount;
RecordTransaction("存款", amount);
}
public void Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("取款金额必须大于0");
if (amount > _balance)
throw new InvalidOperationException("余额不足");
_balance -= amount;
RecordTransaction("取款", amount);
}
public void PrintHistory()
{
Console.WriteLine($"账户 {_accountNumber} 交易记录:");
foreach (var record in _transactionHistory)
{
Console.WriteLine($" {record}");
}
Console.WriteLine($"当前余额:{_balance:C}");
}
// 私有方法 — 内部使用
private void RecordTransaction(string type, decimal amount)
{
_transactionHistory.Add(
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {type} {amount:C}"
);
}
}
// 使用方无需了解内部实现
var account = new BankAccount("622200001234", 1000);
account.Deposit(500);
account.Withdraw(200);
// account._balance = 99999; // 编译错误!无法访问私有字段
account.PrintHistory();封装的访问修饰符层级:
3. 继承性
继承是类间的基本关系,它是基于层次关系的不同类共享数据和操作的一种机制。父类中定义了其所有子类的公共属性和操作,在子类中除了定义自己的特有属性和操作外,可以继承其父类(或祖先类)的属性和操作,还可以对父类中的操作重新定义其实现方法。
/// <summary>
/// 继承:子类复用父类的代码,并扩展自身特性
/// </summary>
// 基类(父类)
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public decimal BaseSalary { get; set; }
// 虚方法 — 子类可以重写
public virtual decimal CalculateSalary()
{
return BaseSalary;
}
public void ShowInfo()
{
Console.WriteLine($"工号:{Id},姓名:{Name},薪资:{CalculateSalary():C}");
}
}
// 派生类(子类)— 继承 Employee
public class Manager : Employee
{
public decimal Bonus { get; set; }
public int TeamSize { get; set; }
// 重写父类方法
public override decimal CalculateSalary()
{
return BaseSalary + Bonus + TeamSize * 500;
}
}
// 再派生 — 继承链
public class SeniorManager : Manager
{
public string Department { get; set; }
public decimal StockOption { get; set; }
public override decimal CalculateSalary()
{
return base.CalculateSalary() + StockOption;
}
}
// 使用
var emp = new Employee { Id = 1, Name = "张三", BaseSalary = 10000 };
var mgr = new Manager
{
Id = 2, Name = "李四", BaseSalary = 15000,
Bonus = 5000, TeamSize = 8
};
var senior = new SeniorManager
{
Id = 3, Name = "王五", BaseSalary = 20000,
Bonus = 10000, TeamSize = 20, StockOption = 15000
};
emp.ShowInfo(); // 工号:1,姓名:张三,薪资:¥10,000.00
mgr.ShowInfo(); // 工号:2,姓名:李四,薪资:¥19,000.00
senior.ShowInfo(); // 工号:3,姓名:王五,薪资:¥45,000.00继承要点:
4. 多态性
一个类实例的相同方法在不同的情形有不同的表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。多态的实现:覆写override、重载overload。
重载(Overload)— 编译时多态
public class Calculator
{
// 方法名相同,参数列表不同
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
public int Add(int a, int b, int c) => a + b + c;
public string Add(string a, string b) => a + b;
}覆写(Override)— 运行时多态
/// <summary>
/// 多态的核心:父类引用指向子类对象
/// 调用同一方法,不同对象有不同表现
/// </summary>
public abstract class Shape
{
public string Name { get; set; }
public abstract double Area();
public virtual void Display()
{
Console.WriteLine($"{Name} 的面积是 {Area():F2}");
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Name = "圆形";
Radius = radius;
}
public override double Area() => Math.PI * Radius * Radius;
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Name = "矩形";
Width = width;
Height = height;
}
public override double Area() => Width * Height;
}
public class Triangle : Shape
{
public double Base { get; set; }
public double Height { get; set; }
public Triangle(double @base, double height)
{
Name = "三角形";
Base = @base;
Height = height;
}
public override double Area() => 0.5 * Base * Height;
}
// ===== 多态的威力 =====
// 父类引用指向不同的子类对象
List<Shape> shapes = new List<Shape>
{
new Circle(5),
new Rectangle(4, 6),
new Triangle(3, 8)
};
// 同一个方法调用,不同的行为表现
foreach (var shape in shapes)
{
shape.Display();
}
// 圆形 的面积是 78.54
// 矩形 的面积是 24.00
// 三角形 的面积是 12.00多态的实际应用 — 策略模式
/// <summary>
/// 利用多态消除 if-else,实现策略模式
/// </summary>
public interface IDiscountStrategy
{
decimal CalculateDiscount(decimal originalPrice);
string Name { get; }
}
public class NoDiscount : IDiscountStrategy
{
public string Name => "无折扣";
public decimal CalculateDiscount(decimal originalPrice) => originalPrice;
}
public class VipDiscount : IDiscountStrategy
{
public string Name => "VIP折扣";
public decimal CalculateDiscount(decimal originalPrice) => originalPrice * 0.8m;
}
public class SuperVipDiscount : IDiscountStrategy
{
public string Name => "超级VIP折扣";
public decimal CalculateDiscount(decimal originalPrice) => originalPrice * 0.6m;
}
public class OrderService
{
// 面向接口编程 — 多态的体现
public void ProcessOrder(decimal price, IDiscountStrategy discountStrategy)
{
decimal finalPrice = discountStrategy.CalculateDiscount(price);
Console.WriteLine($"策略:{discountStrategy.Name},原价:{price:C},实付:{finalPrice:C}");
}
}
// 使用
var service = new OrderService();
service.ProcessOrder(1000, new NoDiscount()); // 实付:¥1,000.00
service.ProcessOrder(1000, new VipDiscount()); // 实付:¥800.00
service.ProcessOrder(1000, new SuperVipDiscount()); // 实付:¥600.00OOP 设计原则 — SOLID
| 原则 | 英文 | 含义 |
|---|---|---|
| S | Single Responsibility | 单一职责 — 一个类只做一件事 |
| O | Open/Closed | 开闭原则 — 对扩展开放,对修改关闭 |
| L | Liskov Substitution | 里氏替换 — 子类可以替换父类 |
| I | Interface Segregation | 接口隔离 — 接口要小而专 |
| D | Dependency Inversion | 依赖倒置 — 面向抽象编程 |
/// <summary>
/// SOLID 在一个示例中的体现
/// </summary>
// S — 单一职责:日志类只负责日志
public interface ILogger
{
void Log(string message);
}
public class FileLogger : ILogger
{
public void Log(string message)
{
File.AppendAllText("app.log", $"[{DateTime.Now}] {message}\n");
}
}
// O — 开闭原则:新增通知方式不需要修改现有代码
public interface INotification
{
void Send(string to, string message);
}
public class EmailNotification : INotification
{
public void Send(string to, string message)
{
Console.WriteLine($"发送邮件给 {to}: {message}");
}
}
public class SmsNotification : INotification
{
public void Send(string to, string message)
{
Console.WriteLine($"发送短信给 {to}: {message}");
}
}
// D — 依赖倒置:UserService 依赖抽象,不依赖具体实现
public class UserService
{
private readonly ILogger _logger;
private readonly INotification _notification;
// 通过构造函数注入依赖(面向抽象)
public UserService(ILogger logger, INotification notification)
{
_logger = logger;
_notification = notification;
}
public void RegisterUser(string userName, string contact)
{
// 业务逻辑
Console.WriteLine($"注册用户:{userName}");
// 使用抽象的依赖
_logger.Log($"用户 {userName} 注册成功");
_notification.Send(contact, $"欢迎 {userName} 注册");
}
}
// 使用
var service = new UserService(new FileLogger(), new EmailNotification());
service.RegisterUser("张三", "zhangsan@example.com");OOP 在实际项目中的应用
实体模型设计
/// <summary>
/// 电商系统中的实体模型设计 — 体现封装、继承、多态
/// </summary>
// 基类:所有实体的公共属性
public abstract class EntityBase
{
public int Id { get; set; }
public DateTime CreatedTime { get; set; } = DateTime.Now;
public DateTime? UpdatedTime { get; set; }
public bool IsDeleted { get; set; }
}
// 商品实体
public class Product : EntityBase
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
public int Stock { get; private set; }
// 封装业务规则
public void ReduceStock(int quantity)
{
if (quantity > Stock)
throw new InvalidOperationException("库存不足");
Stock -= quantity;
}
public void AddStock(int quantity)
{
if (quantity <= 0)
throw new ArgumentException("数量必须大于0");
Stock += quantity;
}
}
// 订单实体
public class Order : EntityBase
{
public string OrderNo { get; set; }
public int UserId { get; set; }
public List<OrderItem> Items { get; set; } = new List<OrderItem>();
public OrderStatus Status { get; set; }
public decimal TotalAmount => Items.Sum(i => i.Subtotal);
public void AddItem(Product product, int quantity)
{
product.ReduceStock(quantity);
Items.Add(new OrderItem
{
ProductId = product.Id,
ProductName = product.Name,
Price = product.Price,
Quantity = quantity
});
}
}
public class OrderItem
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public decimal Subtotal => Price * Quantity;
}
public enum OrderStatus
{
Pending, Paid, Shipped, Completed, Cancelled
}优点
缺点
总结
OOP 是现代软件开发的核心范式,理解抽象、封装、继承、多态四大特征是进阶设计模式和架构思维的基础。在实际开发中,要遵循 SOLID 原则,避免过度设计,根据场景选择合适的抽象粒度。
关键知识点
- 模式不是目标,降低耦合和控制变化才是目标。
- 先找变化点、稳定点和协作边界,再决定是否引入模式。
- 同一个模式在不同规模下的收益和代价差异很大。
项目落地视角
- 优先画出参与对象、依赖方向和调用链,再落到代码。
- 把模式放到一个真实场景里,比如支付、规则引擎、工作流或插件扩展。
- 配合单元测试或契约测试,保证重构后的行为没有漂移。
常见误区
- 为了看起来“高级”而套模式。
- 把简单问题拆成过多抽象层,导致阅读和排障都变难。
- 只会背 UML,不会解释为什么这里需要这个模式。
进阶路线
- 继续关注模式之间的组合用法,而不是孤立记忆。
- 从业务建模、演进策略和团队协作角度看模式的适用性。
- 把模式结论沉淀为项目模板、基类或约束文档。
适用场景
- 当你准备把《OOP面向对象编程》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在业务规则频繁变化、分支增多或对象协作复杂时引入。
- 当你希望提高扩展性,但又不想把系统拆得过度抽象时,这类主题很有参考价值。
落地建议
- 先识别变化点,再决定是否引入模式,而不是反过来套模板。
- 优先为模式的边界、依赖和调用路径画出简单结构图。
- 把模式落到一个明确场景,例如支付、规则计算、插件扩展或工作流。
排错清单
- 检查抽象层是否过多,导致调用路径和责任不清晰。
- 确认引入模式后是否真的减少了条件分支和重复代码。
- 警惕“为了模式而模式”,尤其是在简单业务里。
复盘问题
- 如果把《OOP面向对象编程》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《OOP面向对象编程》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《OOP面向对象编程》最大的收益和代价分别是什么?
