VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • 接口与抽象类——用C#开发支付方式统一接口设计

第一部分:C#基础入门
实例3:接口与抽象类——支付方式统一接口设计
实例介绍
电商系统中,支付方式五花八门(微信、支付宝、银联),但核心流程高度一致:创建订单→发起支付→查询状态。如果每个支付方式都单独写一套逻辑,不仅重复代码多,新增方式时还得改原有代码(违反开放封闭原则)。
这节我们用接口定义支付契约、抽象类封装公共逻辑,让不同支付方式既遵vb.net教程C#教程python教程SQL教程access 2010教程循统一标准,又保留各自实现细节。通过这个案例,你能搞懂“什么时候用接口、什么时候用抽象类”,以及如何用它们写出灵活、易扩展的代码。
需求分析
1.统一支付接口:定义IPayable接口,包含所有支付方式必须实现的核心方法(创建订单、支付、查询状态);
2.抽象类封装公共逻辑:BasePayment抽象类封装日志、参数校验等所有支付方式都需要的通用操作;
3.具体支付方式实现:微信(WeChatPay)、支付宝(Alipay)、银联(UnionPay)继承抽象类,实现接口方法;
4.支付管理器:用多态统一处理支付请求,不管是哪种方式,都能通过接口调用;
5.易扩展性:新增支付方式(比如PayPal)时,无需修改现有代码,只需新增子类即可。
代码实现
csharp

	using System;
	using System.Globalization;
	
	namespace PaymentSystemDemo
	{
	// 1. 支付接口:定义所有支付方式必须遵守的契约
	public interface IPayable
	{
	// 创建支付订单(返回第三方支付订单号)
	string CreateOrder(string merchantOrderId, decimal amount);
	// 发起支付(返回支付是否成功)
	bool Pay(string paymentOrderId);
	// 查询支付状态(返回状态描述:成功/失败/处理中)
	string QueryStatus(string paymentOrderId);
	}
	
	// 2. 抽象支付类:封装公共逻辑(日志、参数校验)
	public abstract class BasePayment : IPayable
	{
	// 支付渠道名称(子类必须赋值)
	protected string _channelName;
	
	// 公共日志方法(子类可直接调用,外部不可见)
	protected void Log(string message)
	{
	Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{_channelName}] {message}");
	}
	
	// 公共参数校验方法(子类创建订单时调用)
	protected bool ValidateParams(string merchantOrderId, decimal amount)
	{
	if (string.IsNullOrWhiteSpace(merchantOrderId))
	{
	Log("商户订单ID不能为空");
	return false;
	}
	if (amount <= 0)
	{
	Log($"金额{amount}不合法,必须大于0");
	return false;
	}
	return true;
	}
	
	// 接口方法的抽象实现(子类必须重写)
	public abstract string CreateOrder(string merchantOrderId, decimal amount);
	public abstract bool Pay(string paymentOrderId);
	public abstract string QueryStatus(string paymentOrderId);
	}
	
	// 3. 微信支付实现
	public class WeChatPay : BasePayment
	{
	public WeChatPay()
	{
	_channelName = "微信支付";
	}
	
	public override string CreateOrder(string merchantOrderId, decimal amount)
	{
	if (!ValidateParams(merchantOrderId, amount)) return null;
	Log($"创建订单:商户ID={merchantOrderId},金额={amount:C}");
	// 模拟微信支付订单号生成(实际调用微信SDK)
	return $"WX_{merchantOrderId}_{Guid.NewGuid().ToString("N").Substring(0, 8)}";
	}
	
	public override bool Pay(string paymentOrderId)
	{
	Log($"发起支付:订单ID={paymentOrderId}");
	// 模拟微信支付成功(实际调用微信支付接口)
	return true;
	}
	
	public override string QueryStatus(string paymentOrderId)
	{
	Log($"查询状态:订单ID={paymentOrderId}");
	// 模拟微信支付状态查询
	return "支付成功";
	}
	}
	
	// 4. 支付宝支付实现
	public class Alipay : BasePayment
	{
	public Alipay()
	{
	_channelName = "支付宝";
	}
	
	public override string CreateOrder(string merchantOrderId, decimal amount)
	{
	if (!ValidateParams(merchantOrderId, amount)) return null;
	Log($"创建订单:商户ID={merchantOrderId},金额={amount:C}");
	// 模拟支付宝订单号生成
	return $"ALI_{merchantOrderId}_{DateTime.Now.Ticks.ToString().Substring(0, 10)}";
	}
	
	public override bool Pay(string paymentOrderId)
	{
	Log($"发起支付:订单ID={paymentOrderId}");
	// 模拟支付宝支付成功
	return true;
	}
	
	public override string QueryStatus(string paymentOrderId)
	{
	Log($"查询状态:订单ID={paymentOrderId}");
	return "支付成功";
	}
	}
	
	// 5. 银联支付实现
	public class UnionPay : BasePayment
	{
	public UnionPay()
	{
	_channelName = "银联支付";
	}
	
	public override string CreateOrder(string merchantOrderId, decimal amount)
	{
	if (!ValidateParams(merchantOrderId, amount)) return null;
	Log($"创建订单:商户ID={merchantOrderId},金额={amount:C}");
	// 模拟银联订单号生成
	return $"UNION_{merchantOrderId}_{new Random().Next(100000, 999999)}";
	}
	
	public override bool Pay(string paymentOrderId)
	{
	Log($"发起支付:订单ID={paymentOrderId}");
	// 模拟银联支付成功
	return true;
	}
	
	public override string QueryStatus(string paymentOrderId)
	{
	Log($"查询状态:订单ID={paymentOrderId}");
	return "支付成功";
	}
	}
	
	// 6. 支付管理器:统一处理支付请求(多态核心应用)
	public static class PaymentManager
	{
	public static void ProcessPayment(IPayable payment, string merchantOrderId, decimal amount)
	{
	Console.WriteLine("
===== 开始支付流程 =====");
	// 1. 创建支付订单
	var paymentOrderId = payment.CreateOrder(merchantOrderId, amount);
	if (string.IsNullOrWhiteSpace(paymentOrderId))
	{
	Console.WriteLine("===== 支付失败:订单创建失败 =====");
	return;
	}
	// 2. 发起支付
	bool paySuccess = payment.Pay(paymentOrderId);
	if (!paySuccess)
	{
	Console.WriteLine("===== 支付失败:支付请求被拒绝 =====");
	return;
	}
	// 3. 查询支付状态
	string status = payment.QueryStatus(paymentOrderId);
	Console.WriteLine($"===== 支付完成:状态={status} =====");
	}
	}
	
	// 7. 主程序:模拟电商支付场景
	class Program
	{
	static void Main(string[] args)
	{
	// 模拟商户订单信息
	string merchantOrderId = "M202405200001";
	decimal amount = 199.9m;
	
	// 微信支付测试
	Console.WriteLine("=== 微信支付测试 ===");
	PaymentManager.ProcessPayment(new WeChatPay(), merchantOrderId, amount);
	
	// 支付宝支付测试
	Console.WriteLine("
=== 支付宝支付测试 ===");
	PaymentManager.ProcessPayment(new Alipay(), merchantOrderId, amount);
	
	// 银联支付测试
	Console.WriteLine("
=== 银联支付测试 ===");
	PaymentManager.ProcessPayment(new UnionPay(), merchantOrderId, amount);
	}
	}
	}

逐行讲解

  1. 支付接口IPayable
    csharp
	public interface IPayable
	{
	string CreateOrder(string merchantOrderId, decimal amount);
	bool Pay(string paymentOrderId);
	string QueryStatus(string paymentOrderId);
	}

接口的作用:定义“支付”这个行为的契约——不管哪种支付方式,都必须实现这三个方法。这就像一份协议,所有支付方式都得遵守,保证了统一的调用方式。
为什么用接口:支付方式是不同的类(微信、支付宝),但它们的核心行为一致,接口让这些类“看起来一样”,方便统一管理。
2. 抽象类BasePayment
csharp

	public abstract class BasePayment : IPayable
	{
	protected string _channelName;
	protected void Log(string message) { ... }
	protected bool ValidateParams(...) { ... }
	public abstract string CreateOrder(...) { ... }
	}

抽象类的定位:封装公共逻辑,避免子类重复写代码。比如日志(每个支付方式都要打日志)、参数校验(每个支付方式都要校验订单ID和金额)。
protected修饰符:Log和ValidateParams方法用protected,意味着子类可以直接调用,但外部代码不能访问——既复用了代码,又保证了封装性。
抽象方法:CreateOrder等方法用abstract,说明子类必须重写这些方法——公共逻辑在父类,具体实现留给子类。
3. 微信支付子类WeChatPay
csharp

	public class WeChatPay : BasePayment
	{
	public WeChatPay() { _channelName = "微信支付"; }
	public override string CreateOrder(...)
	{
	if (!ValidateParams(...)) return null;
	Log(...);
	return $"WX_{merchantOrderId}_{...}";
	}
	}

继承抽象类:WeChatPay继承BasePayment,自动获得Log和ValidateParams方法,不用自己写。
重写抽象方法:CreateOrder方法里,先调用父类的ValidateParams校验参数,再打日志,最后生成微信支付的订单号——这就是“公共逻辑+具体实现”的完美结合。
构造函数赋值:_channelName是父类的protected字段,子类构造函数里赋值为“微信支付”,这样日志里就能显示支付渠道名称。
4. 支付管理器PaymentManager
csharp

	public static void ProcessPayment(IPayable payment, ...)
	{
	var orderId = payment.CreateOrder(...);
	bool success = payment.Pay(orderId);
	string status = payment.QueryStatus(orderId);
	}

多态的核心:ProcessPayment方法的参数是IPayable接口,不管传入的是WeChatPay、Alipay还是UnionPay,都能调用CreateOrder等方法——因为它们都实现了IPayable接口。
开放封闭原则:如果要新增PayPal支付,只需写一个PayPalPay类继承BasePayment并实现IPayable,不用修改ProcessPayment方法——这就是“对扩展开放,对修改关闭”。
5. 主程序测试
csharp

	PaymentManager.ProcessPayment(new WeChatPay(), merchantOrderId, amount);
	PaymentManager.ProcessPayment(new Alipay(), merchantOrderId, amount);

统一调用:不管是微信还是支付宝,调用方式完全一样——PaymentManager.ProcessPayment一句话搞定。新增支付方式时,只需加一行类似的代码,不用改任何已有逻辑。
基础知识拓展

  1. 接口vs抽象类:什么时候用哪个?
场景 用接口 用抽象类
定义行为契约(比如支付、打印) ✅ ❌
封装公共逻辑(比如日志、校验) ❌ ✅
多继承需求(一个类需要多个行为) ✅(可实现多个接口) ❌(只能继承一个抽象类)
类之间无层级关系但有共同行为 ✅ ❌
类之间有层级关系且有公共逻辑 ❌ ✅

支付场景的选择逻辑:
支付方式的核心行为(创建订单、支付)用接口定义,保证统一调用;
公共逻辑(日志、校验)用抽象类封装,避免重复代码;
子类继承抽象类并实现接口,既复用了公共逻辑,又遵守了行为契约。
2. 接口的默认方法(C#8+)
C#8开始,接口可以有默认方法——当新增接口方法时,子类不用强制重写。比如给IPayable加一个Refund方法:
csharp

	public interface IPayable
	{
	// 原有方法...
	default bool Refund(string orderId)
	{
	Console.WriteLine("默认退款逻辑");
	return true;
	}
	}

好处:新增功能时不破坏现有代码——子类可以选择重写Refund方法,也可以直接用默认实现。
3. 依赖倒置原则(DIP)
这个案例完美体现了依赖倒置原则:
高层模块(PaymentManager)不依赖低层模块(WeChatPay、Alipay),而是依赖抽象(IPayable);
低层模块依赖抽象:WeChatPay等子类实现IPayable接口。
这样,高层模块和低层模块都依赖抽象,新增低层模块(比如PayPalPay)时,高层模块不用改——这就是代码灵活的关键。
扩展思考
1.新增支付方式:写一个PayPalPay类,继承BasePayment并实现IPayable,看看是否需要修改PaymentManager?
2.支付异常处理:在子类的Pay方法里添加try-catch,处理第三方支付SDK的异常,并返回false;
3.依赖注入:用依赖注入框架(比如Autofac)注册IPayable的实现,替换主程序里的new WeChatPay(),实现解耦;
4.异步支付:把CreateOrder、Pay方法改成异步(async/await),适应实际支付场景中的异步接口;
5.支付回调:添加回调方法,处理第三方支付的异步通知(比如微信支付的回调接口)。
总结
接口和抽象类是C#面向对象编程的“黄金搭档”:
接口定义行为契约,保证统一调用;
抽象类封装公共逻辑,避免重复代码;
两者结合,能写出高复用、高扩展、易维护的代码——新增功能不用改旧代码,只需加新类。
通过支付系统这个案例,你应该能明白:接口是“做什么”,抽象类是“怎么做(公共部分)”,子类是“具体怎么做(个性化部分)”。掌握了这个逻辑,你就能应对大部分复杂的业务场景了!

本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49404.html


相关教程