iOS 微信、支付宝、银联支付组件的进一步设计

原文地址:https://zhanglei.blog.csdn.net/article/details/121376500

前言

有段时间没写技术文章了,一是因为工作太忙,再者因为本人文笔实在一般。最近终于闲下来,本着分享的目的将一些组件设计上的心得与大家分享。
本篇文章是基于原有一篇关于支付文章的进一步优化设计,所以在阅读本篇文章前还是建议先移步到那篇文章。
文章地址: 微信、支付宝、银联、Paypal 支付组件封装

描述

在封装支付接口时,需要面临各支付平台不同SDK集成的问题,有的支付第三方平台只通过sdk组件就可完成支付,像支付宝,但大部分的支付第三方平台需要先去调用服务端API接口获取支付需要的信息,拿到这些支付信息后,再去调用sdk组件完成支付,这类第三方有银联(需要获取tn交易流水号),微信(需要获取prepayData)等。

为了以后的支付功能复用,想要设计一个支付组件,该组件整合并统一了这些第三方支付sdk的接口,以便给客户端快速集成。在设计支付组件的过程中就遇到上面提到的问题。 如何解决某些第三方需要请求一些数据后,再进行支付的问题 ? 试想一下如果将这些请求hardcode到组件中,显然能满足当前的功能,局限性也非常明显,只能适用当前的支付业务。 此时的支付组件会和网络组件藕合,不利于扩展及复用。

设计

如何解藕? 如何能让这些需要请求API的第三方不污染组件? 解藕的思想就是“抽离变化,并封装”。 我们需要把不稳定的部分抽离出来,使其独自变化,不影响稳定的部分。 找到了方向, 如何抽离? 这里我们可以使用面向协议的编程的思想,将请求API的行为进行抽象。
伪代码:

//抽像一个协议, 协议定义一个获取支付信息的方法,调用接口是异步操作,所以返回的数据使用block返回

@protocol PayDataPrepareProtocol <NSObject>

@required
- (void)getPayData:(void(^)(id result, NSError *error))block;

@end

微信支付伪代码:

@interface WXPayPrepareData : NSObject <PayDataPrepareProtocol>
@property (nonatomic,strong) NSDictionary *requestParams;

@end

@implementation WXPayPrepareData

- (void)getPayData:(void(^)(id result, NSError *error))block {
    //根据请求参数,使用网络层组件调用API,并返回预支付信息
    block(result,nil);
}

@end

银联支付伪代码:

@interface UnionPayPrepareData : NSObject <PayDataPrepareProtocol>
@property (nonatomic,strong) NSDictionary *requestParams;
@end

@implementation UnionPayPrepareData

- (void)getPayData:(void(^)(id result, NSError *error))block {
    //根据请求参数,使用网络层组件调用API,并返回预支付信息
    block(result,nil);
}
@end

如何能将支付类型与预支付实现类之间建立联系呢?我们需要设计一个配置类来管理这种支付类型与预支付实现类的对应关系 。

支付配置的伪代码:

typedef NS_Enum(NSInteger, PayType) {
    PayTypeForAlipay,    //支付宝支付
    PayTypeForWXPay,    //微信支付
    PayTypeForUPPay,    //银联支付
} 


@interface PayConfig: NSObject

//单例对象
+ (instancetype)config;

//添加获取预支付信息对应的策略类, 没有传递实例对象,避免未使用而造成的内存浪费
- (void)appendPrepayDataStrategy:(Class)strategyClass withPayType:(PayType)payType;

//判断是否存在指定类型对应的实现策略
- (BOOL)containsPrepayDataStrategyWithPayType:(PayType)payType;

//根据支付的枚举类型,获取预支付信息处理对象
- (id<PayDataPrepareProtocol>)getPrepayDataStrategyWithPayType:(PayType)payType;
@end


@interface PayConfig ()
@property (nonatomic,strong) NSMutableDictionary *strategyMap;
@end

@implementation PayConfig

//单例对象
+ (instancetype)config {
    static PayConfig *_instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!_instance) {
           _instance = [[PayConfig alloc] init]; 
        }
    });
    return _instance;
}

//添加获取预支付信息对应的策略类, 没有传递实例对象,避免未使用而造成的内存浪费
- (void)appendPrepayDataStrategy:(Class)strategyClass withPayType:(PayType)payType {
    if (!strategyClass) {
        return;
    }

    //判断是否实现了协议
    if (![strategyClass conformsToProtocol:@protocol(PayDataPrepareProtocol)]) {
        return;
    }
    
    NSString *payTypeKey = [Utils convertPayTypeToString:payType]; //将枚举转成字符串
    [self.strategyMap setObject:strategyClass forKey:payTypeKey];

}

//判断是否存在指定类型对应的实现策略
- (BOOL)containsPrepayDataStrategyWithPayType:(PayType)payType {
    NSString *payTypeKey = [Utils convertPayTypeToString:payType]; //将枚举转成字符串
    return  !self.strategyMap[payTypeKey];
}

//根据支付的枚举类型,获取预支付信息处理对象
- (id<PayDataPrepareProtocol>)getPrepayDataStrategyWithPayType:(PayType)payType {

    if (![self containsPrepayDataStrategyWithPayType:payType]) {
        return nil;
    }

    NSString *payTypeKey = [Utils convertPayTypeToString:payType]; //将枚举转成字符串
    Class cls = self.strategyMap[payTypeKey];

    return [[cls alloc] init]; //在需要时才返回创建的对象
}

//懒加载,需要时创建
- (NSMutableDictionary *)strategyMap {
    if (!_strategyMap) {
        _strategyMap = @{}.mutableCopy;
    }
    return _strategyMap;
}
@end

通过上面的支付配置(单例)对象,我们将以支付类型为key, 以对应的获取预支付信息的类为value, 使用字典来管理。 将配置类设计成单例,这样就可以在支付组件中访问,并使用其中的配置信息。

支付组件的伪代码:

#import "PayConfig.h"

@interface PayManager :NSObject 

//调用预支付信息API接口,用到的请求参数
@property (nonatomic,strong) NSDictionary *requestParams;

//支付第三方类型
@property (nonatomic,assign) PayType payType;

//开始支付
- (void)startPay;
@end


@implementation PayManager 


//开始支付
- (void)startPay {
    
    //1. 根据支付类型,判断支付配置中是否有需要请求服务API的处理类
    if ([[PayConfig config] containsPrepayDataStrategyWithPayType:self.payType]) {
        id strategy = [[PayConfig config] getPrepayDataStrategyWithPayType:self.payType];
        
        //利用KVC向请求API接口的策略类传递请求参数
        [strategy setValue:self.requestParams forKey:@“requestParams”];
        
        //准备好请求数据后,开始调用接口API获取所需要的预支付信息
        [strategy getPayData:^(id result, NSError *error ) {
            //拿到需要的预支付信息后,再调起第三方支付组件
            
        }];
    
    }else {
        // 调起第三方支付组件
    }

}
@end

总结

通过我们的进一步设计,支付组件已完全不依赖于网络组件来完成对预支付信息的获取,而且扩展性更强了。 如果以后有新的支付第三方加入进来,且需要获取预支付信息的, 我们只需要实现PayDataPrepareProtocol 协议, 并将其加入到 PayConfig 中就可以了。 通过少量的修改我们就可以完成扩展,也遵循了“开闭原则( 对扩展开放,对修改关闭)” 。

完整的支付组件代码请前往:RZPayManager
在README.md文件中有该组件的使用详解,如果喜欢,点关注支持一下。

后记

如果本文对你有一点帮助的话,欢迎收藏、点赞,感谢。文中如有不对之处,也欢迎大家在评论区指出,共勉。

posted @ 2023-06-23 10:22  reyzhang  阅读(202)  评论(0编辑  收藏  举报