Objective-C中的代码重构与设计模式应用
代码重构的概念与意义
在Objective-C编程中,代码重构是一个至关重要的实践,它能够显著提升代码的质量、可维护性和可扩展性。简单来说,代码重构就是在不改变代码外部行为的前提下,对代码内部结构进行调整和优化。
为什么要进行代码重构呢?随着项目的不断发展和迭代,代码库会逐渐变大,复杂度也会随之增加。最初编写的代码可能在当时的场景下是可行的,但随着需求的变化,可能会出现重复代码、过长的方法、紧密耦合等问题。这些问题会使得代码难以理解、修改和扩展,甚至可能引入更多的错误。通过代码重构,可以消除这些问题,让代码更加清晰、简洁且易于维护。
例如,假设有一个简单的iOS应用,用于显示用户的个人信息。在开发初期,为了快速实现功能,可能会在视图控制器中直接编写大量获取和显示用户信息的代码:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *userEmail;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 直接在视图控制器中获取用户信息
self.userName = @"John Doe";
self.userEmail = @"johndoe@example.com";
// 显示用户信息
UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 30)];
nameLabel.text = self.userName;
[self.view addSubview:nameLabel];
UILabel *emailLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 150, 200, 30)];
emailLabel.text = self.userEmail;
[self.view addSubview:emailLabel];
}
@end
这段代码虽然实现了基本功能,但存在一些问题。获取用户信息的逻辑直接写在视图控制器中,导致视图控制器的职责不单一,而且如果后续获取用户信息的方式发生变化(比如从网络请求获取),就需要在视图控制器中大量修改代码。这时候就需要进行代码重构,将获取用户信息的逻辑提取到一个单独的服务类中:
#import <Foundation/Foundation.h>
@interface UserService : NSObject
+ (NSString *)fetchUserName;
+ (NSString *)fetchUserEmail;
@end
@implementation UserService
+ (NSString *)fetchUserName {
// 这里可以是从数据库、网络等获取用户名
return @"John Doe";
}
+ (NSString *)fetchUserEmail {
// 这里可以是从数据库、网络等获取用户邮箱
return @"johndoe@example.com";
}
@end
然后在视图控制器中调用这个服务类:
#import "ViewController.h"
#import "UserService.h"
@interface ViewController ()
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *userEmail;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.userName = [UserService fetchUserName];
self.userEmail = [UserService fetchUserEmail];
// 显示用户信息
UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 30)];
nameLabel.text = self.userName;
[self.view addSubview:nameLabel];
UILabel *emailLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 150, 200, 30)];
emailLabel.text = self.userEmail;
[self.view addSubview:emailLabel];
}
@end
通过这样的重构,视图控制器的职责变得更加清晰,只负责显示用户信息,而获取用户信息的逻辑封装在UserService
类中。如果获取用户信息的方式发生变化,只需要在UserService
类中进行修改,而不需要修改视图控制器的代码。
常见的代码重构手法
提取方法(Extract Method)
提取方法是一种非常常用的重构手法,它将一段代码从一个方法中提取出来,形成一个新的独立方法。这样做可以使原方法更加简洁,提高代码的可读性和可维护性。例如,在一个处理订单的方法中,如果有一段复杂的计算订单总价的代码:
#import <Foundation/Foundation.h>
@interface Order : NSObject
@property (nonatomic, strong) NSArray<NSNumber *> *productPrices;
- (void)processOrder {
// 订单处理逻辑
NSNumber *totalPrice = @0;
for (NSNumber *price in self.productPrices) {
totalPrice = @([totalPrice doubleValue] + [price doubleValue]);
}
// 其他订单处理逻辑,比如保存订单到数据库等
NSLog(@"Total price: %@", totalPrice);
}
@end
可以将计算订单总价的代码提取出来,形成一个新的方法:
#import <Foundation/Foundation.h>
@interface Order : NSObject
@property (nonatomic, strong) NSArray<NSNumber *> *productPrices;
- (NSNumber *)calculateTotalPrice {
NSNumber *totalPrice = @0;
for (NSNumber *price in self.productPrices) {
totalPrice = @([totalPrice doubleValue] + [price doubleValue]);
}
return totalPrice;
}
- (void)processOrder {
NSNumber *totalPrice = [self calculateTotalPrice];
// 其他订单处理逻辑,比如保存订单到数据库等
NSLog(@"Total price: %@", totalPrice);
}
@end
这样processOrder
方法变得更加简洁,而且calculateTotalPrice
方法可以在其他地方复用。
内联方法(Inline Method)
与提取方法相反,内联方法是将一个方法的调用替换为方法的实际代码。当一个方法的逻辑非常简单,只有一行代码,并且方法调用的开销大于方法本身的执行开销时,可以考虑使用内联方法。例如:
#import <Foundation/Foundation.h>
@interface MathUtils : NSObject
+ (NSInteger)add:(NSInteger)a and:(NSInteger)b;
@end
@implementation MathUtils
+ (NSInteger)add:(NSInteger)a and:(NSInteger)b {
return a + b;
}
@end
在另一个类中调用这个方法:
#import "MathUtils.h"
@interface Calculator : NSObject
- (NSInteger)performCalculation {
NSInteger result = [MathUtils add:2 and:3];
return result;
}
@end
由于add
方法非常简单,可以将其内联:
#import <Foundation/Foundation.h>
@interface Calculator : NSObject
- (NSInteger)performCalculation {
NSInteger result = 2 + 3;
return result;
}
@end
这样可以减少方法调用的开销,提高代码的执行效率。
提炼类(Extract Class)
当一个类承担了过多的职责,变得过于庞大和复杂时,可以使用提炼类的重构手法。将相关的属性和方法从原类中提取出来,形成一个新的类。例如,有一个Employee
类,既包含员工的基本信息,又包含员工的考勤记录和薪资计算逻辑:
#import <Foundation/Foundation.h>
@interface Employee : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSDate *hireDate;
@property (nonatomic, strong) NSMutableArray<NSDate *> *attendanceDates;
- (NSInteger)calculateSalary;
@end
@implementation Employee
- (NSInteger)calculateSalary {
// 复杂的薪资计算逻辑
return 5000;
}
@end
可以将考勤记录相关的逻辑提取到一个新的Attendance
类中,将薪资计算逻辑提取到一个SalaryCalculator
类中:
#import <Foundation/Foundation.h>
@interface Attendance : NSObject
@property (nonatomic, strong) NSMutableArray<NSDate *> *attendanceDates;
@end
@implementation Attendance
@end
@interface SalaryCalculator : NSObject
+ (NSInteger)calculateSalaryForEmployee:(Employee *)employee;
@end
@implementation SalaryCalculator
+ (NSInteger)calculateSalaryForEmployee:(Employee *)employee {
// 复杂的薪资计算逻辑
return 5000;
}
@end
@interface Employee : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSDate *hireDate;
@property (nonatomic, strong) Attendance *attendance;
- (NSInteger)calculateSalary {
return [SalaryCalculator calculateSalaryForEmployee:self];
}
@end
@implementation Employee
@end
这样Employee
类的职责更加单一,代码的结构也更加清晰。
设计模式在Objective-C中的应用
单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例,并提供一个全局访问点。在Objective-C中,实现单例模式有多种方式。传统的方式是使用static
变量和dispatch_once
:
#import <Foundation/Foundation.h>
@interface AppSettings : NSObject
@property (nonatomic, strong) NSString *theme;
@property (nonatomic, assign) BOOL isDarkMode;
+ (instancetype)sharedSettings;
@end
@implementation AppSettings
static AppSettings *sharedInstance = nil;
+ (instancetype)sharedSettings {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@end
在应用中,可以通过[AppSettings sharedSettings]
来获取唯一的实例:
#import "AppSettings.h"
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
AppSettings *settings = [AppSettings sharedSettings];
settings.theme = @"Default Theme";
settings.isDarkMode = NO;
}
@end
单例模式在很多场景下都非常有用,比如应用的配置管理、数据库连接管理等。它可以避免创建多个不必要的实例,节省资源。
工厂模式(Factory Pattern)
工厂模式提供了一种创建对象的方式,将对象的创建和使用分离。在Objective-C中,可以通过类方法来实现简单的工厂模式。例如,有一个图形绘制的应用,需要创建不同类型的图形:
#import <Foundation/Foundation.h>
@interface Shape : NSObject
- (void)draw;
@end
@implementation Shape
- (void)draw {
NSLog(@"Drawing a shape");
}
@end
@interface Circle : Shape
@end
@implementation Circle
- (void)draw {
NSLog(@"Drawing a circle");
}
@end
@interface Rectangle : Shape
@end
@implementation Rectangle
- (void)draw {
NSLog(@"Drawing a rectangle");
}
@end
@interface ShapeFactory : NSObject
+ (Shape *)createShapeWithType:(NSString *)type;
@end
@implementation ShapeFactory
+ (Shape *)createShapeWithType:(NSString *)type {
if ([type isEqualToString:@"circle"]) {
return [[Circle alloc] init];
} else if ([type isEqualToString:@"rectangle"]) {
return [[Rectangle alloc] init];
}
return [[Shape alloc] init];
}
@end
在使用时,可以通过ShapeFactory
来创建不同类型的图形:
#import "ShapeFactory.h"
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Shape *circle = [ShapeFactory createShapeWithType:@"circle"];
[circle draw];
Shape *rectangle = [ShapeFactory createShapeWithType:@"rectangle"];
[rectangle draw];
}
@end
工厂模式使得代码更加灵活,当需要添加新的图形类型时,只需要在ShapeFactory
中添加相应的创建逻辑,而不需要修改使用图形的代码。
观察者模式(Observer Pattern)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新。在Objective-C中,可以使用NSNotificationCenter
来实现类似观察者模式的功能。例如,有一个新闻应用,当有新的新闻发布时,需要通知所有关注该类型新闻的用户:
#import <Foundation/Foundation.h>
@interface NewsPublisher : NSObject
+ (instancetype)sharedPublisher;
- (void)publishNews:(NSString *)news;
@end
@implementation NewsPublisher
static NewsPublisher *sharedInstance = nil;
+ (instancetype)sharedPublisher {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (void)publishNews:(NSString *)news {
NSDictionary *userInfo = @{@"news": news};
[[NSNotificationCenter defaultCenter] postNotificationName:@"NewNewsPublished" object:self userInfo:userInfo];
}
@end
@interface NewsSubscriber : NSObject
- (void)handleNewsNotification:(NSNotification *)notification;
@end
@implementation NewsSubscriber
- (void)handleNewsNotification:(NSNotification *)notification {
NSString *news = notification.userInfo[@"news"];
NSLog(@"Received news: %@", news);
}
@end
在应用中,订阅者可以注册监听通知:
#import "NewsPublisher.h"
#import "NewsSubscriber.h"
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NewsSubscriber *subscriber = [[NewsSubscriber alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:subscriber selector:@selector(handleNewsNotification:) name:@"NewNewsPublished" object:nil];
NewsPublisher *publisher = [NewsPublisher sharedPublisher];
[publisher publishNews:@"New technology breakthrough!"];
}
@end
这样,当有新的新闻发布时,所有注册的订阅者都会收到通知并进行相应的处理。
结合代码重构与设计模式提升代码质量
在实际的Objective-C项目开发中,代码重构和设计模式往往是相辅相成的。通过代码重构,可以为设计模式的应用创造更好的条件,而设计模式的应用又可以进一步指导代码重构的方向。
例如,在一个电商应用中,最初的购物车功能可能是在一个视图控制器中直接实现的,代码比较混乱,存在重复计算购物车总价等问题。通过代码重构,将购物车相关的逻辑提取到一个单独的CartManager
类中:
#import <Foundation/Foundation.h>
@interface Product : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) double price;
@end
@implementation Product
@end
@interface CartManager : NSObject
@property (nonatomic, strong) NSMutableArray<Product *> *cartItems;
- (void)addProductToCart:(Product *)product;
- (void)removeProductFromCart:(Product *)product;
- (double)calculateCartTotal;
@end
@implementation CartManager
- (void)addProductToCart:(Product *)product {
if (!self.cartItems) {
self.cartItems = [NSMutableArray array];
}
[self.cartItems addObject:product];
}
- (void)removeProductFromCart:(Product *)product {
[self.cartItems removeObject:product];
}
- (double)calculateCartTotal {
double total = 0;
for (Product *product in self.cartItems) {
total += product.price;
}
return total;
}
@end
此时,购物车的逻辑已经相对清晰。但如果需要在购物车总价发生变化时通知其他模块(比如更新显示总价的视图),可以应用观察者模式。首先定义一个通知名称和相关的通知发送逻辑:
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT NSString *const CartTotalDidChangeNotification;
@interface CartManager : NSObject
@property (nonatomic, strong) NSMutableArray<Product *> *cartItems;
- (void)addProductToCart:(Product *)product;
- (void)removeProductFromCart:(Product *)product;
- (double)calculateCartTotal;
@end
@implementation CartManager
NSString *const CartTotalDidChangeNotification = @"CartTotalDidChangeNotification";
- (void)addProductToCart:(Product *)product {
if (!self.cartItems) {
self.cartItems = [NSMutableArray array];
}
[self.cartItems addObject:product];
double total = [self calculateCartTotal];
NSDictionary *userInfo = @{@"total": @(total)};
[[NSNotificationCenter defaultCenter] postNotificationName:CartTotalDidChangeNotification object:self userInfo:userInfo];
}
- (void)removeProductFromCart:(Product *)product {
[self.cartItems removeObject:product];
double total = [self calculateCartTotal];
NSDictionary *userInfo = @{@"total": @(total)};
[[NSNotificationCenter defaultCenter] postNotificationName:CartTotalDidChangeNotification object:self userInfo:userInfo];
}
- (double)calculateCartTotal {
double total = 0;
for (Product *product in self.cartItems) {
total += product.price;
}
return total;
}
@end
然后在需要监听购物车总价变化的地方注册通知:
#import "CartManager.h"
@interface TotalPriceViewController : UIViewController
@end
@implementation TotalPriceViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTotalPrice:) name:CartTotalDidChangeNotification object:nil];
}
- (void)updateTotalPrice:(NSNotification *)notification {
NSNumber *total = notification.userInfo[@"total"];
// 更新显示总价的视图
NSLog(@"New cart total: %@", total);
}
@end
通过这样的方式,结合代码重构和设计模式,使得购物车功能的代码更加清晰、可维护,并且具备良好的扩展性。
在处理复杂业务逻辑时,比如订单处理流程,可能会涉及多个类之间的交互和状态变化。通过代码重构,可以将不同的职责分配到合适的类中,然后应用设计模式来管理这些类之间的关系。例如,可以使用状态模式来处理订单在不同状态下的行为。假设订单有“未支付”、“已支付”、“已发货”等状态:
#import <Foundation/Foundation.h>
@interface Order;
@interface OrderState : NSObject
- (void)handleOrder:(Order *)order;
@end
@implementation OrderState
- (void)handleOrder:(Order *)order {
NSLog(@"Handling order in default state");
}
@end
@interface UnpaidOrderState : OrderState
- (void)handleOrder:(Order *)order;
@end
@implementation UnpaidOrderState
- (void)handleOrder:(Order *)order {
NSLog(@"Order is unpaid. Please make a payment.");
}
@end
@interface PaidOrderState : OrderState
- (void)handleOrder:(Order *)order;
@end
@implementation PaidOrderState
- (void)handleOrder:(Order *)order {
NSLog(@"Order is paid. Preparing to ship.");
}
@end
@interface ShippedOrderState : OrderState
- (void)handleOrder:(Order *)order;
@end
@implementation ShippedOrderState
- (void)handleOrder:(Order *)order {
NSLog(@"Order is shipped. Tracking number: 123456789");
}
@end
@interface Order : NSObject
@property (nonatomic, strong) OrderState *currentState;
- (void)setState:(OrderState *)state;
- (void)handleOrder;
@end
@implementation Order
- (void)setState:(OrderState *)state {
self.currentState = state;
}
- (void)handleOrder {
[self.currentState handleOrder:self];
}
@end
在使用时,可以根据订单的实际状态设置相应的状态对象:
#import "Order.h"
@interface OrderProcessor : NSObject
@end
@implementation OrderProcessor
- (void)processOrder {
Order *order = [[Order alloc] init];
order.currentState = [[UnpaidOrderState alloc] init];
[order handleOrder];
order.currentState = [[PaidOrderState alloc] init];
[order handleOrder];
order.currentState = [[ShippedOrderState alloc] init];
[order handleOrder];
}
@end
这样,通过代码重构将订单状态相关的逻辑分离出来,再应用状态模式,使得订单处理流程的代码更加清晰、易于理解和维护。在面对业务需求变化,比如新增订单状态或者修改某个状态下的行为时,只需要在相应的状态类中进行修改,而不会影响其他部分的代码。
再比如,在一个图片加载和缓存的功能中,最初可能是在视图控制器中直接实现图片的加载和缓存逻辑,代码耦合度较高。通过代码重构,将图片加载和缓存的逻辑提取到一个ImageLoader
类中:
#import <UIKit/UIKit.h>
@interface ImageLoader : NSObject
@property (nonatomic, strong) NSCache<NSString *, UIImage *> *imageCache;
- (UIImage *)loadImageFromURL:(NSURL *)url;
@end
@implementation ImageLoader
- (instancetype)init {
self = [super init];
if (self) {
self.imageCache = [[NSCache alloc] init];
}
return self;
}
- (UIImage *)loadImageFromURL:(NSURL *)url {
UIImage *cachedImage = [self.imageCache objectForKey:url.absoluteString];
if (cachedImage) {
return cachedImage;
}
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
[self.imageCache setObject:image forKey:url.absoluteString];
}
return image;
}
@end
此时,图片加载和缓存的逻辑已经相对独立。为了提高代码的灵活性和可扩展性,可以应用策略模式。假设后续需要支持不同的图片加载策略,比如优先从本地缓存加载,如果本地缓存没有再从网络加载,或者直接从网络加载不使用缓存等。可以定义一个图片加载策略的协议和不同的策略实现类:
#import <UIKit/UIKit.h>
@protocol ImageLoadingStrategy <NSObject>
- (UIImage *)loadImageFromURL:(NSURL *)url;
@end
@interface DefaultImageLoadingStrategy : NSObject <ImageLoadingStrategy>
@property (nonatomic, strong) NSCache<NSString *, UIImage *> *imageCache;
- (instancetype)init;
- (UIImage *)loadImageFromURL:(NSURL *)url;
@end
@implementation DefaultImageLoadingStrategy
- (instancetype)init {
self = [super init];
if (self) {
self.imageCache = [[NSCache alloc] init];
}
return self;
}
- (UIImage *)loadImageFromURL:(NSURL *)url {
UIImage *cachedImage = [self.imageCache objectForKey:url.absoluteString];
if (cachedImage) {
return cachedImage;
}
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
[self.imageCache setObject:image forKey:url.absoluteString];
}
return image;
}
@end
@interface NetworkOnlyImageLoadingStrategy : NSObject <ImageLoadingStrategy>
- (UIImage *)loadImageFromURL:(NSURL *)url;
@end
@implementation NetworkOnlyImageLoadingStrategy
- (UIImage *)loadImageFromURL:(NSURL *)url {
NSData *imageData = [NSData dataWithContentsOfURL:url];
return [UIImage imageWithData:imageData];
}
@end
@interface ImageLoader : NSObject
@property (nonatomic, strong) id<ImageLoadingStrategy> loadingStrategy;
- (UIImage *)loadImageFromURL:(NSURL *)url;
@end
@implementation ImageLoader
- (UIImage *)loadImageFromURL:(NSURL *)url {
return [self.loadingStrategy loadImageFromURL:url];
}
@end
在使用时,可以根据需要设置不同的图片加载策略:
#import "ImageLoader.h"
#import "DefaultImageLoadingStrategy.h"
#import "NetworkOnlyImageLoadingStrategy.h"
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ImageLoader *imageLoader = [[ImageLoader alloc] init];
imageLoader.loadingStrategy = [[DefaultImageLoadingStrategy alloc] init];
NSURL *imageURL = [NSURL URLWithString:@"https://example.com/image.jpg"];
UIImage *image = [imageLoader loadImageFromURL:imageURL];
// 切换到网络-only策略
imageLoader.loadingStrategy = [[NetworkOnlyImageLoadingStrategy alloc] init];
image = [imageLoader loadImageFromURL:imageURL];
}
@end
通过这样的方式,先进行代码重构,将相关逻辑分离出来,然后应用策略模式,使得图片加载和缓存的功能更加灵活,能够适应不同的需求变化。无论是增加新的图片加载策略,还是修改现有策略的实现,都可以在不影响其他部分代码的情况下进行。
综上所述,在Objective-C开发中,代码重构和设计模式的应用是提升代码质量、可维护性和可扩展性的关键手段。通过合理地运用各种代码重构手法和设计模式,可以打造出更加健壮、灵活且易于理解的软件系统。开发者应该不断地在实践中积累经验,熟练掌握这些技术,以应对日益复杂的项目需求。