编程思想进阶笔记POP → OOP → AOP → DDD本文以用户注册这个业务场景为主线逐步演示四种编程思想的演进过程帮助理解每种思想解决了什么问题、又带来了什么新的局限。一、POP —— 面向过程编程核心思想面向过程(Procedure-Oriented Programming)是最直接的编程方式把要做的事情按步骤一行一行写下来从上到下顺序执行。思维方式是线性的适合逻辑简单、不需要扩展的小程序。示例// 用户注册的面向过程写法 Console.WriteLine(用户注册提交信息); Console.WriteLine(1 参数检查); Console.WriteLine(2 数据入库); Console.WriteLine(3 记录日志);问题在哪所有逻辑堆在一起代码越写越长越来越难维护需求一变就要到处改代码牵一发动全身无法复用同样的逻辑在不同地方重复出现不能应对变化扩展性极差二、OOP —— 面向对象编程核心思想面向对象(Object-Oriented Programming)把程序拆分成一个个对象每个对象负责自己的事情。三大特性封装、继承、多态。封装把细节藏起来外部只看接口继承子类复用父类代码多态(面向抽象)上层代码只依赖接口不依赖具体实现这是 OOP 扩展性的核心示例演进第一步封装对象// 把用户信息封装成对象 UserInfo userInfo new UserInfo() { Account Administrator, Name 张三, Password 888888 };第二步面向抽象(依赖接口而非具体类)// 定义接口 public interface IDBHelper { int Save(UserInfo userInfo); } // MySQL 实现 public class MySQLDBHelper : IDBHelper { public int Save(UserInfo userInfo) { Console.WriteLine(数据入MySQL); return 0; } } // SQLServer 实现 public class SqlServerDBHelper : IDBHelper { public int Save(UserInfo userInfo) { Console.WriteLine(数据入SqlServer); return 0; } }第三步用简单工厂解耦public class SimpleFactory { public static IDBHelper CreateDBHelper() { // 只改这一行上层代码完全不用动 return new MySQLDBHelper(); // return new SqlServerDBHelper(); } } // 调用方 IDBHelper dbHelper SimpleFactory.CreateDBHelper(); dbHelper.Save(userInfo);OOP 的价值封装屏蔽了细节上层代码不关心底层怎么实现面向抽象(接口 多实现)让切换实现变得简单这是 80% 设计模式的核心套路依赖注入(DI)、工厂模式、策略模式……都是这个思路的延伸OOP 的局限OOP 的扩展性靠的是对象替换对象。但如果我不想换掉整个对象只是想在某个方法执行前后加点通用逻辑(比如日志、权限检查、性能统计)OOP 就显得力不从心了。最直接的做法是修改原来的类但这违反了开闭原则(对扩展开放对修改关闭)。三、AOP —— 面向切面编程核心思想面向切面(Aspect-Oriented Programming)解决的问题是在不修改原有对象、不替换原有对象的前提下给它额外扩展功能。这些额外的功能通常是横跨多个业务模块的通用逻辑比如日志记录权限验证性能统计异常处理事务管理缓存AOP 不能做业务逻辑它只负责这些横切关注点。实现方式一静态代理手动写一个代理类实现同一个接口内部持有真实对象在调用前后插入额外逻辑。public class DBHelperProxy : IDBHelper { // 持有真实对象 private IDBHelper _iDBHelper new MySQLDBHelper(); private ILogHelper _logger new LogConsole(); public int Save(UserInfo userInfo) { // 方法执行前 _logger.Log(Prepare Save); // 调用真实方法 int result _iDBHelper.Save(userInfo); // 方法执行后 _logger.Log(After Save); return result; } } // 使用时把代理对象当作真实对象用 IDBHelper dbHelper new DBHelperProxy(); dbHelper.Save(userInfo);优点简单直观不需要任何框架支持。缺点每个类都要手写一个代理类代码量大维护成本高。实现方式二动态代理(.NET Core 版).NET Core 提供了DispatchProxy可以在运行时动态生成代理不需要为每个类手写代理代码。// 真实代理拦截所有方法调用 public class MyRealProxyT : DispatchProxy { public T _Instance default; protected override object? Invoke(MethodInfo? targetMethod, object?[]? args) { // 方法执行前的通用逻辑 Console.WriteLine(方法执行前可以加入的逻辑); // 调用真实方法 var result targetMethod?.Invoke(_Instance, args); // 方法执行后的通用逻辑 Console.WriteLine(方法执行后可以加入的逻辑); return result; } } // 透明代理负责创建代理对象 public static class TransparentProxy { public static T CreateT(T instance) { dynamic proxy DispatchProxy.CreateT, MyRealProxyT()!; proxy._Instance instance; return proxy; } } // 使用 IDBHelper dbHelper new MySQLDBHelper(); dbHelper TransparentProxy.CreateIDBHelper(dbHelper); dbHelper.Save(userInfo); // 自动触发前置/后置逻辑注.NET Framework 时代用的是RealProxy(基于 .NET Remoting).NET Core 之后已移除统一用DispatchProxy。动态代理的进阶用法在Invoke方法里可以通过反射读取方法上的自定义特性(Attribute)实现更细粒度的控制// 比如只有标了 [Log] 特性的方法才记录日志 if (targetMethod.IsDefined(typeof(LogAttribute), true)) { // 记录日志 }AOP 的价值业务代码只关注业务逻辑通用逻辑统一在切面里处理代码复用一处修改全局生效方便团队协作业务开发和基础设施开发分离实际框架中的 AOP在实际项目中AOP 通常通过以下方式实现方式代表框架动态代理Castle DynamicProxy、AspectCoreIL 编织PostSharp中间件管道ASP.NET Core Middleware过滤器ASP.NET Core Filter(ActionFilter、ExceptionFilter 等)四、DDD —— 领域驱动设计核心思想领域驱动设计(Domain-Driven Design)不是一种编程语法而是一种软件架构方法论适用于复杂业务系统的设计。它的核心理念是软件的核心复杂性来自业务领域本身代码结构应该反映业务模型。关键概念限界上下文(Bounded Context)把一个大系统按业务边界拆分成多个子域每个子域有自己独立的模型和语言。比如订单系统和库存系统是两个不同的限界上下文它们对商品的理解可能不同。聚合根(Aggregate Root)一组相关对象的入口外部只能通过聚合根来操作内部对象保证数据一致性。实体(Entity)有唯一标识的对象即使属性相同只要 ID 不同就是不同的实体。比如两个同名用户是两个不同的实体。值对象(Value Object)没有唯一标识只靠属性值来区分。比如地址两个地址只要内容相同就认为是同一个。领域服务(Domain Service)不属于任何实体的业务逻辑放在领域服务里。仓储(Repository)负责聚合根的持久化隔离领域层和数据访问层。DDD 与 OOP/AOP 的关系DDD 是更高层次的架构思想它告诉你如何组织代码结构OOP 是实现手段DDD 的各种概念(实体、值对象、聚合根)都用 OOP 来实现AOP 则负责处理横切关注点在 DDD 架构中同样适用。适用场景DDD 适合业务逻辑复杂、团队规模较大的项目。对于简单的 CRUD 应用引入 DDD 反而会增加不必要的复杂度。五、四种思想的对比总结维度POPOOPAOPDDD关注点执行步骤对象和职责横切关注点业务领域模型扩展方式修改代码替换对象(接口多实现)切面拦截领域模型演进适用规模小脚本中小型项目通用功能增强复杂业务系统核心原则无开闭原则、依赖倒置不破坏封装领域模型驱动六、一个贯穿全文的演进脉络以用户注册为例四种思想的演进是这样的POP把注册步骤写成一段顺序代码 ↓ 问题难以维护无法复用 OOP把用户、数据库操作封装成对象用接口解耦 ↓ 问题想加日志只能修改对象或替换对象 AOP用代理模式在方法前后插入日志不动原有代码 ↓ 问题系统越来越大业务边界模糊 DDD按业务领域划分边界用领域模型驱动设计每一步都是在解决上一步遗留的问题没有哪种思想是万能的关键是在合适的场景用合适的工具。
15-C#
编程思想进阶笔记POP → OOP → AOP → DDD本文以用户注册这个业务场景为主线逐步演示四种编程思想的演进过程帮助理解每种思想解决了什么问题、又带来了什么新的局限。一、POP —— 面向过程编程核心思想面向过程(Procedure-Oriented Programming)是最直接的编程方式把要做的事情按步骤一行一行写下来从上到下顺序执行。思维方式是线性的适合逻辑简单、不需要扩展的小程序。示例// 用户注册的面向过程写法 Console.WriteLine(用户注册提交信息); Console.WriteLine(1 参数检查); Console.WriteLine(2 数据入库); Console.WriteLine(3 记录日志);问题在哪所有逻辑堆在一起代码越写越长越来越难维护需求一变就要到处改代码牵一发动全身无法复用同样的逻辑在不同地方重复出现不能应对变化扩展性极差二、OOP —— 面向对象编程核心思想面向对象(Object-Oriented Programming)把程序拆分成一个个对象每个对象负责自己的事情。三大特性封装、继承、多态。封装把细节藏起来外部只看接口继承子类复用父类代码多态(面向抽象)上层代码只依赖接口不依赖具体实现这是 OOP 扩展性的核心示例演进第一步封装对象// 把用户信息封装成对象 UserInfo userInfo new UserInfo() { Account Administrator, Name 张三, Password 888888 };第二步面向抽象(依赖接口而非具体类)// 定义接口 public interface IDBHelper { int Save(UserInfo userInfo); } // MySQL 实现 public class MySQLDBHelper : IDBHelper { public int Save(UserInfo userInfo) { Console.WriteLine(数据入MySQL); return 0; } } // SQLServer 实现 public class SqlServerDBHelper : IDBHelper { public int Save(UserInfo userInfo) { Console.WriteLine(数据入SqlServer); return 0; } }第三步用简单工厂解耦public class SimpleFactory { public static IDBHelper CreateDBHelper() { // 只改这一行上层代码完全不用动 return new MySQLDBHelper(); // return new SqlServerDBHelper(); } } // 调用方 IDBHelper dbHelper SimpleFactory.CreateDBHelper(); dbHelper.Save(userInfo);OOP 的价值封装屏蔽了细节上层代码不关心底层怎么实现面向抽象(接口 多实现)让切换实现变得简单这是 80% 设计模式的核心套路依赖注入(DI)、工厂模式、策略模式……都是这个思路的延伸OOP 的局限OOP 的扩展性靠的是对象替换对象。但如果我不想换掉整个对象只是想在某个方法执行前后加点通用逻辑(比如日志、权限检查、性能统计)OOP 就显得力不从心了。最直接的做法是修改原来的类但这违反了开闭原则(对扩展开放对修改关闭)。三、AOP —— 面向切面编程核心思想面向切面(Aspect-Oriented Programming)解决的问题是在不修改原有对象、不替换原有对象的前提下给它额外扩展功能。这些额外的功能通常是横跨多个业务模块的通用逻辑比如日志记录权限验证性能统计异常处理事务管理缓存AOP 不能做业务逻辑它只负责这些横切关注点。实现方式一静态代理手动写一个代理类实现同一个接口内部持有真实对象在调用前后插入额外逻辑。public class DBHelperProxy : IDBHelper { // 持有真实对象 private IDBHelper _iDBHelper new MySQLDBHelper(); private ILogHelper _logger new LogConsole(); public int Save(UserInfo userInfo) { // 方法执行前 _logger.Log(Prepare Save); // 调用真实方法 int result _iDBHelper.Save(userInfo); // 方法执行后 _logger.Log(After Save); return result; } } // 使用时把代理对象当作真实对象用 IDBHelper dbHelper new DBHelperProxy(); dbHelper.Save(userInfo);优点简单直观不需要任何框架支持。缺点每个类都要手写一个代理类代码量大维护成本高。实现方式二动态代理(.NET Core 版).NET Core 提供了DispatchProxy可以在运行时动态生成代理不需要为每个类手写代理代码。// 真实代理拦截所有方法调用 public class MyRealProxyT : DispatchProxy { public T _Instance default; protected override object? Invoke(MethodInfo? targetMethod, object?[]? args) { // 方法执行前的通用逻辑 Console.WriteLine(方法执行前可以加入的逻辑); // 调用真实方法 var result targetMethod?.Invoke(_Instance, args); // 方法执行后的通用逻辑 Console.WriteLine(方法执行后可以加入的逻辑); return result; } } // 透明代理负责创建代理对象 public static class TransparentProxy { public static T CreateT(T instance) { dynamic proxy DispatchProxy.CreateT, MyRealProxyT()!; proxy._Instance instance; return proxy; } } // 使用 IDBHelper dbHelper new MySQLDBHelper(); dbHelper TransparentProxy.CreateIDBHelper(dbHelper); dbHelper.Save(userInfo); // 自动触发前置/后置逻辑注.NET Framework 时代用的是RealProxy(基于 .NET Remoting).NET Core 之后已移除统一用DispatchProxy。动态代理的进阶用法在Invoke方法里可以通过反射读取方法上的自定义特性(Attribute)实现更细粒度的控制// 比如只有标了 [Log] 特性的方法才记录日志 if (targetMethod.IsDefined(typeof(LogAttribute), true)) { // 记录日志 }AOP 的价值业务代码只关注业务逻辑通用逻辑统一在切面里处理代码复用一处修改全局生效方便团队协作业务开发和基础设施开发分离实际框架中的 AOP在实际项目中AOP 通常通过以下方式实现方式代表框架动态代理Castle DynamicProxy、AspectCoreIL 编织PostSharp中间件管道ASP.NET Core Middleware过滤器ASP.NET Core Filter(ActionFilter、ExceptionFilter 等)四、DDD —— 领域驱动设计核心思想领域驱动设计(Domain-Driven Design)不是一种编程语法而是一种软件架构方法论适用于复杂业务系统的设计。它的核心理念是软件的核心复杂性来自业务领域本身代码结构应该反映业务模型。关键概念限界上下文(Bounded Context)把一个大系统按业务边界拆分成多个子域每个子域有自己独立的模型和语言。比如订单系统和库存系统是两个不同的限界上下文它们对商品的理解可能不同。聚合根(Aggregate Root)一组相关对象的入口外部只能通过聚合根来操作内部对象保证数据一致性。实体(Entity)有唯一标识的对象即使属性相同只要 ID 不同就是不同的实体。比如两个同名用户是两个不同的实体。值对象(Value Object)没有唯一标识只靠属性值来区分。比如地址两个地址只要内容相同就认为是同一个。领域服务(Domain Service)不属于任何实体的业务逻辑放在领域服务里。仓储(Repository)负责聚合根的持久化隔离领域层和数据访问层。DDD 与 OOP/AOP 的关系DDD 是更高层次的架构思想它告诉你如何组织代码结构OOP 是实现手段DDD 的各种概念(实体、值对象、聚合根)都用 OOP 来实现AOP 则负责处理横切关注点在 DDD 架构中同样适用。适用场景DDD 适合业务逻辑复杂、团队规模较大的项目。对于简单的 CRUD 应用引入 DDD 反而会增加不必要的复杂度。五、四种思想的对比总结维度POPOOPAOPDDD关注点执行步骤对象和职责横切关注点业务领域模型扩展方式修改代码替换对象(接口多实现)切面拦截领域模型演进适用规模小脚本中小型项目通用功能增强复杂业务系统核心原则无开闭原则、依赖倒置不破坏封装领域模型驱动六、一个贯穿全文的演进脉络以用户注册为例四种思想的演进是这样的POP把注册步骤写成一段顺序代码 ↓ 问题难以维护无法复用 OOP把用户、数据库操作封装成对象用接口解耦 ↓ 问题想加日志只能修改对象或替换对象 AOP用代理模式在方法前后插入日志不动原有代码 ↓ 问题系统越来越大业务边界模糊 DDD按业务领域划分边界用领域模型驱动设计每一步都是在解决上一步遗留的问题没有哪种思想是万能的关键是在合适的场景用合适的工具。